diff options
Diffstat (limited to 'utils')
167 files changed, 71661 insertions, 0 deletions
diff --git a/utils/open-isns/COPYING b/utils/open-isns/COPYING new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/utils/open-isns/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/utils/open-isns/ChangeLog b/utils/open-isns/ChangeLog new file mode 100644 index 0000000..36d6506 --- /dev/null +++ b/utils/open-isns/ChangeLog @@ -0,0 +1,50 @@ +Under development: + +2007-09-27: + Fixed a serious interoperability bug + Added SLP support (using openslp) + Init script (courtesy Albert Pauw) + +2007-09-18: + Fixed a number of bugs + Added more test cases + Implemented default DD + Support autoconf, and building with/without openssl + +2007-08-24: + Improved discovery domain handling + Implemented DD deregistration + Backward compat fixes for older openssl versions + Made SCN more robust, SCN state now persists across server restarts + More regression tests + +2007-07-27: + Implemented SCN and ESI + Created iSNS discovery daemon (isnsdd) + Rewrote the policy handling a bit + Started to write some regression test code + Better manpages + +2007-07-12: + DevGetNext support + You can now define policies linking authentication + to permitted storage node names, permitted + entity names, etc. + Implemented DDReg + Queries and GetNext are now scoped to discovery domains + Lots of little bits and pieces for RFC conformance + +2005-07-18: + Public snapshot released + DSA based authentication + Deregistration + Simple file backed storage for the iSNS database + Entity Registration Period + Timestamp support (server side), + and entity expiration + isnsd now writes a pid file + Improved manual pages + DevGetNext support under development + +2007-05-11: + First public release, supporting register/query + diff --git a/utils/open-isns/HACKING b/utils/open-isns/HACKING new file mode 100644 index 0000000..95b330c --- /dev/null +++ b/utils/open-isns/HACKING @@ -0,0 +1,30 @@ + +When hacking on open-isns, or when trying to locate a problem, +the following information may be useful: + + - You can start the daemon using the -f option, which + prevents it from backgrounding itself. Crucial if + you want to run it in a debugger, or under strace. + + This option works for isnsd and isnsdd + + - All tools support the "-d" option to enable debugging. + In general, you want to use "-d all" to turn on all + debugging options. However, you can select individual + debug facilities - check out the manpages and/or + the source code in logging.c + + - If isnsd crashes, and you suspect memory corruption, + you can compile open-isns with memory debugging enabled. Re-run + the configure script and add the option --enable-memdebug. Then + run "make clean all" to rebuild everything. + + Memory debugging can be chosen at run-time by setting the + ISNS_MDEBUG environment variable, and re-starting the application: + + export ISNS_MDEBUG=1 + ./isnsd -f -d all + + Memory debugging works for all memory allocations done by the + Open-iSNS code, but does not affect memory allocations by other + libraries (such as glibc or openssl). diff --git a/utils/open-isns/Makefile.in b/utils/open-isns/Makefile.in new file mode 100644 index 0000000..744fe8b --- /dev/null +++ b/utils/open-isns/Makefile.in @@ -0,0 +1,82 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +sbindir = @sbindir@ +mandir = @mandir@ +etcdir = /etc +vardir = /var/lib/isns + +SBINDIR = $(INSTALL_ROOT)$(sbindir) +ETCDIR = $(INSTALL_ROOT)$(etcdir) +CFGDIR = $(ETCDIR)/isns +MANDIR = $(INSTALL_ROOT)$(mandir) +VARDIR = $(INSTALL_ROOT)$(vardir) + +CC = @CC@ +CPPFLAGS= @CPPFLAGS@ +CFLAGS = @CFLAGS@ -I. +LDFLAGS = @LDFLAGS@ + +LIB = libisns.a +LIBOBJS = server.o \ + client.o \ + objects.o \ + callback.o \ + timer.o \ + vendor.o \ + db.o \ + db-file.o \ + db-policy.o \ + relation.o \ + scope.o \ + message.o \ + security.o \ + authblock.o \ + policy.o \ + pki.o \ + register.o \ + query.o \ + getnext.o \ + deregister.o \ + esi.o \ + scn.o \ + dd.o \ + entity.o \ + portal-group.o \ + storage-node.o \ + domain.o \ + simple.o \ + tags.o \ + attrs.o \ + export.o \ + socket.o \ + slp.o \ + error.o \ + logging.o \ + config.o \ + parser.o \ + buffer.o \ + pidfile.o \ + sysdep-unix.o \ + util.o \ + bitvector.o \ + mdebug.o +SECLINK = @SECLIBS@ +SLPLINK = @SLPLIBS@ +SLPLIN = @SLPLIBS@ + +all: $(LIB) + +clean distclean:: + rm -f *.o $(LIB) *~ + +distclean:: + rm -f config.h Makefile config.status config.log + rm -rf autom4te.cache + +$(LIB): $(LIBOBJS) + ar cr $@ $(LIBOBJS) + +depend: + gcc $(CFLAGS) -M `ls *.c` > .depend + +-include .depend diff --git a/utils/open-isns/README b/utils/open-isns/README new file mode 100644 index 0000000..acff29b --- /dev/null +++ b/utils/open-isns/README @@ -0,0 +1,173 @@ + +Welcome to Open-iSNS +==================== + +This is a partial implementation of iSNS, according to RFC4171. +The implementation is still somewhat incomplete, but I'm releasing +it for your reading pleasure. + +The distribution comprises + + isnsd + This is the iSNS server, supporting persistent storage + of registrations in a file based database. + + isnsadm + A command line utility for querying the iSNS database, + and for registering/deregistering nodes and portals + + isnsdd + An iSNS Discovery Daemon, which is still very much work + in progress. The daemon is supposed to handle all the + bit banging and server communications required to register + a node, its portals, and to maintain the registration. + It is also supposed to use the iSNS State Change Notification + framework to learn of new targets or initiators coming online, + and inform local services (such as the iSCSI initiator daemon) + about these changes. + +Thanks! +------- + +Many thanks to Albert Pauw for his fearless testing of snapshots, +and his copious feedback! + +What works, after a fashion: +---------------------------- + + - For now, I've been focusing on getting the iSCSI part to + work. There is some very basic support for FC objects, but + this will be hardly useful yet. + + - Registration, deregistration, query, getnext + You can use isnsadm to register iSCSI nodes, and portals. + isnsadm also illustrates how this is supposed to be used from + the client perspective. + + - Discovery domains are supported mostly. The administrator + can create discovery domains using isnsadm, and place storage + nodes in domains. Queries by clients are scoped by their + discovery domains membership, so that they will be unable to + see nodes not part of a shared DD. + + Open-iSNS currently does not allow clients to place themselves + in a DD. + + Optionally, storage nodes that are not in any discovery domain + will be placed in a "default DD" (see the DefaultDiscoveryDomain + in isnsd.conf). + + - ESI, supported both by the server and the discovery daemon + + - SCN, supported by the server and the discovery daemon + +What is still missing +--------------------- + + - Better documentation (esp. a HOWTO on getting started with iSNS) + - DD Sets + - Various bits and pieces of the protocol + - FC support + + +Building Open-iSNS +------------------ + +The Open-iSNS build is now based on autoconf. The distributed tarball +should include a configure script and a config.h.in file generated +from configure.ac. If these are missing, you can generate them +by running + + autoconf + autoheader + +For most people, it should be sufficient to run configure without any +arguments, or at most with the option --prefix. If run without --prefix, +program files, manpages etc will be installed below /usr/local. To have +everything installed /usr/bin, /usr/share/man etc, run it as + + ./configure --prefix=/usr + +Dependencies: + + - If you want to build Open-iSNS with support for authentication, + you need the OpenSSL libraries and header files installed. + + The configure script should pick up the presence of these + libraries, and enable security support automatically. To disable + this explicitly in your build, pass the --without-security option + to configure. + + - If you want to build Open-iSNS with SLP support, you need the + OpenSLP library and header file installed. + + The configure script should pick up the presence of this library, + and enable SLP support automatically. To disable this explicitly + in your build, pass the --without-slp option to configure. + +When configure is run, it checks for a the presence of a number of +headers and libraries in your system (the results of most of these checks +are currently ignored :-). Then, it creates a Makefile and a config.h +include file. With these in place, you can build the binaries and libraries: + + make + make install + +Getting started +--------------- + +On the iSNS server, you need to generate a server key and install it. The +simplest way is probably to use the isnssetup script included in the +source package. + +For each client you wish to use, you should then + +iSNS Security +------------- + +This implementation of iSNS supports authentication, as descibed in RFC +4171. In order to use it, you have to create DSA keys for the server and +all clients. + +iSNS uses conceptually the same security mechanism as SLP, and identifies +principals by a "Security Parameter Index", which is essentially a string +identifying a key. + +Open-iSNS fully supports DSA based security, and offers a flexible +policy mechanism that ties an SPI to a network entity and the storage +node names it is allowed to use. For an introduction to the security +model used by Open-iSNS, refer to the isns_config(5) manual page. An +overview on setting up the iSNS server for authentication is given in +the EXAMPLES section of the isnsadm(8) manual page. + +Downloading Open-iSNS +--------------------- + +Open-iSNS is available for download from + + http://oss.oracle.com/~okir/open-isns + +You have to grab the latest tarball and compile it; fancy things such +as RPMs are not available yet. + +------------------------------------------------------------------ + + + COPYRIGHT NOTICE + + Copyright (C) 2007 Olaf Kirch. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + diff --git a/utils/open-isns/TODO b/utils/open-isns/TODO new file mode 100644 index 0000000..2ddf008 --- /dev/null +++ b/utils/open-isns/TODO @@ -0,0 +1,100 @@ +Documentation: + - Add HOWTO + +isnsd: + - When registering a node, use the default EID given in its + policy (avoid the isns.control trap) + - make PGs children of the iSCSI storage node they're associated + with? + - Implement missing functions + +isnsadm: + - support iSNS server discovery through DNS SRV + records, and SLP + +isnsdd: + - support iSNS server discovery through DNS SRV + records, and SLP + - At startup, query the server for the list of + visible nodes/portals + - When receiving an SCN, query for the node's + portals, authmethod and such, and compare that + to what we have cached + - At regular intervals, repeat the query for + all visible nodes/portals, and do a diff with + our shadow DB + - At regular intervals, check whether the portals + we registered for ESI are seeing the server's + ESI messages. + +DevAttrReg: + - Refuse registration of nodes inside the CONTROL + entity, unless it's a control node. + - If the client uses REPLACE, is it okay for the + entity's index to change? + - security: optionally validate the IP addresses + a client registers (either against a static policy, + or using DNS). + - relaxed security model: require privilege + for registration of targets; anyone can register + an initiator? + - Gracefully handle registrations where the client + specifies an index attribute, as long as it matches + the next_index + +DevAttrQuery: + - fix --local --query policy-index=iqn.1969-12.brummo + and write test case + - fix the way we enumerate related objects + - ensure DD discovery works (5.6.5.2): + DD membership can be discovered through the DevAttrQry message + by including either DD member attributes (i.e., DD Member + iSCSI Index, DD Member iSCSI Node, DD Member iFCP Node, DD + Member Portal Index, DD Member Portal IP Addr, and DD Member + Portal TCP/UDP) or the object key of the Storage Node or + Portal (i.e., iSCSI Name, iSCSI Index, Portal IP Addr, Portal + TCP/UDP Port, and Portal Index) in the Operating Attributes. + Using DD member attributes SHALL return both registered and + unregistered member Storage Nodes and/or Portals of a DD. + DevAttrQry messages using the Storage Node and/or Portal + object key SHALL return only member Storage Nodes or Portals + that are currently registered in the iSNS database. + +DevAttrDereg: + - PG Removal code: ignore nodes/portal that are dead + - review security + - cancel any SCN/ESI callbacks + +SCN: + - Trigger a mgmt reg SCN when accepting a mgmt registration + +SCNEvent: + - Implement + +ESI: + - Right now the way we re-establish ESI state after database + reload is awkward. + +DDReg: + - Write test cases + +DDDereg: + - Write test cases + +DDSReg/DDSDereg: + - Implement + +Heartbeat: + - Implement message send + - Implement failover? + +Security: + - Allow policies without key? + - Implement simple default policies linking client IP + + hostname (network entity) + storage node names + +Renaming + - Add isns_ prefix to all visible functions + +Socket code: + - impose upper limit on the reassembly buffer diff --git a/utils/open-isns/aclocal/config.guess b/utils/open-isns/aclocal/config.guess new file mode 100644 index 0000000..6d71f75 --- /dev/null +++ b/utils/open-isns/aclocal/config.guess @@ -0,0 +1,1499 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-05-27' + +# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner <per@bothner.com>. +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + amd64:OpenBSD:*:*) + echo x86_64-unknown-openbsd${UNAME_RELEASE} + exit ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit ;; + cats:OpenBSD:*:*) + echo arm-unknown-openbsd${UNAME_RELEASE} + exit ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit ;; + luna88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit ;; + sgi:OpenBSD:*:*) + echo mips64-unknown-openbsd${UNAME_RELEASE} + exit ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <features.h> + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && { + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit + } + test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + *86) UNAME_PROCESSOR=i686 ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c <<EOF +#ifdef _SEQUENT_ +# include <sys/types.h> +# include <sys/utsname.h> +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include <sys/param.h> +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess +and + http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/utils/open-isns/aclocal/config.sub b/utils/open-isns/aclocal/config.sub new file mode 100644 index 0000000..519f2cd --- /dev/null +++ b/utils/open-isns/aclocal/config.sub @@ -0,0 +1,1570 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-05-12' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | msp430-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* | -skyos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/utils/open-isns/aclocal/install-sh b/utils/open-isns/aclocal/install-sh new file mode 100644 index 0000000..220abbf --- /dev/null +++ b/utils/open-isns/aclocal/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/utils/open-isns/attrs.c b/utils/open-isns/attrs.c new file mode 100644 index 0000000..12517c1 --- /dev/null +++ b/utils/open-isns/attrs.c @@ -0,0 +1,1618 @@ +/* + * Handle iSNS attributes and attribute lists + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include "util.h" +#include "vendor.h" +#include "attrs.h" +#include "isns.h" + +/* Implementation limit - sanity checking */ +#define ISNS_ATTR_MAX_LEN 8192 + +static void __isns_attr_set_value(isns_attr_t *, const isns_value_t *); + +/* + * Allocate an attribute + */ +isns_attr_t * +isns_attr_alloc(uint32_t tag, const isns_tag_type_t *tag_type, const isns_value_t *value) +{ + isns_attr_t *attr; + + if (tag_type == NULL) + tag_type = isns_tag_type_by_id(tag); + + attr = isns_calloc(1, sizeof(*attr)); + if (!attr) + isns_fatal("Out of memory!\n"); + + attr->ia_users = 1; + attr->ia_tag_id = tag; + attr->ia_tag = tag_type; + + __isns_attr_set_value(attr, value); + return attr; +} + +isns_attr_t * +isns_attr_get(isns_attr_t *attr) +{ + if (attr) { + isns_assert(attr->ia_users); + attr->ia_users++; + } + return attr; +} + +void +isns_attr_release(isns_attr_t *attr) +{ + const isns_attr_type_t *type; + + isns_assert(attr->ia_users); + if (--(attr->ia_users)) + return; + + type = attr->ia_value.iv_type; + if (type->it_destroy) + type->it_destroy(&attr->ia_value); + isns_free(attr); +} + +/* + * Assign a value to an attribute + */ +void +__isns_attr_set_value(isns_attr_t *attr, const isns_value_t *new_value) +{ + const isns_attr_type_t *type, *old_type; + isns_value_t *old_value; + + old_value = &attr->ia_value; + if (old_value == new_value) + return; + + old_type = old_value->iv_type; + if (old_type && old_type->it_destroy) + old_type->it_destroy(old_value); + + if (!new_value || !(type = new_value->iv_type)) + type = attr->ia_tag->it_type; + + /* When assigning the value to the attr, check + * whether it needs special attention. */ + if (new_value) { + if (type->it_assign) { + type->it_assign(&attr->ia_value, new_value); + } else { + attr->ia_value = *new_value; + } + } + attr->ia_value.iv_type = type; +} + +/* + * Compare two attributes. + * Returns non-null when attributes are the same, else 0. + */ +int +isns_attr_match(const isns_attr_t *a, const isns_attr_t *b) +{ + const isns_attr_type_t *type; + + if (a->ia_tag_id != b->ia_tag_id) + return 0; + + /* NIL acts as a wildcard */ + if (a->ia_value.iv_type == &isns_attr_type_nil + || b->ia_value.iv_type == &isns_attr_type_nil) + return 1; + + if (a->ia_value.iv_type != b->ia_value.iv_type) + return 0; + type = a->ia_value.iv_type; + + if (type->it_match) + return type->it_match(&a->ia_value, &b->ia_value); + + return !memcmp(&a->ia_value, &b->ia_value, sizeof(isns_value_t)); +} + +/* + * Lexicographical comparison of two attributes. + * Returns -1 when a is less than b, +1 when a is greater than + * b, and 0 if equal. + */ +int +isns_attr_compare(const isns_attr_t *a, const isns_attr_t *b) +{ + const isns_attr_type_t *type = a->ia_value.iv_type; + + isns_assert(a->ia_tag_id == b->ia_tag_id); + + if (type != b->ia_value.iv_type) { + /* One of them must be NIL */ + if (type == &isns_attr_type_nil) + return -1; + return 1; + } + + /* If both are NIL, consider them equal */ + if (type == &isns_attr_type_nil) + return 0; + + /* A few types need special comparison functions, but + * most don't. The reason is, we don't care whether the + * ordering this creates is the "canonical" ordering for + * this type, eg for integers. All that matters is that + * there is some consistent ordering suitable for + * DevGetNext. + */ + if (type->it_compare) + return type->it_compare(&a->ia_value, &b->ia_value); + + return memcmp(&a->ia_value, &b->ia_value, sizeof(isns_value_t)); +} + +/* + * Convert a string to an attribute + */ +isns_attr_t * +isns_attr_from_string(uint32_t tag, const char *string) +{ + const isns_tag_type_t *tag_type; + int (*parse)(isns_value_t *, const char *); + isns_value_t value; + + memset(&value, 0, sizeof(value)); + + tag_type = isns_tag_type_by_id(tag); + if (!tag_type) + return NULL; + + parse = tag_type->it_parse; + if (parse == NULL) + parse = tag_type->it_type->it_parse; + + if (!parse || !parse(&value, string)) + return NULL; + + return isns_attr_alloc(tag, tag_type, &value); +} + +/* + * Initialize an attribute list. + */ +void +isns_attr_list_init(isns_attr_list_t *list) +{ + memset(list, 0, sizeof(*list)); +} + +static inline void +__isns_attr_list_resize(isns_attr_list_t *list, unsigned int count) +{ + unsigned int max; + + max = (list->ial_count + 15) & ~15; + if (count < max) + return; + + count = (count + 15) & ~15; + list->ial_data = isns_realloc(list->ial_data, count * sizeof(isns_attr_t *)); + if (!list->ial_data) + isns_fatal("Out of memory!\n"); +} + +void +isns_attr_list_append_list(isns_attr_list_t *dst, + const isns_attr_list_t *src) +{ + unsigned int i, j; + + __isns_attr_list_resize(dst, dst->ial_count + src->ial_count); + j = dst->ial_count; + for (i = 0; i < src->ial_count; ++i, ++j) { + isns_attr_t *attr = src->ial_data[i]; + + dst->ial_data[j] = attr; + attr->ia_users++; + } + dst->ial_count = j; +} + +void +isns_attr_list_copy(isns_attr_list_t *dst, + const isns_attr_list_t *src) +{ + isns_attr_list_destroy(dst); + isns_attr_list_append_list(dst, src); +} + +void +isns_attr_list_destroy(isns_attr_list_t *list) +{ + unsigned int i; + + for (i = 0; i < list->ial_count; ++i) { + isns_attr_t *attr = list->ial_data[i]; + + isns_attr_release(attr); + } + + if (list->ial_data) + isns_free(list->ial_data); + memset(list, 0, sizeof(*list)); +} + +int +isns_attr_list_remove_tag(isns_attr_list_t *list, uint32_t tag) +{ + unsigned int i = 0, j = 0, removed = 0; + + for (i = 0; i < list->ial_count; ++i) { + isns_attr_t *attr = list->ial_data[i]; + + if (attr->ia_tag_id == tag) { + isns_attr_release(attr); + removed++; + } else { + list->ial_data[j++] = attr; + } + } + list->ial_count = j; + return removed; +} + +/* + * Locate the given attribute in the list, remove it + * and any following attributes that have a tag from the + * @subordinate_tags list. This is used by the DDDereg + * code to remove DD members. + */ +int +isns_attr_list_remove_member(isns_attr_list_t *list, + const isns_attr_t *match, + const uint32_t *subordinate_tags) +{ + unsigned int i = 0, j = 0, k, removed = 0, purging = 0; + + while (i < list->ial_count) { + isns_attr_t *attr = list->ial_data[i++]; + + if (purging && subordinate_tags) { + for (k = 0; subordinate_tags[k]; ++k) { + if (attr->ia_tag_id == subordinate_tags[k]) + goto purge_attr; + } + } + purging = 0; + + if (!isns_attr_match(attr, match)) { + list->ial_data[j++] = attr; + continue; + } + +purge_attr: + isns_attr_release(attr); + purging = 1; + removed++; + } + list->ial_count = j; + return removed; +} + +/* + * Find the first attribute with the given tag + */ +static inline isns_attr_t * +__isns_attr_list_find(const isns_attr_list_t *list, uint32_t tag) +{ + isns_attr_t *attr; + unsigned int i; + + for (i = 0; i < list->ial_count; ++i) { + attr = list->ial_data[i]; + + if (attr->ia_tag_id == tag) + return attr; + } + + return NULL; +} + +/* + * Add a new attribute at the end of the list + */ +static inline void +__isns_attr_list_append_attr(isns_attr_list_t *list, isns_attr_t *attr) +{ + __isns_attr_list_resize(list, list->ial_count + 1); + list->ial_data[list->ial_count++] = attr; +} + +void +isns_attr_list_append_attr(isns_attr_list_t *list, isns_attr_t *attr) +{ + attr->ia_users++; + __isns_attr_list_append_attr(list, attr); +} + +/* + * Append an element to an attribute list + */ +static void +__isns_attr_list_append(isns_attr_list_t *list, + uint32_t tag, const isns_tag_type_t *tag_type, + const isns_value_t *value) +{ + isns_attr_t *attr; + + if (tag_type == NULL) + tag_type = isns_tag_type_by_id(tag); + if (value->iv_type != &isns_attr_type_nil + && value->iv_type != tag_type->it_type) { + isns_warning("Using wrong type (%s) " + "when encoding attribute %04x (%s) - should be %s\n", + value->iv_type->it_name, + tag, tag_type->it_name, + tag_type->it_type->it_name); + } + + attr = isns_attr_alloc(tag, tag_type, value); + __isns_attr_list_append_attr(list, attr); +} + +/* + * Update an element to an attribute list + */ +static void +__isns_attr_list_update(isns_attr_list_t *list, + uint32_t tag, const isns_tag_type_t *tag_type, + const isns_value_t *value) +{ + const isns_attr_type_t *type = value->iv_type; + isns_attr_t *attr; + + if (tag_type == NULL) + tag_type = isns_tag_type_by_id(tag); + if (type != &isns_attr_type_nil + && type != tag_type->it_type) { + isns_warning("Using wrong type (%s) " + "when encoding attribute %04x (%s) - should be %s\n", + type->it_name, + tag, tag_type->it_name, + tag_type->it_type->it_name); + } + + if (tag_type->it_multiple + || (attr = __isns_attr_list_find(list, tag)) == NULL) { + attr = isns_attr_alloc(tag, tag_type, NULL); + __isns_attr_list_append_attr(list, attr); + } + + __isns_attr_set_value(attr, value); +} + +/* + * Append an element to an attribute list - public interface + */ +void +isns_attr_list_append_value(isns_attr_list_t *list, + uint32_t tag, const isns_tag_type_t *tag_type, + const isns_value_t *value) +{ + __isns_attr_list_append(list, tag, tag_type, value); +} + +/* + * Update an element of an attribute list - public interface + */ +void +isns_attr_list_update_value(isns_attr_list_t *list, + uint32_t tag, const isns_tag_type_t *tag_type, + const isns_value_t *value) +{ + __isns_attr_list_update(list, tag, tag_type, value); +} + +void +isns_attr_list_update_attr(isns_attr_list_t *list, + const isns_attr_t *attr) +{ + __isns_attr_list_update(list, attr->ia_tag_id, + attr->ia_tag, &attr->ia_value); +} + +/* + * Replace an attribute on a list + */ +int +isns_attr_list_replace_attr(isns_attr_list_t *list, + isns_attr_t *attr) +{ + unsigned int i; + + for (i = 0; i < list->ial_count; ++i) { + isns_attr_t *other = list->ial_data[i]; + + if (other->ia_tag_id == attr->ia_tag_id) { + list->ial_data[i] = attr; + attr->ia_users++; + isns_attr_release(other); + return 1; + } + } + return 0; +} + +/* + * Retrieve an element of an attribute list + */ +int +isns_attr_list_get_attr(const isns_attr_list_t *list, + uint32_t tag, isns_attr_t **result) +{ + *result = __isns_attr_list_find(list, tag); + return *result != NULL; +} + +int +isns_attr_list_get_value(const isns_attr_list_t *list, + uint32_t tag, isns_value_t *value) +{ + isns_attr_t *attr; + + if (!(attr = __isns_attr_list_find(list, tag))) + return 0; + + *value = attr->ia_value; + return 1; +} + +int +isns_attr_list_get_uint32(const isns_attr_list_t *list, + uint32_t tag, uint32_t *value) +{ + isns_attr_t *attr; + + if (!(attr = __isns_attr_list_find(list, tag)) + || !ISNS_ATTR_IS_UINT32(attr)) + return 0; + + *value = attr->ia_value.iv_uint32; + return 1; +} + +int +isns_attr_list_get_ipaddr(const isns_attr_list_t *list, + uint32_t tag, struct in6_addr *value) +{ + isns_attr_t *attr; + + if (!(attr = __isns_attr_list_find(list, tag)) + || !ISNS_ATTR_IS_IPADDR(attr)) + return 0; + + *value = attr->ia_value.iv_ipaddr; + return 1; +} + +int +isns_attr_list_get_string(const isns_attr_list_t *list, + uint32_t tag, const char **value) +{ + isns_attr_t *attr; + + if (!(attr = __isns_attr_list_find(list, tag)) + || !ISNS_ATTR_IS_STRING(attr)) + return 0; + + *value = attr->ia_value.iv_string; + return 1; +} + +int +isns_attr_list_contains(const isns_attr_list_t *list, + uint32_t tag) +{ + return __isns_attr_list_find(list, tag) != NULL; +} + +/* + * Some attribute types have an implied ordering, + * which is needed for GetNext. This is used to + * compare two lists. + */ + +/* + * Typed versions of isns_attr_list_append + */ +void +isns_attr_list_append_nil(isns_attr_list_t *list, uint32_t tag) +{ + isns_value_t var = ISNS_VALUE_INIT(nil, 0); + + __isns_attr_list_append(list, tag, NULL, &var); +} + +void +isns_attr_list_append_string(isns_attr_list_t *list, + uint32_t tag, const char *value) +{ + isns_value_t var = ISNS_VALUE_INIT(string, (char *) value); + + __isns_attr_list_append(list, tag, NULL, &var); +} + +void +isns_attr_list_append_uint32(isns_attr_list_t *list, + uint32_t tag, uint32_t value) +{ + isns_value_t var = ISNS_VALUE_INIT(uint32, value); + + __isns_attr_list_append(list, tag, NULL, &var); +} + +void +isns_attr_list_append_int32(isns_attr_list_t *list, + uint32_t tag, int32_t value) +{ + isns_value_t var = ISNS_VALUE_INIT(int32, value); + + __isns_attr_list_append(list, tag, NULL, &var); +} + +void +isns_attr_list_append_uint64(isns_attr_list_t *list, + uint32_t tag, int64_t value) +{ + isns_value_t var = ISNS_VALUE_INIT(uint64, value); + + __isns_attr_list_append(list, tag, NULL, &var); +} + +void +isns_attr_list_append_ipaddr(isns_attr_list_t *list, + uint32_t tag, const struct in6_addr *value) +{ + isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value); + + __isns_attr_list_append(list, tag, NULL, &var); +} + +/* + * Untyped version of isns_attr_list_append and isns_attr_list_update. + * The caller must make sure that the type of @data matches the tag's type. + */ +int +isns_attr_list_append(isns_attr_list_t *list, uint32_t tag, const void *data) +{ + const isns_tag_type_t *tag_type; + isns_value_t var; + + if (!(tag_type = isns_tag_type_by_id(tag))) + return 0; + + var.iv_type = tag_type->it_type; + if (!var.iv_type->it_set(&var, data)) + return 0; + + __isns_attr_list_append(list, tag, tag_type, &var); + return 1; +} + +int +isns_attr_list_update(isns_attr_list_t *list, uint32_t tag, const void *data) +{ + const isns_tag_type_t *tag_type; + isns_attr_type_t *type; + isns_value_t var; + + if (!(tag_type = isns_tag_type_by_id(tag))) + return 0; + + type = tag_type->it_type; + var.iv_type = type; + if (!type->it_set(&var, data)) + return 0; + + __isns_attr_list_update(list, tag, tag_type, &var); + return 1; +} + +/* + * Validate the attribute list. + */ +int +isns_attr_validate(const isns_attr_t *attr, + const isns_policy_t *policy) +{ + const isns_tag_type_t *tag_type; + + tag_type = attr->ia_tag; + if (tag_type->it_validate == NULL) + return 1; + return tag_type->it_validate(&attr->ia_value, policy); +} + +int +isns_attr_list_validate(const isns_attr_list_t *list, + const isns_policy_t *policy, + unsigned int function) +{ + DECLARE_BITMAP(seen, __ISNS_TAG_MAX); + unsigned int i; + + for (i = 0; i < list->ial_count; ++i) { + const isns_tag_type_t *tag_type; + isns_attr_t *attr = list->ial_data[i]; + uint32_t tag = attr->ia_tag_id; + unsigned int bit; + + if (attr == NULL) + return ISNS_INTERNAL_ERROR; + + tag_type = attr->ia_tag; + if (tag_type == NULL) + return ISNS_INTERNAL_ERROR; + + bit = tag; + if (OPENISNS_IS_PRIVATE_ATTR(tag)) + bit -= OPENISNS_VENDOR_PREFIX; + if (bit >= __ISNS_TAG_MAX) + goto invalid; + + if (attr->ia_value.iv_type == &isns_attr_type_nil) { + if (test_bit(seen, bit)) + goto invalid; + } else + if (attr->ia_value.iv_type == tag_type->it_type) { + if (!tag_type->it_multiple && test_bit(seen, bit)) + goto invalid; + + if (!isns_attr_validate(attr, policy)) + goto invalid; + } else { + return ISNS_INTERNAL_ERROR; + } + + if (function == ISNS_DEVICE_ATTRIBUTE_REGISTER + && tag_type->it_readonly) + goto invalid; + + set_bit(seen, bit); + } + + return ISNS_SUCCESS; + +invalid: + switch (function) { + case ISNS_DEVICE_ATTRIBUTE_REGISTER: + return ISNS_INVALID_REGISTRATION; + + case ISNS_DEVICE_DEREGISTER: + return ISNS_INVALID_DEREGISTRATION; + + case ISNS_DEVICE_ATTRIBUTE_QUERY: + case ISNS_DEVICE_GET_NEXT: + return ISNS_INVALID_QUERY; + } + return ISNS_ATTRIBUTE_NOT_IMPLEMENTED; +} + +/* + * Debug helper: print attribute list + */ +void +isns_attr_list_print(const isns_attr_list_t *list, isns_print_fn_t *fn) +{ + unsigned int i; + + for (i = 0; i < list->ial_count; ++i) + isns_attr_print(list->ial_data[i], fn); +} + +char * +isns_attr_print_value(const isns_attr_t *attr, char *buffer, size_t size) +{ + const isns_tag_type_t *tag_type = attr->ia_tag; + const isns_attr_type_t *type = attr->ia_value.iv_type; + + if (tag_type->it_print && type == tag_type->it_type) + tag_type->it_print(&attr->ia_value, buffer, size); + else + type->it_print(&attr->ia_value, buffer, size); + return buffer; +} + +void +isns_attr_print(const isns_attr_t *attr, isns_print_fn_t *fn) +{ + const isns_tag_type_t *tag_type = attr->ia_tag; + const isns_attr_type_t *type = attr->ia_value.iv_type; + uint32_t tag; + char value[512], *vspec = ""; + + tag = attr->ia_tag_id; + if (OPENISNS_IS_PRIVATE_ATTR(tag)) { + tag -= OPENISNS_VENDOR_PREFIX; + vspec = "v"; + } + + fn(" %04x%1s %-12s: %s = %s\n", + tag, vspec, + type->it_name, + tag_type? tag_type->it_name : "Unknown Attribute", + isns_attr_print_value(attr, value, sizeof(value))); +} + +/* + * TLV encode a single attribute + */ +int +isns_attr_encode(buf_t *bp, const isns_attr_t *attr) +{ + const isns_value_t *value = &attr->ia_value; + const isns_attr_type_t *type = value->iv_type; + + if (!buf_put32(bp, attr->ia_tag_id) + || !type->it_encode(bp, value)) + return ISNS_INTERNAL_ERROR; + + return ISNS_SUCCESS; +} + +/* + * TLV decode a single attribute + */ +int +isns_attr_decode(buf_t *bp, isns_attr_t **result) +{ + isns_attr_t *attr = NULL; + isns_value_t *value; + uint32_t tag, len; + + if (!buf_get32(bp, &tag) + || !buf_get32(bp, &len)) + goto msg_fmt_error; + + /* Attributes MUST be word aligned */ + if (len & 3) + goto msg_fmt_error; + + if (len > ISNS_ATTR_MAX_LEN) + goto msg_fmt_error; + + /* Allocate the attribute */ + attr = isns_attr_alloc(tag, NULL, NULL); + + value = &attr->ia_value; + if (len == 0) + value->iv_type = &isns_attr_type_nil; + + if (!value->iv_type->it_decode(bp, len, value)) + goto msg_fmt_error; + + *result = attr; + return ISNS_SUCCESS; + +msg_fmt_error: + isns_error("Error decoding attribute, tag=0x%04x, len=%u\n", + tag, len); + if (attr) + isns_attr_release(attr); + return ISNS_MESSAGE_FORMAT_ERROR; +} + + +/* + * Decode the list of TLV encoded attributes inside an + * iSNS message. + */ +static int +__isns_attr_list_decode(buf_t *bp, isns_attr_list_t *list, int delimited) +{ + int status; + + while (buf_avail(bp)) { + isns_attr_t *attr; + + status = isns_attr_decode(bp, &attr); + if (status != ISNS_SUCCESS) + return status; + + if (delimited && attr->ia_tag_id == ISNS_TAG_DELIMITER) { + isns_attr_release(attr); + break; + } + + __isns_attr_list_append_attr(list, attr); + } + + return ISNS_SUCCESS; +} + +int +isns_attr_list_decode(buf_t *bp, isns_attr_list_t *list) +{ + return __isns_attr_list_decode(bp, list, 0); +} + +int +isns_attr_list_decode_delimited(buf_t *bp, isns_attr_list_t *list) +{ + return __isns_attr_list_decode(bp, list, 1); +} + +/* + * Remove all attributes from a list save those matching + * the given tags. + */ +void +isns_attr_list_prune(isns_attr_list_t *list, + const uint32_t *tags, unsigned int num_tags) +{ + unsigned int i, j, k; + + for (i = j = 0; i < list->ial_count; ++i) { + isns_attr_t *attr = list->ial_data[i]; + + for (k = 0; k < num_tags; ++k) { + if (attr->ia_tag_id == tags[k]) { + list->ial_data[j++] = attr; + goto next; + } + } + + isns_attr_release(attr); + +next: ; + } + + list->ial_count = j; +} + +/* + * TLV ecode the list of attributes to go with + * iSNS message. + */ +int +isns_attr_list_encode(buf_t *bp, const isns_attr_list_t *list) +{ + unsigned int i, status = ISNS_SUCCESS; + + for (i = 0; i < list->ial_count; ++i) { + struct isns_attr *attr = list->ial_data[i]; + + status = isns_attr_encode(bp, attr); + if (status) + break; + } + return status; +} + +/* + * Encode the delimiter attribute + */ +int +isns_encode_delimiter(buf_t *bp) +{ + uint32_t tag = 0, len = 0; + + if (!buf_put32(bp, tag) + || !buf_put32(bp, len)) + return ISNS_INTERNAL_ERROR; + + return ISNS_SUCCESS; +} + +/* + * Padded encoding + */ +static inline int +isns_encode_padded(buf_t *bp, const void *ptr, size_t len) +{ + if (!buf_put(bp, ptr, len)) + return 0; + + if ((len & 3) == 0) + return 1; + + return buf_put(bp, "\0\0\0", 4 - (len & 3)); +} + +/* + * Helper functions to deal with portal information + */ +void +isns_portal_init(isns_portal_info_t *portal, + const struct sockaddr *saddr, int proto) +{ + const struct sockaddr_in *sin; + + memset(portal, 0, sizeof(*portal)); + switch (saddr->sa_family) { + case AF_INET6: + portal->addr = *(const struct sockaddr_in6 *) saddr; + break; + + case AF_INET: + sin = (const struct sockaddr_in *) saddr; + portal->addr.sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; + portal->addr.sin6_port = sin->sin_port; + portal->addr.sin6_family = AF_INET6; + break; + default: + isns_warning("Unknown address family in isns_portal_init\n"); + return; + } + + portal->proto = proto; +} + +int +isns_portal_from_attr_list(isns_portal_info_t *portal, + uint32_t addr_tag, uint32_t port_tag, + const isns_attr_list_t *list) +{ + const isns_attr_t *addr_attr = NULL, *port_attr = NULL; + unsigned int i; + + for (i = 0; i + 1 < list->ial_count; ++i) { + const isns_attr_t *attr = list->ial_data[i]; + + if (!ISNS_ATTR_IS_IPADDR(attr)) + continue; + if (addr_tag && attr->ia_tag_id != addr_tag) + continue; + addr_attr = attr; + if (port_tag == 0) { + port_attr = list->ial_data[i + 1]; + goto extract_portal; + } + break; + } + + /* We have a specific port tag. */ + while (++i < list->ial_count) { + const isns_attr_t *attr = list->ial_data[i]; + + if (attr->ia_tag_id == port_tag) { + port_attr = attr; + goto extract_portal; + } + } + + return 0; + +extract_portal: + return isns_portal_from_attr_pair(portal, + addr_attr, port_attr); +} + +int +isns_portal_from_attr_pair(isns_portal_info_t *portal, + const isns_attr_t *addr_attr, + const isns_attr_t *port_attr) +{ + uint32_t portspec; + + memset(portal, 0, sizeof(*portal)); + portal->addr.sin6_family = AF_INET6; + + if (!ISNS_ATTR_IS_IPADDR(addr_attr) + || !ISNS_ATTR_IS_UINT32(port_attr)) + return 0; + + portal->addr.sin6_addr = addr_attr->ia_value.iv_ipaddr; + + portspec = port_attr->ia_value.iv_uint32; + portal->addr.sin6_port = htons(portspec & 0xffff); + portal->proto = (portspec & ISNS_PORTAL_PORT_UDP_MASK)? IPPROTO_UDP : IPPROTO_TCP; + + return 1; +} + +int +isns_portal_to_attr_list(const isns_portal_info_t *portal, + uint32_t addr_tag, uint32_t port_tag, + isns_attr_list_t *list) +{ + uint32_t portspec; + + portspec = htons(portal->addr.sin6_port); + if (portal->proto == IPPROTO_UDP) + portspec |= ISNS_PORTAL_PORT_UDP_MASK; + + { + isns_value_t addr_value = ISNS_VALUE_INIT(ipaddr, portal->addr.sin6_addr); + isns_value_t port_value = ISNS_VALUE_INIT(uint32, portspec); + + isns_attr_list_update_value(list, addr_tag, NULL, &addr_value); + isns_attr_list_update_value(list, port_tag, NULL, &port_value); + } + + return 1; +} + +const char * +isns_portal_string(const isns_portal_info_t *portal) +{ + const struct sockaddr_in6 *six = &portal->addr; + static char buffer[128]; + char abuf[128]; + + inet_ntop(six->sin6_family, &six->sin6_addr, abuf, sizeof(abuf)); + snprintf(buffer, sizeof(buffer), "[%s]:%d/%s", + abuf, ntohs(six->sin6_port), + (portal->proto == IPPROTO_UDP)? "udp" : "tcp"); + return buffer; +} + +int +isns_portal_is_wildcard(const isns_portal_info_t *portal) +{ + return !memcmp(&portal->addr.sin6_addr, + &in6addr_any, + sizeof(struct in6_addr)); +} + +int +isns_portal_equal(const isns_portal_info_t *a, + const isns_portal_info_t *b) +{ + if (a->proto != b->proto) + return 0; + return !memcmp(&a->addr, &b->addr, sizeof(a->addr)); +} + +uint32_t +isns_portal_tcpudp_port(const isns_portal_info_t *portal) +{ + uint32_t port; + + port = isns_addr_get_port((const struct sockaddr *) &portal->addr); + if (portal->proto == IPPROTO_UDP) + port |= ISNS_PORTAL_PORT_UDP_MASK; + return port; +} + +int +isns_portal_parse(isns_portal_info_t *portal, + const char *spec, + const char *default_port) +{ + struct sockaddr_storage addr; + char *copy, *psp; + int alen, proto = IPPROTO_TCP, sock_type = SOCK_STREAM; + + if (spec[0] == '/') { + isns_warning("%s: no AF_LOCAL addresses for portals!\n", + __FUNCTION__); + return 0; + } + + /* Look at trailing /tcp or /udp */ + copy = isns_strdup(spec); + if ((psp = strrchr(copy, '/')) != NULL) { + if (!strcasecmp(psp, "/udp")) { + sock_type = SOCK_DGRAM; + proto = IPPROTO_UDP; + *psp = '\0'; + } else + if (!strcasecmp(psp, "/tcp")) { + sock_type = SOCK_STREAM; + proto = IPPROTO_TCP; + *psp = '\0'; + } + } + + alen = isns_get_address(&addr, copy, default_port, 0, sock_type, 0); + isns_free(copy); + + if (alen < 0) + return 0; + + isns_portal_init(portal, (struct sockaddr *) &addr, proto); + return 1; +} + +/* + * Attribute type NIL + */ +static int +isns_attr_type_nil_encode(buf_t *bp, const isns_value_t *value) +{ + return buf_put32(bp, 0); +} + +static int +isns_attr_type_nil_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + return len == 0; +} + +static void +isns_attr_type_nil_print(const isns_value_t *value, char *buf, size_t size) +{ + snprintf(buf, size, "<empty>"); +} + +static int +isns_attr_type_nil_parse(isns_value_t *value, const char *string) +{ + if (string && *string) + return 0; + return 1; +} + +isns_attr_type_t isns_attr_type_nil = { + .it_id = ISNS_ATTR_TYPE_NIL, + .it_name = "nil", + .it_encode = isns_attr_type_nil_encode, + .it_decode = isns_attr_type_nil_decode, + .it_print = isns_attr_type_nil_print, + .it_parse = isns_attr_type_nil_parse, +}; + +/* + * Attribute type UINT32 + */ +static int +isns_attr_type_uint32_encode(buf_t *bp, const isns_value_t *value) +{ + return buf_put32(bp, 4) && buf_put32(bp, value->iv_uint32); +} + +static int +isns_attr_type_uint32_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + if (len != 4) + return 0; + return buf_get32(bp, &value->iv_uint32); +} + +static void +isns_attr_type_uint32_print(const isns_value_t *value, char *buf, size_t size) +{ + snprintf(buf, size, "%u", value->iv_uint32); +} + +static int +isns_attr_type_uint32_parse(isns_value_t *value, const char *string) +{ + char *end; + + value->iv_uint32 = strtoul(string, &end, 0); + return *end == '\0'; +} + +static void +isns_attr_type_int32_print(const isns_value_t *value, char *buf, size_t size) +{ + snprintf(buf, size, "%d", value->iv_uint32); +} + +static int +isns_attr_type_int32_parse(isns_value_t *value, const char *string) +{ + char *end; + + value->iv_int32 = strtol(string, &end, 0); + return *end == '\0'; +} + +isns_attr_type_t isns_attr_type_uint32 = { + .it_id = ISNS_ATTR_TYPE_UINT32, + .it_name = "uint32", + .it_encode = isns_attr_type_uint32_encode, + .it_decode = isns_attr_type_uint32_decode, + .it_print = isns_attr_type_uint32_print, + .it_parse = isns_attr_type_uint32_parse, +}; + +isns_attr_type_t isns_attr_type_int32 = { + .it_id = ISNS_ATTR_TYPE_INT32, + .it_name = "int32", + .it_encode = isns_attr_type_uint32_encode, + .it_decode = isns_attr_type_uint32_decode, + .it_print = isns_attr_type_int32_print, + .it_parse = isns_attr_type_int32_parse, +}; + +/* + * 16bit min/max + */ +static int +isns_attr_type_range16_encode(buf_t *bp, const isns_value_t *value) +{ + uint32_t word; + + word = (value->iv_range.max << 16) | value->iv_range.min; + return buf_put32(bp, 4) && buf_put32(bp, word); +} + +static int +isns_attr_type_range16_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + uint32_t word; + + if (len != 4) + return 0; + if (!buf_get32(bp, &word)) + return 0; + value->iv_range.max = word >> 16; + value->iv_range.min = word & 0xFFFF; + return 1; +} + +static void +isns_attr_type_range16_print(const isns_value_t *value, char *buf, size_t size) +{ + snprintf(buf, size, "[%u, %u]", value->iv_range.min, value->iv_range.max); +} + +isns_attr_type_t isns_attr_type_range16 = { + .it_id = ISNS_ATTR_TYPE_RANGE16, + .it_name = "range16", + .it_encode = isns_attr_type_range16_encode, + .it_decode = isns_attr_type_range16_decode, + .it_print = isns_attr_type_range16_print, +// .it_parse = isns_attr_type_range16_parse, +}; + + +/* + * 64bit integers + */ +static int +isns_attr_type_uint64_encode(buf_t *bp, const isns_value_t *value) +{ + return buf_put32(bp, 8) && buf_put64(bp, value->iv_uint64); +} + +static int +isns_attr_type_uint64_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + if (len != 8) + return 0; + return buf_get64(bp, &value->iv_uint64); +} + +static void +isns_attr_type_uint64_print(const isns_value_t *value, char *buf, size_t size) +{ + snprintf(buf, size, "%Lu", (unsigned long long) value->iv_uint64); +} + +static int +isns_attr_type_uint64_parse(isns_value_t *value, const char *string) +{ + char *end; + + value->iv_uint64 = strtoull(string, &end, 0); + return *end == '\0'; +} + +isns_attr_type_t isns_attr_type_uint64 = { + .it_id = ISNS_ATTR_TYPE_UINT64, + .it_name = "uint64", + .it_encode = isns_attr_type_uint64_encode, + .it_decode = isns_attr_type_uint64_decode, + .it_print = isns_attr_type_uint64_print, + .it_parse = isns_attr_type_uint64_parse, +}; + +/* + * Attribute type STRING + */ +static void +isns_attr_type_string_destroy(isns_value_t *value) +{ + isns_free(value->iv_string); + value->iv_string = NULL; +} + +static int +isns_attr_type_string_match(const isns_value_t *a, const isns_value_t *b) +{ + if (a->iv_string && b->iv_string) + return !strcmp(a->iv_string, b->iv_string); + + return a->iv_string == b->iv_string; +} + +static int +isns_attr_type_string_compare(const isns_value_t *a, const isns_value_t *b) +{ + if (a->iv_string && b->iv_string) + return strcmp(a->iv_string, b->iv_string); + + return a->iv_string? 1 : -1; +} + +static int +isns_attr_type_string_encode(buf_t *bp, const isns_value_t *value) +{ + uint32_t len; + + len = value->iv_string? strlen(value->iv_string) + 1 : 0; + + if (!buf_put32(bp, ISNS_PAD(len))) + return 0; + + if (len && !isns_encode_padded(bp, value->iv_string, len)) + return 0; + + return 1; +} + +static int +isns_attr_type_string_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + /* Is this legal? */ + if (len == 0) + return 1; + + /* The string should be NUL terminated, but + * better be safe than sorry. */ + value->iv_string = isns_malloc(len + 1); + if (!buf_get(bp, value->iv_string, len)) { + isns_free(value->iv_string); + return 0; + } + value->iv_string[len] = '\0'; + return 1; +} + +static void +isns_attr_type_string_print(const isns_value_t *value, char *buf, size_t size) +{ + if (!value->iv_string) + snprintf(buf, size, "(empty)"); + else + snprintf(buf, size, "\"%s\"", value->iv_string); +} + +static int +isns_attr_type_string_parse(isns_value_t *value, const char *string) +{ + value->iv_string = isns_strdup(string); + return 1; +} + +static void +isns_attr_type_string_assign(isns_value_t *value, const isns_value_t *new_value) +{ + isns_assert(!value->iv_string); + if (new_value->iv_string) + value->iv_string = isns_strdup(new_value->iv_string); +} + +isns_attr_type_t isns_attr_type_string = { + .it_id = ISNS_ATTR_TYPE_STRING, + .it_name = "string", + .it_assign = isns_attr_type_string_assign, + .it_destroy = isns_attr_type_string_destroy, + .it_match = isns_attr_type_string_match, + .it_compare = isns_attr_type_string_compare, + .it_encode = isns_attr_type_string_encode, + .it_decode = isns_attr_type_string_decode, + .it_print = isns_attr_type_string_print, + .it_parse = isns_attr_type_string_parse, +}; + +/* + * Attribute type IPADDR + */ +static int +isns_attr_type_ipaddr_encode(buf_t *bp, const isns_value_t *value) +{ + if (!buf_put32(bp, 16) + || !buf_put(bp, &value->iv_ipaddr, 16)) + return 0; + + return 1; +} + +static int +isns_attr_type_ipaddr_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + if (len != 16) + return 0; + + return buf_get(bp, &value->iv_ipaddr, 16); +} + +static void +isns_attr_type_ipaddr_print(const isns_value_t *value, char *buf, size_t size) +{ + const struct in6_addr *addr = &value->iv_ipaddr; + char buffer[INET6_ADDRSTRLEN + 1]; + + /* The standard requires IPv4 mapping, but + * some oldish implementations seem to use + * IPv4 compatible addresss. */ + if (IN6_IS_ADDR_V4MAPPED(addr) || IN6_IS_ADDR_V4COMPAT(addr)) { + struct in_addr ipv4; + + ipv4.s_addr = addr->s6_addr32[3]; + inet_ntop(AF_INET, &ipv4, buffer, sizeof(buffer)); + } else { + inet_ntop(AF_INET6, addr, buffer, sizeof(buffer)); + } + snprintf(buf, size, "%s", buffer); +} + +static int +isns_attr_type_ipaddr_parse(isns_value_t *value, const char *string) +{ + struct in_addr addr4; + + if (inet_pton(AF_INET, string, &addr4)) { + value->iv_ipaddr = in6addr_any; + value->iv_ipaddr.s6_addr32[3] = addr4.s_addr; + return 1; + } + + return inet_pton(AF_INET6, string, &value->iv_ipaddr); +} + +isns_attr_type_t isns_attr_type_ipaddr = { + .it_id = ISNS_ATTR_TYPE_IPADDR, + .it_name = "ipaddr", + .it_encode = isns_attr_type_ipaddr_encode, + .it_decode = isns_attr_type_ipaddr_decode, + .it_print = isns_attr_type_ipaddr_print, + .it_parse = isns_attr_type_ipaddr_parse, +}; + +/* + * Attribute type OPAQUE + */ +static void +isns_attr_type_opaque_assign(isns_value_t *value, const isns_value_t *new_value) +{ + size_t new_len = new_value->iv_opaque.len; + isns_assert(value->iv_opaque.len == 0); + if (new_len) { + value->iv_opaque.ptr = isns_malloc(new_len); + value->iv_opaque.len = new_len; + memcpy(value->iv_opaque.ptr, + new_value->iv_opaque.ptr, + new_len); + } +} + +static void +isns_attr_type_opaque_destroy(isns_value_t *value) +{ + isns_free(value->iv_opaque.ptr); + value->iv_opaque.ptr = NULL; + value->iv_opaque.len = 0; +} + +static int +isns_attr_type_opaque_match(const isns_value_t *a, const isns_value_t *b) +{ + if (a->iv_opaque.len != b->iv_opaque.len) + return 0; + return !memcmp(a->iv_opaque.ptr, b->iv_opaque.ptr, a->iv_opaque.len); +} + +static int +isns_attr_type_opaque_compare(const isns_value_t *a, const isns_value_t *b) +{ + long delta; + + delta = a->iv_opaque.len - b->iv_opaque.len; + if (delta) + return delta; + + return memcmp(a->iv_opaque.ptr, b->iv_opaque.ptr, a->iv_opaque.len); +} + +static int +isns_attr_type_opaque_encode(buf_t *bp, const isns_value_t *value) +{ + uint32_t len; + + len = value->iv_opaque.len; + if (len & 3) + return 0; + + if (!buf_put32(bp, len) + || !buf_put(bp, value->iv_opaque.ptr, len)) + return 0; + + return 1; +} + +static int +isns_attr_type_opaque_decode(buf_t *bp, size_t len, isns_value_t *value) +{ + value->iv_opaque.ptr = isns_malloc(len); + if (!buf_get(bp, value->iv_opaque.ptr, len)) { + isns_free(value->iv_opaque.ptr); + return 0; + } + + value->iv_opaque.len = len; + return 1; +} + +static void +isns_attr_type_opaque_print(const isns_value_t *value, char *buf, size_t size) +{ + unsigned char *data = value->iv_opaque.ptr; + unsigned int i, len; + + /* There must be room for "<...>\0" */ + if (size < 6) + return; + size -= 6; + + if ((len = value->iv_opaque.len) > 20) + len = 20; + if (size < 3 * len) + len = size / 3; + + *buf++ = '<'; + for (i = 0; i < len; ++i) { + if (i) + *buf++ = ' '; + sprintf(buf, "%02x", data[i]); + buf += 2; + } + if (len < value->iv_opaque.len) { + strcat(buf, "..."); + buf += 4; + } + *buf++ = '>'; + *buf++ = '\0'; +} + +isns_attr_type_t isns_attr_type_opaque = { + .it_id = ISNS_ATTR_TYPE_OPAQUE, + .it_name = "opaque", + .it_assign = isns_attr_type_opaque_assign, + .it_destroy = isns_attr_type_opaque_destroy, + .it_match = isns_attr_type_opaque_match, + .it_compare = isns_attr_type_opaque_compare, + .it_encode = isns_attr_type_opaque_encode, + .it_decode = isns_attr_type_opaque_decode, + .it_print = isns_attr_type_opaque_print, +}; + +/* + * Map attribute type IDs to attribute types + */ +static isns_attr_type_t * +isns_attr_types_builtin[__ISNS_ATTR_TYPE_BUILTIN_MAX] = { +[ISNS_ATTR_TYPE_NIL] = &isns_attr_type_nil, +[ISNS_ATTR_TYPE_OPAQUE] = &isns_attr_type_opaque, +[ISNS_ATTR_TYPE_STRING] = &isns_attr_type_string, +[ISNS_ATTR_TYPE_INT32] = &isns_attr_type_int32, +[ISNS_ATTR_TYPE_UINT32] = &isns_attr_type_uint32, +[ISNS_ATTR_TYPE_UINT64] = &isns_attr_type_uint64, +[ISNS_ATTR_TYPE_IPADDR] = &isns_attr_type_ipaddr, +[ISNS_ATTR_TYPE_RANGE16] = &isns_attr_type_range16, +}; + +const isns_attr_type_t * +isns_attr_type_by_id(unsigned int id) +{ + if (id < __ISNS_ATTR_TYPE_BUILTIN_MAX) + return isns_attr_types_builtin[id]; + + /* TODO: handle dynamic registration of attrtypes + * for vendor extensions. */ + return NULL; +} diff --git a/utils/open-isns/attrs.h b/utils/open-isns/attrs.h new file mode 100644 index 0000000..1d3667e --- /dev/null +++ b/utils/open-isns/attrs.h @@ -0,0 +1,262 @@ +/* + * iSNS object attributes + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_ATTRS_H +#define ISNS_ATTRS_H + +#include <netinet/in.h> +#include "buffer.h" +#include "isns.h" + +/* + * Type identifier + */ +enum { + ISNS_ATTR_TYPE_NIL = 0, + ISNS_ATTR_TYPE_OPAQUE, + ISNS_ATTR_TYPE_STRING, + ISNS_ATTR_TYPE_INT32, + ISNS_ATTR_TYPE_UINT32, + ISNS_ATTR_TYPE_UINT64, + ISNS_ATTR_TYPE_IPADDR, + ISNS_ATTR_TYPE_RANGE16, + + __ISNS_ATTR_TYPE_BUILTIN_MAX +}; + +/* + * Union holding an attribute value + */ +typedef struct isns_value { + const struct isns_attr_type * iv_type; + + /* Data is stuffed into an anonymous union */ + union { + uint32_t iv_nil; + struct __isns_opaque { + void * ptr; + size_t len; + } iv_opaque; + char * iv_string; + int32_t iv_int32; + uint32_t iv_uint32; + uint64_t iv_uint64; + struct in6_addr iv_ipaddr; + struct { + uint16_t min, max; + } iv_range; + }; +} isns_value_t; + +#define __ISNS_ATTRTYPE(type) isns_attr_type_##type +#define __ISNS_MEMBER(type) iv_##type +#define ISNS_VALUE_INIT(type, value) \ + (isns_value_t) { .iv_type = &__ISNS_ATTRTYPE(type), \ + { .__ISNS_MEMBER(type) = (value) } } + +#define isns_attr_initialize(attrp, tag, type, value) do { \ + isns_attr_t *__attr = (attrp); \ + uint32_t __tag = (tag); \ + __attr->ia_users = 1; \ + __attr->ia_tag_id = (__tag); \ + __attr->ia_tag = isns_tag_type_by_id(__tag); \ + __attr->ia_value = ISNS_VALUE_INIT(type, value); \ + } while (0) +#define ISNS_ATTR_INIT(tag, type, value) (isns_attr_t) { \ + .ia_users = 1, \ + .ia_tag_id = (tag), \ + .ia_tag = isns_tag_type_by_id(tag), \ + .ia_value = ISNS_VALUE_INIT(type, value) \ + } + +/* + * Attribute type + */ +typedef struct isns_attr_type { + uint32_t it_id; + const char * it_name; + + void (*it_assign)(isns_value_t *, const isns_value_t *); + int (*it_set)(isns_value_t *, const void *); + int (*it_get)(isns_value_t *, void *); + int (*it_match)(const isns_value_t *, const isns_value_t *); + int (*it_compare)(const isns_value_t *, const isns_value_t *); + int (*it_encode)(buf_t *, const isns_value_t *); + int (*it_decode)(buf_t *, size_t, isns_value_t *); + void (*it_destroy)(isns_value_t *); + void (*it_print)(const isns_value_t *, char *, size_t); + int (*it_parse)(isns_value_t *, const char *); +} isns_attr_type_t; + +/* + * Tag info: for each tag, provides a printable name, + * and the attribute type associated with it. + */ +struct isns_tag_type { + uint32_t it_id; + const char * it_name; + unsigned int it_multiple : 1, + it_readonly : 1; + isns_attr_type_t *it_type; + + int (*it_validate)(const isns_value_t *, + const isns_policy_t *); + void (*it_print)(const isns_value_t *, char *, size_t); + int (*it_parse)(isns_value_t *, const char *); + const char * (*it_help)(void); +}; + +/* + * Attribute + */ +struct isns_attr { + unsigned int ia_users; + uint32_t ia_tag_id; + const isns_tag_type_t * ia_tag; + isns_value_t ia_value; +}; + +extern isns_attr_type_t isns_attr_type_nil; +extern isns_attr_type_t isns_attr_type_opaque; +extern isns_attr_type_t isns_attr_type_string; +extern isns_attr_type_t isns_attr_type_int32; +extern isns_attr_type_t isns_attr_type_uint32; +extern isns_attr_type_t isns_attr_type_uint64; +extern isns_attr_type_t isns_attr_type_ipaddr; +extern isns_attr_type_t isns_attr_type_range16; + +extern isns_attr_t * isns_attr_alloc(uint32_t, const isns_tag_type_t *, + const isns_value_t *); + +extern void isns_attr_list_append_value(isns_attr_list_t *, + uint32_t tag, const isns_tag_type_t *, + const isns_value_t *); +extern void isns_attr_list_update_value(isns_attr_list_t *, + uint32_t tag, const isns_tag_type_t *, + const isns_value_t *); +extern int isns_attr_list_get_value(const isns_attr_list_t *, + uint32_t tag, + isns_value_t *); +extern int isns_attr_list_get_uint32(const isns_attr_list_t *, + uint32_t tag, + uint32_t *); +extern int isns_attr_list_get_string(const isns_attr_list_t *, + uint32_t tag, + const char **); + +extern int isns_attr_list_validate(const isns_attr_list_t *, + const isns_policy_t *, + unsigned int function); +extern int isns_attr_validate(const isns_attr_t *, + const isns_policy_t *); + +extern void isns_attr_list_prune(isns_attr_list_t *, + const uint32_t *, + unsigned int); +extern int isns_attr_list_remove_member(isns_attr_list_t *, + const isns_attr_t *, + const uint32_t *); +extern void isns_attr_list_update_attr(isns_attr_list_t *, + const isns_attr_t *); + +extern int isns_attr_decode(buf_t *, isns_attr_t **); +extern int isns_attr_encode(buf_t *, const isns_attr_t *); + +extern int isns_attr_list_decode(buf_t *, isns_attr_list_t *); +extern int isns_attr_list_decode_delimited(buf_t *, isns_attr_list_t *); +extern int isns_attr_list_encode(buf_t *, const isns_attr_list_t *); +extern int isns_encode_delimiter(buf_t *); + +extern const isns_tag_type_t *isns_tag_type_by_id(unsigned int); +extern const isns_attr_type_t *isns_attr_type_by_id(unsigned int); + +typedef struct isns_quick_attr_list isns_quick_attr_list_t; +struct isns_quick_attr_list { + isns_attr_list_t iqa_list; + isns_attr_t * iqa_attrs[1]; + isns_attr_t iqa_attr; +}; +#define ISNS_QUICK_ATTR_LIST_DECLARE(qlist, tag, type, value) \ + isns_quick_attr_list_t qlist = { \ + .iqa_list = (isns_attr_list_t) { \ + .ial_data = qlist.iqa_attrs, \ + .ial_count = 1 \ + }, \ + .iqa_attrs = { &qlist.iqa_attr }, \ + .iqa_attr = ISNS_ATTR_INIT(tag, type, value), \ + } + +/* + * The following is used to chop up an incoming attr list as + * given in eg. a DevAttrReg message into separate chunks, + * following the ordering constraints laid out in the RFC. + * + * isns_attr_list_scanner_init initializes the scanner state. + * + * isns_attr_list_scanner_next advances to the next object in + * the list, returning the keys and attrs for one object. + * + * The isns_attr_list_scanner struct should really be opaque, but + * we put it here so you can declare a scanner variable on the + * stack. + */ +struct isns_attr_list_scanner { + isns_source_t * source; + isns_policy_t * policy; + isns_object_t * key_obj; + isns_attr_list_t orig_attrs; + unsigned int pos; + + isns_attr_list_t keys; + isns_attr_list_t attrs; + isns_object_template_t *tmpl; + unsigned int num_key_attrs; + + unsigned int entities; + + uint32_t pgt_next_attr; + uint32_t pgt_value; + const char * pgt_iscsi_name; + isns_portal_info_t pgt_portal_info; + isns_object_t * pgt_base_object; + + unsigned int index_acceptable : 1; +}; + +extern void isns_attr_list_scanner_init(struct isns_attr_list_scanner *, + isns_object_t *key_obj, + const isns_attr_list_t *attrs); +extern int isns_attr_list_scanner_next(struct isns_attr_list_scanner *); +extern void isns_attr_list_scanner_destroy(struct isns_attr_list_scanner *); + +/* + * The following is used to parse attribute lists given as + * a bunch of strings. + */ +struct isns_attr_list_parser { + struct isns_tag_prefix *prefix; + const char * default_port; + + unsigned int multi_type_permitted : 1, + nil_permitted : 1; + + isns_attr_t * (*load_key)(const char *); + isns_attr_t * (*generate_key)(void); +}; + +extern int isns_attr_list_split(char *line, char **argv, unsigned int argc_max); +extern void isns_attr_list_parser_init(struct isns_attr_list_parser *, + isns_object_template_t *); +extern int isns_parse_attrs(unsigned int, char **, + isns_attr_list_t *, struct isns_attr_list_parser *); +extern int isns_parse_query_attrs(unsigned int, char **, + isns_attr_list_t *, isns_attr_list_t *, + struct isns_attr_list_parser *); +extern void isns_attr_list_parser_help(struct isns_attr_list_parser *); +extern isns_object_template_t *isns_attr_list_parser_context(const struct isns_attr_list_parser *); +extern int isns_print_attrs(isns_object_t *, char **, unsigned int); + +#endif /* ISNS_ATTRS_H */ diff --git a/utils/open-isns/authblock.c b/utils/open-isns/authblock.c new file mode 100644 index 0000000..76d35b4 --- /dev/null +++ b/utils/open-isns/authblock.c @@ -0,0 +1,62 @@ +/* + * iSNS authentication functions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "message.h" +#include "util.h" + +/* We impose an artificial limit on the size of + * the size of the authenticator + */ +#define ISNS_SPISTR_MAX 512 + +int +isns_authblock_decode(buf_t *bp, struct isns_authblk *auth) +{ + unsigned int avail = buf_avail(bp); + + if (!buf_get32(bp, &auth->iab_bsd) + || !buf_get32(bp, &auth->iab_length) + || !buf_get64(bp, &auth->iab_timestamp) + || !buf_get32(bp, &auth->iab_spi_len)) + return 0; + + /* Make sure the length specified by the auth block + * is reasonable. */ + if (auth->iab_length < ISNS_AUTHBLK_SIZE + || auth->iab_length > avail) + return 0; + + /* This chops off any data trailing the auth block. + * It also makes sure that we detect if iab_length + * exceeds the amount of available data. */ + if (!buf_truncate(bp, auth->iab_length - ISNS_AUTHBLK_SIZE)) + return 0; + + auth->iab_spi = buf_head(bp); + if (!buf_pull(bp, auth->iab_spi_len)) + return 0; + + auth->iab_sig = buf_head(bp); + auth->iab_sig_len = buf_avail(bp); + return 1; +} + +int +isns_authblock_encode(buf_t *bp, const struct isns_authblk *auth) +{ + if (!buf_put32(bp, auth->iab_bsd) + || !buf_put32(bp, auth->iab_length) + || !buf_put64(bp, auth->iab_timestamp) + || !buf_put32(bp, auth->iab_spi_len) + || !buf_put(bp, auth->iab_spi, auth->iab_spi_len) + || !buf_put(bp, auth->iab_sig, auth->iab_sig_len)) + return 0; + return 1; +} diff --git a/utils/open-isns/bitvector.c b/utils/open-isns/bitvector.c new file mode 100644 index 0000000..3e23f26 --- /dev/null +++ b/utils/open-isns/bitvector.c @@ -0,0 +1,651 @@ +/* + * Handle bit vector as a run length encoded array of + * 32bit words. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "util.h" + +struct isns_bitvector { + unsigned int ib_count; + uint32_t * ib_words; +}; + +void +isns_bitvector_init(isns_bitvector_t *bv) +{ + memset(bv, 0, sizeof(*bv)); +} + +void +isns_bitvector_destroy(isns_bitvector_t *bv) +{ + isns_free(bv->ib_words); + memset(bv, 0, sizeof(*bv)); +} + +isns_bitvector_t * +isns_bitvector_alloc(void) +{ + return isns_calloc(1, sizeof(isns_bitvector_t)); +} + +void +isns_bitvector_free(isns_bitvector_t *bv) +{ + if (bv) { + isns_free(bv->ib_words); + memset(bv, 0xa5, sizeof(*bv)); + isns_free(bv); + } +} + +/* + * Helper function to locate bit + */ +uint32_t * +__isns_bitvector_find_word(const isns_bitvector_t *bv, unsigned int bit) +{ + uint32_t *wp, *end; + + if (bv->ib_words == NULL) + return NULL; + + wp = bv->ib_words; + end = wp + bv->ib_count; + while (wp < end) { + unsigned int base, rlen; + + base = wp[0]; + rlen = wp[1]; + + isns_assert(!(base % 32)); + if (base <= bit && bit < base + rlen * 32) + return wp + 2 + ((bit - base) / 32); + + wp += 2 + rlen; + isns_assert(wp <= end); + } + + return NULL; +} + +/* + * Insert words in the middle of the array + */ +static inline void +__isns_bitvector_insert_words(isns_bitvector_t *bv, + unsigned int offset, unsigned int count) +{ + bv->ib_words = isns_realloc(bv->ib_words, + (bv->ib_count + count) * sizeof(uint32_t)); + + /* If we insert in the middle, shift out the tail + * to make room for the new range. */ + isns_assert(offset <= bv->ib_count); + if (offset < bv->ib_count) { + memmove(bv->ib_words + offset + count, + bv->ib_words + offset, + (bv->ib_count - offset) * sizeof(uint32_t)); + } + + memset(bv->ib_words + offset, 0, count * sizeof(uint32_t)); + bv->ib_count += count; +} + +/* + * Insert a new range + */ +static inline uint32_t * +__isns_bitvector_insert_range(isns_bitvector_t *bv, + unsigned int offset, unsigned int base) +{ + uint32_t *pos; + + __isns_bitvector_insert_words(bv, offset, 3); + + pos = bv->ib_words + offset; + + *pos++ = base & ~31; + *pos++ = 1; + + return pos; +} + +/* + * Extend an existing range + * @offset marks the beginning of the existing range. + */ +static inline uint32_t * +__isns_bitvector_extend_range(isns_bitvector_t *bv, + unsigned int offset, unsigned int count) +{ + uint32_t *pos, rlen; + + /* Find the end of the range */ + pos = bv->ib_words + offset; + rlen = pos[1]; + + __isns_bitvector_insert_words(bv, offset + 2 + rlen, count); + + pos = bv->ib_words + offset; + pos[1] += count; + + /* Return pointer to the last word of the new range. */ + return pos + 2 + rlen + count - 1; +} + +/* + * Find a suitable range for insertion + */ +static uint32_t * +__isns_bitvector_find_insert_word(isns_bitvector_t *bv, unsigned int bit) +{ + uint32_t *wp, *end; + + if (bv->ib_words == NULL) + return __isns_bitvector_insert_range(bv, 0, bit); + + wp = bv->ib_words; + end = wp + bv->ib_count; + while (wp < end) { + unsigned int base, rlen, distance; + + base = wp[0]; + rlen = wp[1]; + + isns_assert(!(base % 32)); + + if (bit < base) { + return __isns_bitvector_insert_range(bv, + wp - bv->ib_words, bit); + } + + distance = (bit - base) / 32; + if (distance < rlen) { + /* This bit is within range */ + return wp + 2 + distance; + } + + /* Is it efficient to extend this range? + * The break even point is if we have to add + * 3 words to extend the range, because a new + * range would be at least that much. + */ + if (distance + 1 <= rlen + 3) { + return __isns_bitvector_extend_range(bv, + wp - bv->ib_words, + distance + 1 - rlen); + } + + wp += 2 + rlen; + isns_assert(wp <= end); + } + + /* No suitable range found. Append one at the end */ + return __isns_bitvector_insert_range(bv, + bv->ib_count, bit); +} + +/* + * After clearing a bit, check if the bitvector can be + * compacted. + */ +static void +__isns_bitvector_compact(isns_bitvector_t *bv) +{ + uint32_t *src, *dst, *end; + unsigned int dst_base = 0, dst_len = 0; + + if (bv->ib_words == NULL) + return; + + src = dst = bv->ib_words; + end = src + bv->ib_count; + while (src < end) { + unsigned int base, rlen; + + base = *src++; + rlen = *src++; + + /* Consume leading NUL words */ + while (rlen && *src == 0) { + base += 32; + src++; + rlen--; + } + + /* Consume trailing NUL words */ + while (rlen && src[rlen-1] == 0) + rlen--; + + if (rlen != 0) { + if (dst_len && dst_base + 32 * dst_len == base) { + /* We can extend the previous run */ + } else { + /* New run. Close off the previous one, + * if we had one. */ + if (dst_len != 0) { + dst[0] = dst_base; + dst[1] = dst_len; + dst += 2 + dst_len; + } + + dst_base = base; + dst_len = 0; + } + + while (rlen--) + dst[2 + dst_len++] = *src++; + } + + isns_assert(src <= end); + } + + + if (dst_len != 0) { + dst[0] = dst_base; + dst[1] = dst_len; + dst += 2 + dst_len; + } + + bv->ib_count = dst - bv->ib_words; + if (bv->ib_count == 0) + isns_bitvector_destroy(bv); +} + +/* + * Test the value of a single bit + */ +int +isns_bitvector_test_bit(const isns_bitvector_t *bv, unsigned int bit) +{ + const uint32_t *pos; + uint32_t mask; + + pos = __isns_bitvector_find_word(bv, bit); + if (pos == NULL) + return 0; + + mask = 1 << (bit % 32); + return !!(*pos & mask); +} + +int +isns_bitvector_clear_bit(isns_bitvector_t *bv, unsigned int bit) +{ + uint32_t *pos, oldval, mask; + + pos = __isns_bitvector_find_word(bv, bit); + if (pos == NULL) + return 0; + + mask = 1 << (bit % 32); + oldval = *pos; + *pos &= ~mask; + + __isns_bitvector_compact(bv); + return !!(oldval & mask); +} + +int +isns_bitvector_set_bit(isns_bitvector_t *bv, unsigned int bit) +{ + uint32_t *pos, oldval = 0, mask; + + mask = 1 << (bit % 32); + + pos = __isns_bitvector_find_insert_word(bv, bit); + if (pos != NULL) { + oldval = *pos; + *pos |= mask; + + return !!(oldval & mask); + } + + return 0; +} + +int +isns_bitvector_is_empty(const isns_bitvector_t *bv) +{ + uint32_t *wp, *end; + + if (bv == NULL || bv->ib_count == 0) + return 1; + + /* In theory, we should never have a non-compacted + * empty bitvector, as the only way to get one + * is through clear_bit. + * Better safe than sorry... + */ + + wp = bv->ib_words; + end = wp + bv->ib_count; + while (wp < end) { + unsigned int base, rlen; + + base = *wp++; + rlen = *wp++; + + while (rlen--) { + if (*wp++) + return 0; + } + isns_assert(wp <= end); + } + + return 1; +} + +int +isns_bitvector_intersect(const isns_bitvector_t *a, + const isns_bitvector_t *b, + isns_bitvector_t *result) +{ + const uint32_t *runa, *runb, *enda, *endb; + const uint32_t *wpa = NULL, *wpb = NULL; + uint32_t bita = 0, lena = 0, bitb = 0, lenb = 0; + int found = -1; + + if (a == NULL || b == NULL) + return -1; + + /* Returning the intersect is not implemented yet. */ + isns_assert(result == NULL); + + runa = a->ib_words; + enda = runa + a->ib_count; + runb = b->ib_words; + endb = runb + b->ib_count; + + while (1) { + unsigned int skip; + + if (lena == 0) { +next_a: + if (runa >= enda) + break; + bita = *runa++; + lena = *runa++; + wpa = runa; + runa += lena; + lena *= 32; + } + + if (lenb == 0) { +next_b: + if (runb >= endb) + break; + bitb = *runb++; + lenb = *runb++; + wpb = runb; + runb += lenb; + lenb *= 32; + } + + if (bita < bitb) { + skip = bitb - bita; + + /* range A ends before range B starts. + * Proceed to next run in vector A. */ + if (skip >= lena) + goto next_a; + + bita += skip; + lena -= skip; + wpa += skip / 32; + } else + if (bitb < bita) { + skip = bita - bitb; + + /* range B ends before range A starts. + * Proceed to next run in vector B. */ + if (skip >= lenb) + goto next_b; + + bitb += skip; + lenb -= skip; + wpb += skip / 32; + } + + isns_assert(bita == bitb); + + while (lena && lenb) { + uint32_t intersect; + + intersect = *wpa & *wpb; + + if (!intersect) + goto next_word; + + /* Find the bit */ + if (found < 0) { + uint32_t mask = intersect; + + found = bita; + while (!(mask & 1)) { + found++; + mask >>= 1; + } + } + + if (result == NULL) + return found; + + /* Append to result vector */ + /* FIXME: TBD */ + +next_word: + bita += 32; lena -= 32; wpa++; + bitb += 32; lenb -= 32; wpb++; + } + } + + return found; +} + +/* + * Iterate over the bit vector + */ +void +isns_bitvector_foreach(const isns_bitvector_t *bv, + int (*cb)(uint32_t, void *), + void *user_data) +{ + uint32_t *wp, *end; + + wp = bv->ib_words; + end = wp + bv->ib_count; + while (wp < end) { + unsigned int base, rlen, bits; + + base = wp[0]; + rlen = wp[1]; + bits = rlen * 32; + wp += 2; + + while (rlen--) { + uint32_t mask, word; + + word = *wp++; + for (mask = 1; mask; mask <<= 1, ++base) { + if (word & mask) + cb(base, user_data); + } + } + isns_assert(wp <= end); + } +} + +void +isns_bitvector_dump(const isns_bitvector_t *bv, isns_print_fn_t *fn) +{ + uint32_t *wp, *end; + + fn("Bit Vector %p (%u words):", bv, bv->ib_count); + + wp = bv->ib_words; + end = wp + bv->ib_count; + while (wp < end) { + unsigned int base, rlen, bits; + + base = wp[0]; + rlen = wp[1]; + bits = rlen * 32; + wp += 2; + + fn(" <%u:", base); + while (rlen--) + fn(" 0x%x", *wp++); + fn(">"); + + isns_assert(wp <= end); + } + + if (bv->ib_count == 0) + fn("<empty>"); + fn("\n"); +} + +static inline void +__isns_bitvector_print_next(uint32_t first, uint32_t last, + isns_print_fn_t *fn) +{ + switch (last - first) { + case 0: + return; + case 1: + fn(", %u", last); + break; + default: + fn("-%u", last); + break; + } +} + +void +isns_bitvector_print(const isns_bitvector_t *bv, + isns_print_fn_t *fn) +{ + uint32_t *wp, *end, first = 0, next = 0; + const char *sepa = ""; + + wp = bv->ib_words; + end = wp + bv->ib_count; + while (wp < end) { + unsigned int base, rlen, bits; + + base = wp[0]; + rlen = wp[1]; + bits = rlen * 32; + wp += 2; + + while (rlen--) { + uint32_t mask, word; + + word = *wp++; + for (mask = 1; mask; mask <<= 1, ++base) { + if (word & mask) { + if (next++) + continue; + fn("%s%u", sepa, base); + sepa = ", "; + first = base; + next = base + 1; + } else { + if (next) + __isns_bitvector_print_next(first, next - 1, fn); + first = next = 0; + } + } + } + isns_assert(wp <= end); + } + + if (next) + __isns_bitvector_print_next(first, next - 1, fn); + + if (*sepa == '\0') + fn("<empty>"); + fn("\n"); +} + +#ifdef TEST +int +main(void) +{ + isns_bitvector_t a, b; + int i; + + isns_bitvector_init(&a); + isns_bitvector_set_bit(&a, 0); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_set_bit(&a, 1); + isns_bitvector_set_bit(&a, 16); + isns_bitvector_set_bit(&a, 32); + isns_bitvector_set_bit(&a, 64); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_set_bit(&a, 8192); + isns_bitvector_set_bit(&a, 8196); + isns_bitvector_set_bit(&a, 8194); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_set_bit(&a, 2052); + isns_bitvector_set_bit(&a, 2049); + isns_bitvector_set_bit(&a, 2051); + isns_bitvector_set_bit(&a, 2050); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_print(&a, isns_print_stdout); + isns_bitvector_destroy(&a); + + isns_bitvector_init(&a); + for (i = 127; i >= 0; --i) + isns_bitvector_set_bit(&a, i); + isns_bitvector_dump(&a, isns_print_stdout); + printf("[Compacting]\n"); + __isns_bitvector_compact(&a); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_print(&a, isns_print_stdout); + isns_bitvector_destroy(&a); + + isns_bitvector_init(&a); + for (i = 0; i < 128; ++i) + isns_bitvector_set_bit(&a, i); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_print(&a, isns_print_stdout); + isns_bitvector_destroy(&a); + + isns_bitvector_init(&a); + isns_bitvector_init(&b); + isns_bitvector_set_bit(&a, 0); + isns_bitvector_set_bit(&a, 77); + isns_bitvector_set_bit(&a, 249); + isns_bitvector_set_bit(&a, 102); + + isns_bitvector_set_bit(&b, 1); + isns_bitvector_set_bit(&b, 76); + isns_bitvector_set_bit(&b, 250); + isns_bitvector_set_bit(&b, 102); + i = isns_bitvector_intersect(&a, &b, NULL); + if (i != 102) + fprintf(stderr, "*** BAD: Intersect should return 102 (got %d)! ***\n", i); + else + printf("Intersect okay: %d\n", i); + isns_bitvector_destroy(&a); + isns_bitvector_destroy(&b); + + isns_bitvector_init(&a); + isns_bitvector_set_bit(&a, 0); + isns_bitvector_set_bit(&a, 1); + isns_bitvector_clear_bit(&a, 1); + isns_bitvector_clear_bit(&a, 0); + isns_bitvector_dump(&a, isns_print_stdout); + isns_bitvector_print(&a, isns_print_stdout); + isns_bitvector_destroy(&a); + return 0; +} +#endif diff --git a/utils/open-isns/buffer.c b/utils/open-isns/buffer.c new file mode 100644 index 0000000..279ab76 --- /dev/null +++ b/utils/open-isns/buffer.c @@ -0,0 +1,407 @@ +/* + * Buffer handling functions + * + * Copyright (C) 2003-2007, Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> +#include <netinet/in.h> /* ntohl&htonl */ +#include "buffer.h" +#include "util.h" /* htonll */ + +static int buf_drain(buf_t *bp); + +buf_t * +buf_alloc(size_t size) +{ + buf_t *bp; + + bp = isns_calloc(1, sizeof(*bp)); + buf_init_empty(bp, size); + + return bp; +} + +buf_t * +buf_open(const char *filename, int flags) +{ + static const unsigned int buflen = 4096; + buf_t *bp; + int oerr; + + if (!(bp = isns_calloc(1, sizeof(*bp) + buflen))) + return NULL; + buf_init(bp, (bp + 1), buflen); + + switch (flags & O_ACCMODE) { + case O_RDONLY: + bp->write_mode = 0; + break; + + case O_WRONLY: + bp->write_mode = 1; + break; + + default: + errno = EINVAL; + goto failed; + } + + if (!filename || !strcmp(filename, "-")) { + bp->fd = dup(bp->write_mode? 1 : 0); + } else { + bp->fd = open(filename, flags, 0666); + } + + if (bp->fd < 0) + goto failed; + + return bp; + +failed: oerr = errno; + isns_free(bp); + errno = oerr; + return NULL; +} + +buf_t * +buf_dup(const buf_t *src) +{ + buf_t *bp; + + bp = buf_alloc(src->max_size); + buf_put(bp, src->base + src->head, src->tail - src->head); + + bp->addr = src->addr; + bp->addrlen = src->addrlen; + return bp; +} + +void +buf_close(buf_t *bp) +{ + if (bp->write_mode) + buf_drain(bp); + if (bp->fd >= 0) + close(bp->fd); + bp->fd = -1; + isns_free(bp); +} + +void +buf_free(buf_t *bp) +{ + if (!bp) + return; + if (bp->allocated) + isns_free(bp->base); + isns_free(bp); +} + +void +buf_list_free(buf_t *bp) +{ + buf_t *next; + + while (bp) { + next = bp->next; + buf_free(bp); + bp = next; + } +} + +void +buf_init(buf_t *bp, void *mem, size_t len) +{ + memset(bp, 0, sizeof(*bp)); + bp->base = (unsigned char *) mem; + bp->size = len; + bp->max_size = len; + bp->fd = -1; +} + +void +buf_init_empty(buf_t *bp, size_t len) +{ + memset(bp, 0, sizeof(*bp)); + bp->max_size = len; + bp->fd = -1; +} + +void +buf_set(buf_t *bp, void *mem, size_t len) +{ + buf_init(bp, mem, len); + bp->tail = len; +} + +void +buf_clear(buf_t *bp) +{ + bp->head = bp->tail = 0; +} + +int +buf_fill(buf_t *bp) +{ + int n; + + if (bp->head || bp->tail) + buf_compact(bp); + + if (bp->write_mode || bp->fd < 0) + return 0; + + n = read(bp->fd, bp->base + bp->tail, buf_tailroom(bp)); + if (n < 0) { + warn("read error"); + return 0; + } + + bp->tail += n; + return n; +} + +int +buf_drain(buf_t *bp) +{ + int n; + + if (!bp->write_mode || bp->fd < 0) + return 0; + + n = write(bp->fd, bp->base + bp->head, buf_avail(bp)); + if (n < 0) { + warn("write error"); + return 0; + } + + bp->head += n; + return n; +} + +int +__buf_resize(buf_t *bp, size_t new_size) +{ + void *new_base; + + if (new_size > bp->max_size) + return 0; + isns_assert(bp->allocated || bp->base == NULL); + + new_size = (new_size + 127) & ~127; + if (new_size > bp->max_size) + new_size = bp->max_size; + + new_base = isns_realloc(bp->base, new_size); + if (new_base == NULL) + return 0; + + bp->base = new_base; + bp->size = new_size; + bp->allocated = 1; + return new_size; +} + +buf_t * +buf_split(buf_t **to_split, size_t size) +{ + buf_t *old = *to_split, *new; + size_t avail; + + avail = buf_avail(old); + if (size > avail) + return NULL; + + if (size == avail) { + *to_split = NULL; + return old; + } + + new = buf_alloc(size); + buf_put(new, buf_head(old), size); + buf_pull(old, size); + + return new; +} + +int +buf_seek(buf_t *bp, off_t offset) +{ + if (bp->write_mode && !buf_drain(bp)) + return 0; + if (lseek(bp->fd, offset, SEEK_SET) < 0) { + warn("cannot seek to offset %ld", (long) offset); + return 0; + } + return 1; +} + +int +buf_get(buf_t *bp, void *mem, size_t len) +{ + caddr_t dst = (caddr_t) mem; + unsigned int total = len, copy; + + while (len) { + if ((copy = buf_avail(bp)) > len) + copy = len; + if (copy == 0) { + if (!buf_fill(bp)) + return 0; + continue; + } + if (dst) { + memcpy(dst, bp->base + bp->head, copy); + dst += copy; + } + bp->head += copy; + len -= copy; + } + return total; +} + +int +buf_get32(buf_t *bp, uint32_t *vp) +{ + if (!buf_get(bp, vp, 4)) + return 0; + *vp = ntohl(*vp); + return 1; +} + +int +buf_get64(buf_t *bp, uint64_t *vp) +{ + if (!buf_get(bp, vp, 8)) + return 0; + *vp = ntohll(*vp); + return 1; +} + +int +buf_gets(buf_t *bp, char *stringbuf, size_t size) +{ + uint32_t len, copy; + + if (size == 0) + return 0; + + if (!buf_get32(bp, &len)) + return 0; + + if ((copy = len) >= size) + copy = size - 1; + + if (!buf_get(bp, stringbuf, copy)) + return 0; + stringbuf[copy] = '\0'; + + /* Pull remaining bytes */ + if (copy != len && !buf_pull(bp, len - copy)) + return 0; + + return copy + 1; +} + +int +buf_put(buf_t *bp, const void *mem, size_t len) +{ + caddr_t src = (caddr_t) mem; + unsigned int total = len, copy; + + while (len) { + if ((copy = bp->size - bp->tail) > len) + copy = len; + if (copy == 0) { + if (buf_drain(bp)) { + buf_compact(bp); + continue; + } + if (__buf_resize(bp, bp->tail + len)) { + buf_compact(bp); + continue; + } + return 0; + } + if (src) { + memcpy(bp->base + bp->tail, src, copy); + src += copy; + } + bp->tail += copy; + len -= copy; + } + return total; +} + +int +buf_putc(buf_t *bp, int byte) +{ + unsigned char c = byte; + + return buf_put(bp, &c, 1); +} + +int +buf_put32(buf_t *bp, uint32_t val) +{ + val = htonl(val); + if (!buf_put(bp, &val, 4)) + return 0; + return 1; +} + +int +buf_put64(buf_t *bp, uint64_t val) +{ + val = htonll(val); + return buf_put(bp, &val, 8); +} + +int +buf_puts(buf_t *bp, const char *sp) +{ + uint32_t len = 0; + + if (sp) + len = strlen(sp); + return buf_put32(bp, len) && buf_put(bp, sp, len); +} + +void +buf_compact(buf_t *bp) +{ + unsigned int count; + + if (bp->head == 0) + return; + + count = bp->tail - bp->head; + memmove(bp->base, bp->base + bp->head, count); + bp->tail -= bp->head; + bp->head = 0; +} + +void +buf_list_append(buf_t **list, buf_t *bp) +{ + bp->next = NULL; + while (*list) + list = &(*list)->next; + *list = bp; +} + +int +buf_truncate(buf_t *bp, size_t len) +{ + if (bp->head + len > bp->tail) + return 0; + + bp->tail = bp->head + len; + return 1; +} diff --git a/utils/open-isns/buffer.h b/utils/open-isns/buffer.h new file mode 100644 index 0000000..75ba910 --- /dev/null +++ b/utils/open-isns/buffer.h @@ -0,0 +1,141 @@ +/* + * Buffer handling functions + * + * Copyright (C) 2003-2006, Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef BUFFER_H +#define BUFFER_H + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdint.h> + +typedef struct isns_buf { + struct isns_buf * next; + unsigned char * base; + unsigned int head, tail, size, max_size; + unsigned int write_mode : 1, + allocated : 1; + int fd; + + /* Anonymous union for misc stuff */ + union { + struct { + struct sockaddr_storage addr; + socklen_t addrlen; + }; + }; +} buf_t; + +extern buf_t * buf_open(const char *, int); +extern buf_t * buf_alloc(size_t); +extern buf_t * buf_dup(const buf_t *); +extern void buf_init(buf_t *, void *, size_t); +extern void buf_init_empty(buf_t *, size_t); +extern void buf_set(buf_t *, void *, size_t); + +extern void buf_clear(buf_t *); +extern void buf_close(buf_t *); +extern void buf_destroy(buf_t *); +extern void buf_free(buf_t *); +extern void buf_list_free(buf_t *); + +extern int buf_get(buf_t *, void *, size_t); +extern int buf_get32(buf_t *, uint32_t *); +extern int buf_get64(buf_t *, uint64_t *); +extern int buf_gets(buf_t *, char *, size_t); +extern int buf_put(buf_t *, const void *, size_t); +extern int buf_put32(buf_t *, uint32_t); +extern int buf_put64(buf_t *, uint64_t); +extern int buf_puts(buf_t *, const char *); +extern int buf_putc(buf_t *, int); +extern int buf_read(buf_t *, int); +extern int buf_seek(buf_t *bp, off_t offset); +extern int buf_truncate(buf_t *, size_t); +extern void buf_compact(buf_t *); +extern buf_t * buf_split(buf_t **to_split, size_t len); +extern int __buf_resize(buf_t *, size_t); + +extern void buf_list_append(buf_t **, buf_t *); + +static inline size_t +buf_avail(const buf_t *bp) +{ + return bp->tail - bp->head; +} + +static inline size_t +buf_tailroom(const buf_t *bp) +{ + return bp->max_size - bp->tail; +} + +static inline size_t +buf_size(const buf_t *bp) +{ + return bp->size; +} + +static inline void * +buf_head(const buf_t *bp) +{ + return bp->base + bp->head; +} + +static inline void * +buf_tail(const buf_t *bp) +{ + return bp->base + bp->tail; +} + +static inline int +buf_reserve(buf_t *bp, size_t len) +{ + if (bp->head != bp->tail) + return 0; + if (bp->max_size - bp->head < len) + return 0; + bp->head += len; + bp->tail += len; + return 1; +} + +static inline int +buf_pull(buf_t *bp, size_t len) +{ + if (len > buf_avail(bp)) + return 0; + bp->head += len; + return 1; +} + +static inline void * +buf_push(buf_t *bp, size_t len) +{ + if (bp->max_size - bp->tail < len) + return NULL; + + if (bp->tail + len > bp->size + && !__buf_resize(bp, bp->tail + len)) + return NULL; + + bp->tail += len; + return bp->base + bp->tail - len; +} + +static inline void * +buf_push_head(buf_t *bp, size_t len) +{ + if (bp->head < len) + return NULL; + + if (bp->tail > bp->size + && !__buf_resize(bp, bp->tail)) + return NULL; + + bp->head -= len; + return bp->base + bp->head; +} + +#endif /* BUFFER_H */ diff --git a/utils/open-isns/callback.c b/utils/open-isns/callback.c new file mode 100644 index 0000000..ecdabd7 --- /dev/null +++ b/utils/open-isns/callback.c @@ -0,0 +1,148 @@ +/* + * iSNS object callbacks for SCN and other stuff + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "objects.h" +#include "vendor.h" +#include "attrs.h" +#include "util.h" + +typedef struct isns_object_notifier isns_object_notifier_t; +struct isns_object_notifier { + isns_list_t list; + isns_db_callback_t * func; + void * data; +}; + +typedef struct isns_cb_event isns_cb_event_t; +struct isns_cb_event { + isns_list_t list; + isns_db_event_t info; +}; + +static ISNS_LIST_DECLARE(notifiers); +static ISNS_LIST_DECLARE(events); + +static inline void +__isns_db_event(isns_object_t *dst, + isns_object_t *obj, + unsigned int bits, + isns_object_t *trigger) +{ + isns_cb_event_t *ev; + + ev = isns_calloc(1, sizeof(*ev)); + ev->info.ie_recipient = isns_object_get(dst); + ev->info.ie_object = isns_object_get(obj); + ev->info.ie_bits = bits; + ev->info.ie_trigger = isns_object_get(trigger); + isns_list_append(&events, &ev->list); +} + +void +isns_object_event(isns_object_t *obj, + unsigned int bits, + isns_object_t *trigger) +{ + __isns_db_event(NULL, obj, bits, trigger); +} + +void +isns_unicast_event(isns_object_t *dst, + isns_object_t *obj, + unsigned int bits, + isns_object_t *trigger) +{ + __isns_db_event(dst, obj, bits, trigger); +} + +/* + * Given an object pair and an event bitmask, + * invoke all callbacks + */ +static inline void +isns_call_callbacks(isns_db_event_t *ev) +{ + isns_object_t *obj = ev->ie_object; + isns_list_t *pos, *next; + + ev->ie_bits |= obj->ie_scn_bits; + if (ev->ie_bits == 0) + return; + isns_list_foreach(¬ifiers, pos, next) { + isns_object_notifier_t *not; + + not = isns_list_item(isns_object_notifier_t, list, pos); + not->func(ev, not->data); + } + obj->ie_scn_bits = 0; +} + +void +isns_flush_events(void) +{ + while (!isns_list_empty(&events)) { + isns_cb_event_t *ev = isns_list_item(isns_cb_event_t, list, events.next); + + isns_call_callbacks(&ev->info); + isns_object_release(ev->info.ie_recipient); + isns_object_release(ev->info.ie_object); + isns_object_release(ev->info.ie_trigger); + isns_list_del(&ev->list); + isns_free(ev); + } +} + +void +isns_register_callback(isns_db_callback_t *func, + void *user_data) +{ + isns_object_notifier_t *not; + + not = isns_calloc(1, sizeof(*not)); + not->func = func; + not->data = user_data; + + isns_list_append(¬ifiers, ¬->list); +} + +const char * +isns_event_string(unsigned int bits) +{ + static const char *names[16] = { + [ISNS_SCN_DD_MEMBER_ADDED] = "member added", + [ISNS_SCN_DD_MEMBER_REMOVED] = "member removed", + [ISNS_SCN_OBJECT_UPDATED] = "updated", + [ISNS_SCN_OBJECT_ADDED] = "added", + [ISNS_SCN_OBJECT_REMOVED] = "removed", + [ISNS_SCN_MANAGEMENT_REGISTRATION]= "mgmt registration", + [ISNS_SCN_TARGET_AND_SELF_ONLY] = "target+self", + [ISNS_SCN_INITIATOR_AND_SELF_ONLY]= "initiator+self", + }; + static char buffer[128]; + unsigned int pos = 0, i; + + + for (i = 0; i < 16; ++i, bits >>= 1) { + if (!(bits & 1)) + continue; + + if (names[i]) { + snprintf(buffer + pos, sizeof(buffer) - pos, + "%s%s", pos? ", " : "", names[i]); + } else { + snprintf(buffer + pos, sizeof(buffer) - pos, + "%sevent %u", pos? ", " : "", i); + } + pos = strlen(buffer); + } + if (pos == 0) + return "<no event>"; + + return buffer; +} diff --git a/utils/open-isns/client.c b/utils/open-isns/client.c new file mode 100644 index 0000000..44e07b8 --- /dev/null +++ b/utils/open-isns/client.c @@ -0,0 +1,203 @@ +/* + * Client functions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#include <isns.h> +#include "security.h" +#include "util.h" +#include "internal.h" +#include "config.h" + +static isns_client_t * +__isns_create_default_client(isns_socket_t *sock, isns_security_t *ctx, + const char *source_name) +{ + isns_client_t *clnt; + + clnt = isns_calloc(1, sizeof(*clnt)); + + if (!source_name) + source_name = isns_config.ic_source_name; + + clnt->ic_source = isns_source_create_iscsi(source_name); + clnt->ic_socket = sock; + + isns_socket_set_security_ctx(clnt->ic_socket, ctx); + + return clnt; +} + +isns_client_t * +isns_create_client(isns_security_t *ctx, const char *source_name) +{ + isns_socket_t *sock; + const char *server_name; + + server_name = isns_config.ic_server_name; + if (!strcasecmp(server_name, "SLP:") + && !(server_name = isns_slp_find())) { + isns_error("Unable to locate iSNS server through SLP\n"); + return NULL; + } + + sock = isns_create_bound_client_socket( + isns_config.ic_bind_address, + server_name, + "isns", 0, SOCK_STREAM); + if (sock == NULL) { + isns_error("Unable to create socket for host \"%s\"\n", + isns_config.ic_server_name); + return NULL; + } + + return __isns_create_default_client(sock, + ctx? : isns_default_security_context(0), + source_name); +} + +isns_client_t * +isns_create_default_client(isns_security_t *ctx) +{ + return isns_create_client(ctx, isns_config.ic_source_name); +} + +isns_client_t * +isns_create_local_client(isns_security_t *ctx, const char *source_name) +{ + isns_socket_t *sock; + + if (isns_config.ic_control_socket == NULL) + isns_fatal("Cannot use local mode: no local control socket\n"); + + sock = isns_create_client_socket(isns_config.ic_control_socket, + NULL, 0, SOCK_STREAM); + if (sock == NULL) { + isns_error("Unable to create control socket (%s)\n", + isns_config.ic_control_socket); + return NULL; + } + + return __isns_create_default_client(sock, ctx, source_name); +} + +int +isns_client_call(isns_client_t *clnt, + isns_simple_t **inout) +{ + return isns_simple_call(clnt->ic_socket, inout); +} + +void +isns_client_destroy(isns_client_t *clnt) +{ + if (clnt->ic_socket) + isns_socket_free(clnt->ic_socket); + if (clnt->ic_source) + isns_source_release(clnt->ic_source); + isns_free(clnt); +} + +/* + * Get the local address + */ +int +isns_client_get_local_address(const isns_client_t *clnt, + isns_portal_info_t *portal_info) +{ + return isns_socket_get_portal_info(clnt->ic_socket, portal_info); +} + +/* + * Create a security context + */ +static isns_security_t * +__create_security_context(const char *name, const char *auth_key, + const char *server_key) +{ + isns_security_t *ctx; + isns_principal_t *princ; + + if (!isns_config.ic_security) + return NULL; + +#ifndef WITH_SECURITY + isns_error("Cannot create security context: security disabled at build time\n"); + return NULL; +#else /* WITH_SECURITY */ + ctx = isns_create_dsa_context(); + if (ctx == NULL) + isns_fatal("Unable to create security context\n"); + + /* Load my own key */ + princ = isns_security_load_privkey(ctx, auth_key); + if (!princ) + isns_fatal("Unable to load private key from %s\n", + auth_key); + + isns_principal_set_name(princ, name); + isns_security_set_identity(ctx, princ); + + if (server_key) { + /* We're a client, and we want to load the + * server's public key in order to authenticate + * the server's responses. + */ + princ = isns_security_load_pubkey(ctx, server_key); + if (!princ) + isns_fatal("Unable to load public key from %s\n", + server_key); + + /* Do *not* set a name for this principal - + * this will be the default principal used when + * verifying the server's reply, which is a good thing + * because we don't know what SPI the server will + * be using. */ + isns_add_principal(ctx, princ); + + /* But set a policy for the server which allows it + to send ESI and SCN messages */ + isns_principal_set_policy(princ, isns_policy_server()); + } + + return ctx; +#endif /* WITH_SECURITY */ +} + +/* + * Create the default security context + */ +isns_security_t * +isns_default_security_context(int server_only) +{ + static isns_security_t *ctx; + + if (ctx == NULL) + ctx = __create_security_context(isns_config.ic_auth_name, + isns_config.ic_auth_key_file, + server_only? NULL : isns_config.ic_server_key_file); + return ctx; +} + +/* + * Create the control security context + */ +isns_security_t * +isns_control_security_context(int server_only) +{ + static isns_security_t *ctx; + + if (ctx == NULL) + ctx = __create_security_context(isns_config.ic_control_name, + isns_config.ic_control_key_file, + server_only? NULL : isns_config.ic_server_key_file); + return ctx; +} diff --git a/utils/open-isns/compat/my_getopt.c b/utils/open-isns/compat/my_getopt.c new file mode 100644 index 0000000..512b0ae --- /dev/null +++ b/utils/open-isns/compat/my_getopt.c @@ -0,0 +1,271 @@ +/* + * my_getopt.c - my re-implementation of getopt. + * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "my_getopt.h" + +int my_optind=1, my_opterr=1, my_optopt=0; +char *my_optarg=0; + +/* this is the plain old UNIX getopt, with GNU-style extensions. */ +/* if you're porting some piece of UNIX software, this is all you need. */ +/* this supports GNU-style permution and optional arguments */ + +int my_getopt(int argc, char * argv[], const char *opts) +{ + static int charind=0; + const char *s; + char mode, colon_mode; + int off = 0, opt = -1; + + if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; + else { + if((colon_mode = *opts) == ':') off ++; + if(((mode = opts[off]) == '+') || (mode == '-')) { + off++; + if((colon_mode != ':') && ((colon_mode = opts[off]) == ':')) + off ++; + } + } + my_optarg = 0; + if(charind) { + my_optopt = argv[my_optind][charind]; + for(s=opts+off; *s; s++) if(my_optopt == *s) { + charind++; + if((*(++s) == ':') || ((my_optopt == 'W') && (*s == ';'))) { + if(argv[my_optind][charind]) { + my_optarg = &(argv[my_optind++][charind]); + charind = 0; + } else if(*(++s) != ':') { + charind = 0; + if(++my_optind >= argc) { + if(my_opterr) fprintf(stderr, + "%s: option requires an argument -- %c\n", + argv[0], my_optopt); + opt = (colon_mode == ':') ? ':' : '?'; + goto my_getopt_ok; + } + my_optarg = argv[my_optind++]; + } + } + opt = my_optopt; + goto my_getopt_ok; + } + if(my_opterr) fprintf(stderr, + "%s: illegal option -- %c\n", + argv[0], my_optopt); + opt = '?'; + if(argv[my_optind][++charind] == '\0') { + my_optind++; + charind = 0; + } + my_getopt_ok: + if(charind && ! argv[my_optind][charind]) { + my_optind++; + charind = 0; + } + } else if((my_optind >= argc) || + ((argv[my_optind][0] == '-') && + (argv[my_optind][1] == '-') && + (argv[my_optind][2] == '\0'))) { + my_optind++; + opt = -1; + } else if((argv[my_optind][0] != '-') || + (argv[my_optind][1] == '\0')) { + char *tmp; + int i, j, k; + + if(mode == '+') opt = -1; + else if(mode == '-') { + my_optarg = argv[my_optind++]; + charind = 0; + opt = 1; + } else { + for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') && + (argv[i][1] != '\0')) { + my_optind=i; + opt=my_getopt(argc, argv, opts); + while(i > j) { + tmp=argv[--i]; + for(k=i; k+1<my_optind; k++) argv[k]=argv[k+1]; + argv[--my_optind]=tmp; + } + break; + } + if(i == argc) opt = -1; + } + } else { + charind++; + opt = my_getopt(argc, argv, opts); + } + if (my_optind > argc) my_optind = argc; + return opt; +} + +/* this is the extended getopt_long{,_only}, with some GNU-like + * extensions. Implements _getopt_internal in case any programs + * expecting GNU libc getopt call it. + */ + +int _my_getopt_internal(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind, + int long_only) +{ + char mode, colon_mode = *shortopts; + int shortoff = 0, opt = -1; + + if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; + else { + if((colon_mode = *shortopts) == ':') shortoff ++; + if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) { + shortoff++; + if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':')) + shortoff ++; + } + } + my_optarg = 0; + if((my_optind >= argc) || + ((argv[my_optind][0] == '-') && + (argv[my_optind][1] == '-') && + (argv[my_optind][2] == '\0'))) { + my_optind++; + opt = -1; + } else if((argv[my_optind][0] != '-') || + (argv[my_optind][1] == '\0')) { + char *tmp; + int i, j, k; + + opt = -1; + if(mode == '+') return -1; + else if(mode == '-') { + my_optarg = argv[my_optind++]; + return 1; + } + for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') && + (argv[i][1] != '\0')) { + my_optind=i; + opt=_my_getopt_internal(argc, argv, shortopts, + longopts, longind, + long_only); + while(i > j) { + tmp=argv[--i]; + for(k=i; k+1<my_optind; k++) + argv[k]=argv[k+1]; + argv[--my_optind]=tmp; + } + break; + } + } else if((!long_only) && (argv[my_optind][1] != '-')) + opt = my_getopt(argc, argv, shortopts); + else { + int charind, offset; + int found = 0, ind, hits = 0; + + if(((my_optopt = argv[my_optind][1]) != '-') && ! argv[my_optind][2]) { + int c; + + ind = shortoff; + while((c = shortopts[ind++])) { + if(((shortopts[ind] == ':') || + ((c == 'W') && (shortopts[ind] == ';'))) && + (shortopts[++ind] == ':')) + ind ++; + if(my_optopt == c) return my_getopt(argc, argv, shortopts); + } + } + offset = 2 - (argv[my_optind][1] != '-'); + for(charind = offset; + (argv[my_optind][charind] != '\0') && + (argv[my_optind][charind] != '='); + charind++); + for(ind = 0; longopts[ind].name && !hits; ind++) + if((strlen(longopts[ind].name) == (size_t) (charind - offset)) && + (strncmp(longopts[ind].name, + argv[my_optind] + offset, charind - offset) == 0)) + found = ind, hits++; + if(!hits) for(ind = 0; longopts[ind].name; ind++) + if(strncmp(longopts[ind].name, + argv[my_optind] + offset, charind - offset) == 0) + found = ind, hits++; + if(hits == 1) { + opt = 0; + + if(argv[my_optind][charind] == '=') { + if(longopts[found].has_arg == 0) { + opt = '?'; + if(my_opterr) fprintf(stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], longopts[found].name); + } else { + my_optarg = argv[my_optind] + ++charind; + charind = 0; + } + } else if(longopts[found].has_arg == 1) { + if(++my_optind >= argc) { + opt = (colon_mode == ':') ? ':' : '?'; + if(my_opterr) fprintf(stderr, + "%s: option `--%s' requires an argument\n", + argv[0], longopts[found].name); + } else my_optarg = argv[my_optind]; + } + if(!opt) { + if (longind) *longind = found; + if(!longopts[found].flag) opt = longopts[found].val; + else *(longopts[found].flag) = longopts[found].val; + } + my_optind++; + } else if(!hits) { + if(offset == 1) opt = my_getopt(argc, argv, shortopts); + else { + opt = '?'; + if(my_opterr) fprintf(stderr, + "%s: unrecognized option `%s'\n", + argv[0], argv[my_optind++]); + } + } else { + opt = '?'; + if(my_opterr) fprintf(stderr, + "%s: option `%s' is ambiguous\n", + argv[0], argv[my_optind++]); + } + } + if (my_optind > argc) my_optind = argc; + return opt; +} + +int my_getopt_long(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind) +{ + return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 0); +} + +int my_getopt_long_only(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind) +{ + return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 1); +} diff --git a/utils/open-isns/compat/my_getopt.h b/utils/open-isns/compat/my_getopt.h new file mode 100644 index 0000000..34fcfe7 --- /dev/null +++ b/utils/open-isns/compat/my_getopt.h @@ -0,0 +1,69 @@ +/* + * my_getopt.h - interface to my re-implementation of getopt. + * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 MY_GETOPT_H_INCLUDED +#define MY_GETOPT_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/* UNIX-style short-argument parser */ +extern int my_getopt(int argc, char * argv[], const char *opts); + +extern int my_optind, my_opterr, my_optopt; +extern char *my_optarg; + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +/* human-readable values for has_arg */ +#undef no_argument +#define no_argument 0 +#undef required_argument +#define required_argument 1 +#undef optional_argument +#define optional_argument 2 + +/* GNU-style long-argument parsers */ +extern int my_getopt_long(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind); + +extern int my_getopt_long_only(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind); + +extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind, + int long_only); + +#ifdef __cplusplus +} +#endif + +#endif /* MY_GETOPT_H_INCLUDED */ diff --git a/utils/open-isns/config.c b/utils/open-isns/config.c new file mode 100644 index 0000000..cc470a4 --- /dev/null +++ b/utils/open-isns/config.c @@ -0,0 +1,278 @@ +/* + * Config file reader + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "isns.h" +#include "util.h" +#include "paths.h" + +/* + * iSNS configuration + */ +struct isns_config isns_config = { + /* Security parameters */ + .ic_security = -1, + .ic_auth_key_file = ISNS_ETCDIR "/auth_key", + .ic_server_key_file = ISNS_ETCDIR "/server_key.pub", + .ic_client_keystore = "DB:", + .ic_control_socket = ISNS_RUNDIR "/isnsctl", + .ic_pidfile = ISNS_RUNDIR "/isnsd.pid", + .ic_local_registry_file = ISNS_DEFAULT_LOCAL_REGISTRY, + + .ic_control_name = "isns.control", + .ic_control_key_file = ISNS_ETCDIR "/control.key", + + .ic_registration_period = 3600, /* 1 hour */ + .ic_scn_timeout = 60, + .ic_scn_retries = 3, + + .ic_esi_max_interval = 600, /* 10 minutes */ + .ic_esi_min_interval = 60, /* 1 minute */ + .ic_esi_retries = 3, + + .ic_auth = { + .replay_window = 300, /* 5 min clock skew */ + .timestamp_jitter = 1, /* 1 sec timestamp jitter */ + .allow_unknown_peers = 1, + }, + .ic_network = { + .max_sockets = 1024, + .connect_timeout = 5, + .reconnect_timeout = 10, + .call_timeout = 60, + .udp_retrans_timeout = 10, + .tcp_retrans_timeout = 60, + .idle_timeout = 300, + }, + .ic_dsa = { + .param_file = ISNS_ETCDIR "/dsa.params", + }, +}; + +/* + * Default string values need to be dup'ed, + * so that later assignment does't try to free + * these strings. + */ +static inline void +__isns_config_defaults(void) +{ + static int defaults_init = 1; + + if (!defaults_init) + return; + +#define DUP(member) \ + if (isns_config.member) \ + isns_config.member = isns_strdup(isns_config.member) + + DUP(ic_source_name); + DUP(ic_database); + DUP(ic_server_name); + DUP(ic_bind_address); + DUP(ic_auth_key_file); + DUP(ic_server_key_file); + DUP(ic_client_keystore); + DUP(ic_control_socket); + DUP(ic_pidfile); + DUP(ic_control_name); + DUP(ic_control_key_file); + DUP(ic_local_registry_file); + DUP(ic_dsa.param_file); + +#undef DUP + + defaults_init = 0; +} + +/* + * Read the iSNS configuration file + */ +int +isns_read_config(const char *filename) +{ + FILE *fp; + char *name, *pos; + + __isns_config_defaults(); + + if ((fp = fopen(filename, "r")) == NULL) { + perror(filename); + return -1; + } + + while ((pos = parser_get_next_line(fp)) != NULL) { + pos[strcspn(pos, "#")] = '\0'; + + if (!(name = parser_get_next_word(&pos))) + continue; + + isns_config_set(name, pos); + } + + fclose(fp); + + /* Massage the config file */ + if (isns_config.ic_security < 0) { + /* By default, we will enable authentication + * whenever we find our private key, and + * the server's public key. */ + if (access(isns_config.ic_auth_key_file, R_OK) == 0 + && access(isns_config.ic_server_key_file, R_OK) == 0) + isns_config.ic_security = 1; + else + isns_config.ic_security = 0; + } + + isns_init_names(); + + return 0; +} + +int +isns_config_set(const char *name, char *pos) +{ + char *value; + + value = parser_get_rest_of_line(&pos); + if (value) + while (isspace(*value) || *value == '=') + ++value; + if (!strcasecmp(name, "HostName")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_host_name, value); + } else if (!strcasecmp(name, "SourceName")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_source_name, value); + } else if (!strcasecmp(name, "AuthName")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_auth_name, value); + } else if (!strcasecmp(name, "Database")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_database, value); + } else if (!strcasecmp(name, "ServerAddress")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_server_name, value); + } else if (!strcasecmp(name, "BindAddress")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_bind_address, value); + } else if (!strcasecmp(name, "ControlSocket")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_control_socket, value); + } else if (!strcasecmp(name, "PIDFile")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_pidfile, value); + } else if (!strcasecmp(name, "LocalRegistry")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_local_registry_file, value); + } else if (!strcasecmp(name, "RegistrationPeriod")) { + if (!value) + goto no_value; + isns_config.ic_registration_period = parse_timeout(value); + } else if (!strcasecmp(name, "SCNTimeout")) { + if (!value) + goto no_value; + isns_config.ic_scn_timeout = parse_timeout(value); + } else if (!strcasecmp(name, "SCNRetries")) { + if (!value) + goto no_value; + isns_config.ic_scn_retries = parse_int(value); + } else if (!strcasecmp(name, "SCNCallout")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_scn_callout, value); + } else if (!strcasecmp(name, "ESIMinInterval")) { + if (!value) + goto no_value; + isns_config.ic_esi_min_interval = parse_timeout(value); + } else if (!strcasecmp(name, "ESIMaxInterval")) { + if (!value) + goto no_value; + isns_config.ic_esi_max_interval = parse_timeout(value); + } else if (!strcasecmp(name, "ESIRetries")) { + if (!value) + goto no_value; + isns_config.ic_esi_retries = parse_int(value); + } else if (!strcasecmp(name, "DefaultDiscoveryDomain")) { + if (!value) + goto no_value; + isns_config.ic_use_default_domain = parse_int(value); + } else if (!strcasecmp(name, "SLPRegister")) { + if (!value) + goto no_value; + isns_config.ic_slp_register = parse_int(value); + } else if (!strcasecmp(name, "Security")) { + if (!value) + goto no_value; + isns_config.ic_security = parse_int(value); + } else if (!strcasecmp(name, "AuthKeyFile")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_auth_key_file, value); + } else if (!strcasecmp(name, "ServerKeyFile")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_server_key_file, value); + } else if (!strcasecmp(name, "ClientKeyStore") + || !strcasecmp(name, "KeyStore")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_client_keystore, value); + } else if (!strcasecmp(name, "Control.SourceName")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_control_name, value); + } else if (!strcasecmp(name, "Control.AuthKeyFile")) { + if (!value) + goto no_value; + isns_assign_string(&isns_config.ic_control_key_file, value); + } else if (!strcasecmp(name, "Auth.ReplayWindow")) { + if (!value) + goto no_value; + isns_config.ic_auth.replay_window = parse_timeout(value); + } else if (!strcasecmp(name, "Auth.TimestampJitter")) { + if (!value) + goto no_value; + isns_config.ic_auth.timestamp_jitter = parse_timeout(value); + } else if (!strcasecmp(name, "Network.MaxSockets")) { + if (!value) + goto no_value; + isns_config.ic_network.max_sockets = parse_timeout(value); + } else if (!strcasecmp(name, "Network.ConnectTimeout")) { + if (!value) + goto no_value; + isns_config.ic_network.connect_timeout = parse_timeout(value); + } else if (!strcasecmp(name, "Network.ReconnectTimeout")) { + if (!value) + goto no_value; + isns_config.ic_network.reconnect_timeout = parse_timeout(value); + } else if (!strcasecmp(name, "Network.CallTimeout")) { + if (!value) + goto no_value; + isns_config.ic_network.call_timeout = parse_timeout(value); + } else { + fprintf(stderr, "Unknown config item %s=%s\n", name, value); + } + return 0; + +no_value: + fprintf(stderr, + "*** Missing value in configuration assignment for %s ***\n", + name); + return -1; +} diff --git a/utils/open-isns/config.h.in b/utils/open-isns/config.h.in new file mode 100644 index 0000000..b560bb0 --- /dev/null +++ b/utils/open-isns/config.h.in @@ -0,0 +1,103 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 if you have the <errno.h> header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the <getopt.h> header file. */ +#undef HAVE_GETOPT_H + +/* Define if you have the `getopt_long' function. */ +#undef HAVE_GETOPT_LONG + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <locale.h> header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the <malloc.h> header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the <openssl/crypto.h> header file. */ +#undef HAVE_OPENSSL_CRYPTO_H + +/* Define to 1 if you have the <slp.h> header file. */ +#undef HAVE_SLP_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you want to support iSNS authentication */ +#undef WITH_SECURITY + +/* Define if you want to support SLP discovery */ +#undef WITH_SLP + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif diff --git a/utils/open-isns/configure b/utils/open-isns/configure new file mode 100644 index 0000000..2d1054b --- /dev/null +++ b/utils/open-isns/configure @@ -0,0 +1,6727 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.63 for open-isns 0.90. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell bug-autoconf@gnu.org about your system, + echo including any error possibly output before this message. + echo This can help us improve future autoconf versions. + echo Configuration will now proceed without shell functions. +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 </dev/null 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME='open-isns' +PACKAGE_TARNAME='open-isns' +PACKAGE_VERSION='0.90' +PACKAGE_STRING='open-isns 0.90' +PACKAGE_BUGREPORT='' + +ac_unique_file="isnsd.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +OPTIMIZE +SLPLIBS +SECLIBS +GETOPTSRC +SH +SET_MAKE +LN_S +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +EGREP +GREP +CPP +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_security +with_slp +enable_memdebug +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { $as_echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { $as_echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures open-isns 0.90 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/open-isns] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of open-isns 0.90:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-memdebug Enable malloc debugging + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-security Enable iSNS authentication - requires OpenSSL + --with-slp Enable SLP for server discovery - requires OpenSLP + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +open-isns configure 0.90 +generated by GNU Autoconf 2.63 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by open-isns $as_me 0.90, which was +generated by GNU Autoconf 2.63. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_aux_dir= +for ac_dir in aclocal "$srcdir"/aclocal; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in aclocal \"$srcdir\"/aclocal" >&5 +$as_echo "$as_me: error: cannot find install-sh or install.sh in aclocal \"$srcdir\"/aclocal" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +ac_config_headers="$ac_config_headers config.h" + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + fi + fi +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 +$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} + { (exit 1); exit 1; }; } + +{ $as_echo "$as_me:$LINENO: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if test "${ac_cv_build+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 +$as_echo "$as_me: error: invalid value of canonical build" >&2;} + { (exit 1); exit 1; }; };; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:$LINENO: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if test "${ac_cv_host+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5 +$as_echo "$as_me: error: invalid value of canonical host" >&2;} + { (exit 1); exit 1; }; };; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:$LINENO: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:$LINENO: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done +done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + { $as_echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if test "${ac_cv_c_bigendian+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + + # Check for potential -arch flags. It is not universal unless + # there are some -arch flags. Note that *ppc* also matches + # ppc64. This check is also rather less than ideal. + case "${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}" in #( + *-arch*ppc*|*-arch*i386*|*-arch*x86_64*) ac_cv_c_bigendian=universal;; + esac +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + #include <sys/param.h> + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + # It does; now see whether it defined to BIG_ENDIAN or not. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> + #include <sys/param.h> + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_bigendian=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_bigendian=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <limits.h> + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <limits.h> + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_bigendian=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_bigendian=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then + # Try to guess by grepping values from an object file. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + + fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF +;; #( + no) + ;; #( + universal) + +cat >>confdefs.h <<\_ACEOF +#define AC_APPLE_UNIVERSAL_BUILD 1 +_ACEOF + + ;; #( + *) + { { $as_echo "$as_me:$LINENO: error: unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +$as_echo "$as_me: error: unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; + esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:$LINENO: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + +done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "sh", so it can be a program name with args. +set dummy sh; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_SH+set}" = set; then + $as_echo_n "(cached) " >&6 +else + case $SH in + [\\/]* | ?:[\\/]*) + ac_cv_path_SH="$SH" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_SH="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +SH=$ac_cv_path_SH +if test -n "$SH"; then + { $as_echo "$as_me:$LINENO: result: $SH" >&5 +$as_echo "$SH" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +{ $as_echo "$as_me:$LINENO: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if test "${ac_cv_c_inline+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_inline=$ac_kw +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +if test "$GCC" = "yes"; then + CFLAGS="-Wall -fno-strict-aliasing $CFLAGS" + CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" +fi + +{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if test "${ac_cv_header_sys_wait_h+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/wait.h> +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_sys_wait_h=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_sys_wait_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + + + + + + + + + + + +for ac_header in errno.h fcntl.h malloc.h stdlib.h string.h strings.h sys/time.h unistd.h locale.h getopt.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Check if socket() is in libsocket +{ $as_echo "$as_me:$LINENO: checking for socket in -lsocket" >&5 +$as_echo_n "checking for socket in -lsocket... " >&6; } +if test "${ac_cv_lib_socket_socket+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_socket_socket=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_socket_socket=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_socket_socket" >&5 +$as_echo "$ac_cv_lib_socket_socket" >&6; } +if test "x$ac_cv_lib_socket_socket" = x""yes; then + LIBS="$LIBS -lsocket" +fi + + + + +{ $as_echo "$as_me:$LINENO: checking for getopt_long" >&5 +$as_echo_n "checking for getopt_long... " >&6; } +if test "${ac_cv_func_getopt_long+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define getopt_long to an innocuous variant, in case <limits.h> declares getopt_long. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define getopt_long innocuous_getopt_long + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char getopt_long (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef getopt_long + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char getopt_long (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_getopt_long || defined __stub___getopt_long +choke me +#endif + +int +main () +{ +return getopt_long (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_func_getopt_long=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func_getopt_long=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_getopt_long" >&5 +$as_echo "$ac_cv_func_getopt_long" >&6; } +if test "x$ac_cv_func_getopt_long" = x""yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETOPT_LONG 1 +_ACEOF + +else + GETOPTSRC="$GETOPTSRC \$(top_srcdir)/compat/my_getopt.c" + CPPFLAGS="-I\$(top_srcdir)/compat/ $CPPFLAGS" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETOPT_H 1 +_ACEOF + +fi + + +WITH_SECURITY=maybe + +# Check whether --with-security was given. +if test "${with_security+set}" = set; then + withval=$with_security; + if test "x$withval" = "xno" -o "x$withval" = "xyes"; then + WITH_SECURITY=$withval + else + WITH_SECURITY=yes + CPPFLAGS="$CPPFLAGS -I${withval}" + LDFLAGS="$LDFLAGS -L${withval}" + fi + + +fi + + +if test "x$WITH_SECURITY" != "xno" ; then + # Check for openssl support - very primitive, we just + # check for the presence of crypto.h + +for ac_header in openssl/crypto.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +else + have_libcrypto=no +fi + +done + + { $as_echo "$as_me:$LINENO: checking for EVP_PKEY_new in -lcrypto" >&5 +$as_echo_n "checking for EVP_PKEY_new in -lcrypto... " >&6; } +if test "${ac_cv_lib_crypto_EVP_PKEY_new+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char EVP_PKEY_new (); +int +main () +{ +return EVP_PKEY_new (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_crypto_EVP_PKEY_new=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_crypto_EVP_PKEY_new=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_EVP_PKEY_new" >&5 +$as_echo "$ac_cv_lib_crypto_EVP_PKEY_new" >&6; } +if test "x$ac_cv_lib_crypto_EVP_PKEY_new" = x""yes; then + SECLIBS="-lcrypto" +else + have_libcrypto=no +fi + + + if test "x$have_libcrypto" != "xno" ; then + +cat >>confdefs.h <<\_ACEOF +#define WITH_SECURITY 1 +_ACEOF + + else + if test "x$WITH_SECURITY" = "xyes" ; then + { { $as_echo "$as_me:$LINENO: error: Security requested, but unable to find libcrypto" >&5 +$as_echo "$as_me: error: Security requested, but unable to find libcrypto" >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi + + +WITH_SLP=maybe + +# Check whether --with-slp was given. +if test "${with_slp+set}" = set; then + withval=$with_slp; + if test "x$withval" = "xno" -o "x$withval" = "xyes"; then + WITH_SLP=$withval + else + WITH_SLP=yes + CPPFLAGS="$CPPFLAGS -I${withval}" + LDFLAGS="$LDFLAGS -L${withval}" + fi + + +fi + + +if test "x$WITH_SLP" != "xno" ; then + # Check for openslp support - very primitive + +for ac_header in slp.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +else + have_openslp=no +fi + +done + + { $as_echo "$as_me:$LINENO: checking for SLPOpen in -lslp" >&5 +$as_echo_n "checking for SLPOpen in -lslp... " >&6; } +if test "${ac_cv_lib_slp_SLPOpen+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lslp $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char SLPOpen (); +int +main () +{ +return SLPOpen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_slp_SLPOpen=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_slp_SLPOpen=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_slp_SLPOpen" >&5 +$as_echo "$ac_cv_lib_slp_SLPOpen" >&6; } +if test "x$ac_cv_lib_slp_SLPOpen" = x""yes; then + SLPLIBS="-lslp" +else + have_openslp=no +fi + + + if test "x$have_openslp" != "xno" ; then + +cat >>confdefs.h <<\_ACEOF +#define WITH_SLP 1 +_ACEOF + + else + if test "x$WITH_SLP" = "xyes" ; then + { { $as_echo "$as_me:$LINENO: error: SLP requested, but unable to find openslp" >&5 +$as_echo "$as_me: error: SLP requested, but unable to find openslp" >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi + + +MEMDEBUG= +# Check whether --enable-memdebug was given. +if test "${enable_memdebug+set}" = set; then + enableval=$enable_memdebug; + if test "x$enableval" = "xyes" ; then + CPPFLAGS="$CPPFLAGS -DMEMDEBUG" + fi + + +fi + + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by open-isns $as_me 0.90, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to <bug-autoconf@gnu.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +open-isns config.status 0.90 +configured by $0, generated by GNU Autoconf 2.63, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { $as_echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr='
' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5 +$as_echo "$as_me: error: could not setup config headers machinery" >&2;} + { (exit 1); exit 1; }; } +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5 +$as_echo "$as_me: error: could not create -" >&2;} + { (exit 1); exit 1; }; } + fi + ;; + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/utils/open-isns/configure.ac b/utils/open-isns/configure.ac new file mode 100644 index 0000000..a02915b --- /dev/null +++ b/utils/open-isns/configure.ac @@ -0,0 +1,118 @@ +AC_INIT(open-isns, [0.90]) +AC_CONFIG_SRCDIR([isnsd.c]) +AC_CONFIG_AUX_DIR([aclocal]) + +AC_CONFIG_HEADER(config.h) + +AC_PROG_CC +AC_CANONICAL_HOST +AC_C_BIGENDIAN + +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PATH_PROG(SH, sh) + +dnl C Compiler features +AC_C_INLINE +if test "$GCC" = "yes"; then + CFLAGS="-Wall -fno-strict-aliasing $CFLAGS" + CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" +fi + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([errno.h fcntl.h malloc.h stdlib.h string.h strings.h sys/time.h unistd.h locale.h getopt.h]) + +# Check if socket() is in libsocket +AC_CHECK_LIB(socket, socket, [LIBS="$LIBS -lsocket"]) + + +AC_SUBST(GETOPTSRC) +AC_CHECK_FUNC(getopt_long, AC_DEFINE(HAVE_GETOPT_LONG, 1, [Define if you have the `getopt_long' function.]), + [GETOPTSRC="$GETOPTSRC \$(top_srcdir)/compat/my_getopt.c" + CPPFLAGS="-I\$(top_srcdir)/compat/ $CPPFLAGS" + AC_DEFINE(HAVE_GETOPT_H, 1, [Define if you have the <getopt.h> header file.])]) + +WITH_SECURITY=maybe +AC_ARG_WITH(security, + [ --with-security Enable iSNS authentication - requires OpenSSL], + [ + if test "x$withval" = "xno" -o "x$withval" = "xyes"; then + WITH_SECURITY=$withval + else + WITH_SECURITY=yes + CPPFLAGS="$CPPFLAGS -I${withval}" + LDFLAGS="$LDFLAGS -L${withval}" + fi + ] +) + +if test "x$WITH_SECURITY" != "xno" ; then + # Check for openssl support - very primitive, we just + # check for the presence of crypto.h + AC_CHECK_HEADERS([openssl/crypto.h], + , + [have_libcrypto=no]) + AC_CHECK_LIB(crypto, EVP_PKEY_new, + [SECLIBS="-lcrypto"], + [have_libcrypto=no]) + + if test "x$have_libcrypto" != "xno" ; then + AC_DEFINE(WITH_SECURITY, 1, + [Define if you want to support iSNS authentication]) + else + if test "x$WITH_SECURITY" = "xyes" ; then + AC_MSG_ERROR([Security requested, but unable to find libcrypto]) + fi + fi +fi +AC_SUBST(SECLIBS) + +WITH_SLP=maybe +AC_ARG_WITH(slp, + [ --with-slp Enable SLP for server discovery - requires OpenSLP], + [ + if test "x$withval" = "xno" -o "x$withval" = "xyes"; then + WITH_SLP=$withval + else + WITH_SLP=yes + CPPFLAGS="$CPPFLAGS -I${withval}" + LDFLAGS="$LDFLAGS -L${withval}" + fi + ] +) + +if test "x$WITH_SLP" != "xno" ; then + # Check for openslp support - very primitive + AC_CHECK_HEADERS([slp.h],, + [have_openslp=no]) + AC_CHECK_LIB(slp, SLPOpen, + [SLPLIBS="-lslp"], + [have_openslp=no]) + + if test "x$have_openslp" != "xno" ; then + AC_DEFINE(WITH_SLP, 1, + [Define if you want to support SLP discovery]) + else + if test "x$WITH_SLP" = "xyes" ; then + AC_MSG_ERROR([SLP requested, but unable to find openslp]) + fi + fi +fi +AC_SUBST(SLPLIBS) + +MEMDEBUG= +AC_ARG_ENABLE(memdebug, + [ --enable-memdebug Enable malloc debugging], + [ + if test "x$enableval" = "xyes" ; then + CPPFLAGS="$CPPFLAGS -DMEMDEBUG" + fi + ] +) +AC_SUBST(OPTIMIZE) + +AC_OUTPUT(Makefile) diff --git a/utils/open-isns/db-file.c b/utils/open-isns/db-file.c new file mode 100644 index 0000000..8adee06 --- /dev/null +++ b/utils/open-isns/db-file.c @@ -0,0 +1,615 @@ +/* + * iSNS object database + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> + +#include "isns.h" +#include "objects.h" +#include "message.h" +#include "util.h" +#include "db.h" + +#define DBE_FILE_VERSION 1 + +struct isns_db_file_info { + uint32_t db_version; + uint32_t db_last_eid; + uint32_t db_last_index; +}; + +struct isns_db_object_info { + uint32_t db_version; + char db_type[64]; + uint32_t db_parent; + uint32_t db_state; + uint32_t db_flags; + uint32_t db_scn_mask; + /* reserved bytes */ + uint32_t __db_reserved[15]; +}; + +static int isns_dbe_file_sync(isns_db_t *); +static int isns_dbe_file_reload(isns_db_t *); +static int isns_dbe_file_store(isns_db_t *, + const isns_object_t *); +static int isns_dbe_file_remove(isns_db_t *, + const isns_object_t *); +static int __dbe_file_load_all(const char *, + isns_object_list_t *); + +/* + * Helper functions + */ +static const char * +__path_concat(const char *dirname, const char *basename) +{ + static char pathname[PATH_MAX]; + + snprintf(pathname, sizeof(pathname), "%s/%s", + dirname, basename); + return pathname; +} + +static const char * +__print_index(uint32_t index) +{ + static char namebuf[32]; + + snprintf(namebuf, sizeof(namebuf), "%08x", index); + return namebuf; +} + +static int +__get_index(const char *name, uint32_t *result) +{ + char *end; + + *result = strtoul(name, &end, 16); + if (*end) + return ISNS_INTERNAL_ERROR; + return ISNS_SUCCESS; +} + +/* + * Build path names for an object + */ +static const char * +__dbe_file_object_path(const char *dirname, const isns_object_t *obj) +{ + return __path_concat(dirname, __print_index(obj->ie_index)); +} + +/* + * Build a path name for a temporary file. + * Cannot use __path_concat, because we need both names + * when storing objects + */ +static const char * +__dbe_file_object_temp(const char *dirname, const isns_object_t *obj) +{ + static char pathname[PATH_MAX]; + + snprintf(pathname, sizeof(pathname), "%s/.%s", + dirname, __print_index(obj->ie_index)); + return pathname; +} + +/* + * Recursively create a directory + */ +static int +__dbe_mkdir_path(const char *dirname) +{ + unsigned int true_len = strlen(dirname); + char *copy, *s; + + copy = isns_strdup(dirname); + + /* Walk up until we find a directory that exists */ + while (1) { + s = strrchr(copy, '/'); + if (s == NULL) + break; + + *s = '\0'; + if (access(copy, F_OK) == 0) + break; + } + + while (strcmp(dirname, copy)) { + unsigned int len = strlen(copy); + + /* Better safe than sorry */ + isns_assert(len < true_len); + + /* Put the next slash back in */ + copy[len] = '/'; + + /* and try to create the directory */ + if (mkdir(copy, 0700) < 0) + return -1; + } + + return 0; +} + +/* + * Write an object to a file + */ +static int +__dbe_file_store_object(const char *dirname, const isns_object_t *obj) +{ + struct isns_db_object_info info; + const char *path = __dbe_file_object_path(dirname, obj); + const char *temp = __dbe_file_object_temp(dirname, obj); + buf_t *bp = NULL; + int status = ISNS_INTERNAL_ERROR; + + isns_debug_state("DB: Storing object %u -> %s\n", obj->ie_index, path); + if (access(dirname, F_OK) < 0 + && (errno != ENOENT || __dbe_mkdir_path(dirname) < 0)) { + isns_error("DB: Unable to create %s: %m\n", + dirname); + goto out; + } + + bp = buf_open(temp, O_CREAT|O_TRUNC|O_WRONLY); + if (bp == NULL) { + isns_error("Unable to open %s: %m\n", temp); + goto out; + } + + /* Encode the header info ... */ + memset(&info, 0, sizeof(info)); + info.db_version = htonl(DBE_FILE_VERSION); + info.db_state = htonl(obj->ie_state); + info.db_flags = htonl(obj->ie_flags); + info.db_scn_mask = htonl(obj->ie_scn_mask); + strcpy(info.db_type, obj->ie_template->iot_name); + if (obj->ie_container) + info.db_parent = htonl(obj->ie_container->ie_index); + + if (!buf_put(bp, &info, sizeof(info))) + goto out; + + /* ... and attributes */ + status = isns_attr_list_encode(bp, &obj->ie_attrs); + if (status != ISNS_SUCCESS) + goto out; + + /* Renaming an open file. NFS will hate this */ + if (rename(temp, path) < 0) { + isns_error("Cannot rename %s -> %s: %m\n", + temp, path); + unlink(temp); + status = ISNS_INTERNAL_ERROR; + } + +out: + if (bp) + buf_close(bp); + return status; +} + +/* + * Store all children of an object + */ +static int +__dbe_file_store_children(const char *dirname, const isns_object_t *obj) +{ + int status = ISNS_SUCCESS; + unsigned int i; + + for (i = 0; i < obj->ie_children.iol_count; ++i) { + isns_object_t *child; + + child = obj->ie_children.iol_data[i]; + status = __dbe_file_store_object(dirname, child); + if (status) + break; + status = __dbe_file_store_children(dirname, child); + if (status) + break; + } + + return status; +} + +/* + * Remove object and children + */ +static int +__dbe_file_remove_object(const char *dirname, const isns_object_t *obj) +{ + const char *path = __dbe_file_object_path(dirname, obj); + + isns_debug_state("DB: Purging object %u (%s)\n", obj->ie_index, path); + if (unlink(path) < 0) + isns_error("DB: Cannot remove %s: %m\n", path); + return ISNS_SUCCESS; +} + +static int +__dbe_file_remove_children(const char *dirname, const isns_object_t *obj) +{ + const isns_object_list_t *list = &obj->ie_children; + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) + __dbe_file_remove_object(dirname, list->iol_data[i]); + + return ISNS_SUCCESS; +} + +/* + * Load an object from file + */ +static int +__dbe_file_load_object(const char *filename, const char *basename, + isns_object_list_t *result) +{ + struct isns_db_object_info info; + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + isns_object_template_t *tmpl; + isns_object_t *obj = NULL; + buf_t *bp = NULL; + uint32_t index; + int status; + + bp = buf_open(filename, O_RDONLY); + if (bp == NULL) { + isns_error("Unable to open %s: %m\n", filename); + goto internal_error; + } + + /* Decode the header ... */ + if (!buf_get(bp, &info, sizeof(info))) + goto internal_error; + if (info.db_version != htonl(DBE_FILE_VERSION)) { + /* If we ever have to deal with a DB version + * upgrade, we could do it here. */ + isns_fatal("Found iSNS database version %u; not supported\n", + ntohl(info.db_version)); + } + + /* ... and attributes */ + status = isns_attr_list_decode(bp, &attrs); + if (status != ISNS_SUCCESS) + goto out; + + /* Get the index from the file name */ + status = __get_index(basename, &index); + if (status != ISNS_SUCCESS) + goto out; + + tmpl = isns_object_template_by_name(info.db_type); + if (tmpl == NULL) { + isns_error("DB: Bad type name \"%s\" in object file\n", + info.db_type); + goto internal_error; + } + + obj = isns_create_object(tmpl, &attrs, NULL); + if (obj == NULL) + goto internal_error; + + obj->ie_state = ntohl(info.db_state); + obj->ie_flags = ntohl(info.db_flags) & ~(ISNS_OBJECT_DIRTY); + obj->ie_scn_mask = ntohl(info.db_scn_mask); + obj->ie_index = index; + + /* Stash away the parent's index; we resolve them later on + * once we've loaded all objects */ + obj->ie_container = (isns_object_t *) ntohl(info.db_parent); + + isns_object_list_append(result, obj); + +out: + if (bp) + buf_close(bp); + if (obj) + isns_object_release(obj); + isns_attr_list_destroy(&attrs); + return status; + +internal_error: + isns_error("Unable to load %s: Internal error\n", + filename); + status = ISNS_INTERNAL_ERROR; + goto out; +} + +/* + * Load contents of directory into our database. + * + * We take two passes over the directory. In the first pass, we load + * all regular files containing objects. The file names correspond to + * the DB index. + * + * In the second pass, we load all directories, containing children of + * an object. The directories names are formed by the object's index, + * with ".d" appended to it. + */ +static int +__dbe_file_load_all(const char *dirpath, isns_object_list_t *result) +{ + struct dirent *dp; + DIR *dir; + int status = ISNS_SUCCESS; + + if ((dir = opendir(dirpath)) == NULL) { + isns_error("DB: cannot open %s: %m\n", dirpath); + return ISNS_INTERNAL_ERROR; + } + + while ((dp = readdir(dir)) != NULL) { + struct stat stb; + const char *path; + + if (dp->d_name[0] == '.' + || !strcmp(dp->d_name, "DB")) + continue; + + path = __path_concat(dirpath, dp->d_name); + if (lstat(path, &stb) < 0) { + isns_error("DB: cannot stat %s: %m\n", path); + status = ISNS_INTERNAL_ERROR; + } else + if (S_ISREG(stb.st_mode)) { + status = __dbe_file_load_object(path, + dp->d_name, result); + } else { + isns_debug_state("DB: ignoring %s\n", path); + } + + if (status != ISNS_SUCCESS) + break; + } + + closedir(dir); + return status; +} + +/* + * Load and store DB metadata + */ +static int +__dbe_file_write_info(isns_db_t *db) +{ + isns_db_backend_t *back = db->id_backend; + const char *path; + buf_t *bp; + int status = ISNS_INTERNAL_ERROR; + + path = __path_concat(back->idb_name, "DB"); + if ((bp = buf_open(path, O_CREAT|O_TRUNC|O_WRONLY)) == NULL) { + isns_error("Unable to write %s: %m\n", path); + goto out; + } + + if (buf_put32(bp, DBE_FILE_VERSION) + && buf_put32(bp, db->id_last_eid) + && buf_put32(bp, db->id_last_index)) + status = ISNS_SUCCESS; + +out: + if (bp) + buf_close(bp); + return status; +} + +static int +__dbe_file_load_info(isns_db_t *db) +{ + isns_db_backend_t *back = db->id_backend; + struct isns_db_file_info info; + const char *path; + buf_t *bp = NULL; + int status; + + path = __path_concat(back->idb_name, "DB"); + if ((bp = buf_open(path, O_RDONLY)) == NULL) { + status = ISNS_NO_SUCH_ENTRY; + goto out; + } + + status = ISNS_INTERNAL_ERROR; + if (!buf_get32(bp, &info.db_version)) + goto out; + + if (info.db_version != DBE_FILE_VERSION) { + isns_error("DB file from unsupported version %04x\n", + info.db_version); + goto out; + } + + if (buf_get32(bp, &info.db_last_eid) + && buf_get32(bp, &info.db_last_index)) { + db->id_last_eid = info.db_last_eid; + db->id_last_index = info.db_last_index; + status = ISNS_SUCCESS; + } + +out: + if (bp) + buf_close(bp); + return status; +} + +/* + * Find object with the given index. + */ +static isns_object_t * +__dbe_find_object(isns_object_list_t *list, uint32_t index) +{ + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj->ie_index == index) + return obj; + } + return NULL; +} + +int +isns_dbe_file_reload(isns_db_t *db) +{ + isns_db_backend_t *back = db->id_backend; + int status; + unsigned int i; + + isns_debug_state("DB: loading all objects from %s\n", + back->idb_name); + + if (access(back->idb_name, R_OK) < 0) { + if (errno == ENOENT) { + /* Empty database is okay */ + return ISNS_NO_SUCH_ENTRY; + } + isns_error("Cannot open database %s: %m\n", back->idb_name); + return ISNS_INTERNAL_ERROR; + } + + status = __dbe_file_load_info(db); + if (status) + return status; + + status = __dbe_file_load_all(back->idb_name, db->id_objects); + if (status) + return status; + + /* Resolve parent/child relationship for all nodes */ + for (i = 0; i < db->id_objects->iol_count; ++i) { + isns_object_t *obj = db->id_objects->iol_data[i]; + uint32_t index = (uint32_t) obj->ie_container; + isns_object_t *parent; + + if (index == 0) + continue; + + obj->ie_container = NULL; + + parent = __dbe_find_object(db->id_objects, index); + if (parent == NULL) { + isns_warning("DB: object %u references " + "unknown container %u\n", + obj->ie_index, + index); + } else { + isns_object_attach(obj, parent); + } + } + + /* Add objects to the appropriate lists */ + for (i = 0; i < db->id_objects->iol_count; ++i) { + isns_object_template_t *tmpl; + isns_object_t *obj = db->id_objects->iol_data[i]; + + switch (obj->ie_state) { + case ISNS_OBJECT_STATE_MATURE: + isns_scope_add(db->id_global_scope, obj); + obj->ie_references++; + + tmpl = obj->ie_template; + if (tmpl->iot_build_relation + && !tmpl->iot_build_relation(db, obj, NULL)) + isns_warning("DB: cannot build relation for " + "object %u\n", + obj->ie_index); + + if (obj->ie_relation) + isns_relation_add(db->id_relations, + obj->ie_relation); + + if (ISNS_IS_ENTITY(obj)) + isns_esi_register(obj); + break; + + case ISNS_OBJECT_STATE_LIMBO: + isns_object_list_append(&db->id_limbo, obj); + break; + + default: + isns_error("Unexpected object state %d in object %u " + "loaded from %s\n", + obj->ie_state, obj->ie_index, + back->idb_name); + } + + /* Clear the dirty flag, which will be set when the + object is created. */ + obj->ie_flags &= ~ISNS_OBJECT_DIRTY; + } + + return ISNS_SUCCESS; +} + +int +isns_dbe_file_sync(isns_db_t *db) +{ + return __dbe_file_write_info(db); +} + +int +isns_dbe_file_store(isns_db_t *db, const isns_object_t *obj) +{ + isns_db_backend_t *back = db->id_backend; + int status; + + if (obj->ie_index == 0) { + isns_error("DB: Refusing to store object with index 0\n"); + return ISNS_INTERNAL_ERROR; + } + + status = __dbe_file_store_object(back->idb_name, obj); + if (status == ISNS_SUCCESS) + status = __dbe_file_store_children(back->idb_name, obj); + + return status; +} + +int +isns_dbe_file_remove(isns_db_t *db, const isns_object_t *obj) +{ + isns_db_backend_t *back = db->id_backend; + int status; + + status = __dbe_file_remove_object(back->idb_name, obj); + if (status == ISNS_SUCCESS) + status = __dbe_file_remove_children(back->idb_name, obj); + + return status; +} + +/* + * Create the file backend + */ +isns_db_backend_t * +isns_create_file_db_backend(const char *pathname) +{ + isns_db_backend_t *back; + + isns_debug_state("Creating file DB backend (%s)\n", pathname); + + back = isns_calloc(1, sizeof(*back)); + back->idb_name = isns_strdup(pathname); + back->idb_reload = isns_dbe_file_reload; + back->idb_sync = isns_dbe_file_sync; + back->idb_store = isns_dbe_file_store; + back->idb_remove = isns_dbe_file_remove; + + return back; +} + diff --git a/utils/open-isns/db-policy.c b/utils/open-isns/db-policy.c new file mode 100644 index 0000000..7f09cba --- /dev/null +++ b/utils/open-isns/db-policy.c @@ -0,0 +1,185 @@ +/* + * Use database as policy and keystore + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include "isns.h" +#include "security.h" +#include "objects.h" +#include "vendor.h" +#include "util.h" +#include "config.h" + +/* + * DB keystore + */ +typedef struct isns_db_keystore isns_db_keystore_t; +struct isns_db_keystore { + isns_keystore_t sd_base; + isns_db_t * sd_db; + isns_object_t * sd_control; +}; + +/* + * Look up the policy object given its SPI + */ +isns_object_t * +__isns_db_keystore_lookup(isns_db_keystore_t *store, + const char *name, size_t namelen) +{ + isns_attr_list_t keys = ISNS_ATTR_LIST_INIT; + char namebuf[256]; + + if (namelen >= sizeof(namebuf)) + return NULL; + memcpy(namebuf, name, namelen); + namebuf[namelen] = '\0'; + + isns_attr_list_append_string(&keys, + OPENISNS_TAG_POLICY_SPI, + namebuf); + return isns_db_lookup(store->sd_db, NULL, &keys); +} + +/* + * Load a DSA key from the DB store + */ +static EVP_PKEY * +__isns_db_keystore_find(isns_keystore_t *store_base, + const char *name, size_t namelen) +{ +#ifdef WITH_SECURITY + isns_db_keystore_t *store = (isns_db_keystore_t *) store_base; + isns_object_t *obj; + const void *key_data; + size_t key_size; + + obj = __isns_db_keystore_lookup(store, name, namelen); + if (obj == NULL) + return NULL; + + if (!isns_object_get_opaque(obj, OPENISNS_TAG_POLICY_KEY, + &key_data, &key_size)) + return NULL; + + return isns_dsa_decode_public(key_data, key_size); +#else + return NULL; +#endif +} + +/* + * Retrieve policy from database + */ +static void +__isns_db_keystore_copy_policy_string(isns_object_t *obj, + uint32_t tag, char **var) +{ + const char *value; + + if (!isns_object_get_string(obj, tag, &value)) + return; + isns_assign_string(var, value); +} + +static void +__isns_db_keystore_copy_policy_strings(isns_object_t *obj, + uint32_t tag, struct string_array *array) +{ + isns_attr_list_t *attrs = &obj->ie_attrs; + unsigned int i; + + for (i = 0; i < attrs->ial_count; ++i) { + isns_attr_t *attr = attrs->ial_data[i]; + + if (attr->ia_tag_id != tag + || !ISNS_ATTR_IS_STRING(attr)) + continue; + isns_string_array_append(array, attr->ia_value.iv_string); + } +} + +static isns_policy_t * +__isns_db_keystore_get_policy(isns_keystore_t *store_base, + const char *name, size_t namelen) +{ + isns_db_keystore_t *store = (isns_db_keystore_t *) store_base; + isns_policy_t *policy; + isns_object_t *obj; + uint32_t intval; + + obj = __isns_db_keystore_lookup(store, name, namelen); + if (obj == NULL) + return NULL; + + policy = __isns_policy_alloc(name, namelen); + + /* retrieve policy bits from object */ +#if 0 + __isns_db_keystore_copy_policy_string(obj, + OPENISNS_TAG_POLICY_SOURCE_NAME, + &policy->ip_source); +#endif + __isns_db_keystore_copy_policy_string(obj, + OPENISNS_TAG_POLICY_ENTITY, + &policy->ip_entity); + __isns_db_keystore_copy_policy_string(obj, + OPENISNS_TAG_POLICY_DEFAULT_DD, + &policy->ip_dd_default); + __isns_db_keystore_copy_policy_strings(obj, + OPENISNS_TAG_POLICY_NODE_NAME, + &policy->ip_node_names); + + if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_OBJECT_TYPE, &intval)) + policy->ip_object_types = intval; + if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_NODE_TYPE, &intval)) + policy->ip_node_types = intval; + if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_FUNCTIONS, &intval)) + policy->ip_functions = intval; + + return policy; +} + +void +__isns_db_keystore_change_notify(const isns_db_event_t *ev, void *handle) +{ + isns_db_keystore_t *store = handle; + isns_object_t *obj = ev->ie_object; + + if (isns_object_get_entity(obj) == store->sd_control) { + isns_debug_auth("DB keystore: policy data was modified\n"); + store->sd_base.ic_generation++; + } +} + +isns_keystore_t * +isns_create_db_keystore(isns_db_t *db) +{ + isns_db_keystore_t *store; + isns_object_t *entity; + + isns_debug_auth("Creating DB keystore\n"); + if (!(entity = isns_db_get_control(db))) { + isns_error("Could not create control entity in database\n"); + return NULL; + } + isns_debug_auth("Control entity is 0x%08x\n", entity->ie_index); + + store = isns_calloc(1, sizeof(*store)); + store->sd_base.ic_name = "database key store"; + store->sd_base.ic_find = __isns_db_keystore_find; + store->sd_base.ic_get_policy = __isns_db_keystore_get_policy; + store->sd_control = entity; + store->sd_db = db; + + isns_register_callback(__isns_db_keystore_change_notify, store); + + return (isns_keystore_t *) store; +} + diff --git a/utils/open-isns/db.c b/utils/open-isns/db.c new file mode 100644 index 0000000..c66dfbb --- /dev/null +++ b/utils/open-isns/db.c @@ -0,0 +1,994 @@ +/* + * iSNS object database + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <stdarg.h> + +#include "isns.h" +#include "objects.h" +#include "db.h" +#include "util.h" + +enum { + IDT_INSERT, + IDT_REMOVE, + IDT_UPDATE +}; +struct isns_db_trans { + struct isns_db_trans * idt_next; + int idt_action; + isns_object_t * idt_object; +}; + +/* Internal helpers */ +static int isns_db_sanity_check(isns_db_t *); +static int isns_db_get_key_tags(const isns_attr_list_t *, + uint32_t *, unsigned int); +static int isns_db_keyed_compare(const isns_object_t *, + const isns_attr_list_t *, + const uint32_t *, unsigned int); + +/* + * Open a database + */ +static isns_db_t * +isns_db_create(isns_db_backend_t *backend) +{ + isns_db_t *db; + + db = isns_calloc(1, sizeof(*db)); + db->id_last_index = 1; + db->id_last_eid = 1; + db->id_backend = backend; + db->id_global_scope = isns_scope_alloc(db); + db->id_relations = isns_relation_soup_alloc(); + db->id_objects = &db->__id_objects; + + if (backend && backend->idb_reload) { + int status; + + status = backend->idb_reload(db); + /* "No such entry" is returned when the DB + * is still empty. */ + if (status != ISNS_SUCCESS + && status != ISNS_NO_SUCH_ENTRY) { + isns_error("Error loading database: %s\n", + isns_strerror(status)); + /* FIXME: isns_db_free(db); */ + return NULL; + } + + isns_db_sanity_check(db); + } + + return db; +} + +isns_db_t * +isns_db_open(const char *location) +{ + isns_db_backend_t *backend; + + if (location == NULL) { + isns_debug_state("Using in-memory DB\n"); + return isns_db_create(NULL); + } + + if (location[0] == '/') { + backend = isns_create_file_db_backend(location); + } else + if (!strncmp(location, "file:", 5)) { + backend = isns_create_file_db_backend(location + 5); + } else { + isns_error("Unsupported database type \"%s\"\n", + location); + return NULL; + } + + return isns_db_create(backend); +} + +isns_db_t * +isns_db_open_shadow(isns_object_list_t *list) +{ + isns_db_t *db; + + if ((db = isns_db_create(NULL)) != NULL) + db->id_objects = list; + return db; +} + +int +isns_db_sanity_check(isns_db_t *db) +{ + unsigned int i; + + i = 0; + while (i < db->id_objects->iol_count) { + isns_object_t *obj = db->id_objects->iol_data[i]; + + switch (obj->ie_state) { + case ISNS_OBJECT_STATE_MATURE: + /* Nothing yet. */ + break; + + case ISNS_OBJECT_STATE_LIMBO: + if (!ISNS_IS_ISCSI_NODE(obj) + && !ISNS_IS_PORTAL(obj)) { + isns_error("Unexpected object %u (%s) in limbo\n", + obj->ie_index, + obj->ie_template->iot_name); + isns_db_remove(db, obj); + } + break; + + default: + isns_error("Unexpected object state %d in object %u (%s)\n", + obj->ie_state, obj->ie_index, + obj->ie_template->iot_name); + isns_db_remove(db, obj); + break; + } + + i += 1; + } + + return 1; +} + +isns_object_t * +isns_db_lookup(isns_db_t *db, + isns_object_template_t *tmpl, + const isns_attr_list_t *keys) +{ + return isns_object_list_lookup(db->id_objects, tmpl, keys); +} + +int +isns_db_gang_lookup(isns_db_t *db, + isns_object_template_t *tmpl, + const isns_attr_list_t *keys, + isns_object_list_t *result) +{ + return isns_object_list_gang_lookup(db->id_objects, + tmpl, keys, result); +} + +/* + * Look up the storage node for the given source. + */ +isns_object_t * +isns_db_lookup_source_node(isns_db_t *db, + const isns_source_t *source) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + isns_object_t *node; + + isns_attr_list_append_attr(&attrs, isns_source_attr(source)); + node = isns_db_lookup(db, NULL, &attrs); + isns_attr_list_destroy(&attrs); + + return node; +} + +isns_object_t * +isns_db_vlookup(isns_db_t *db, + isns_object_template_t *tmpl, + ...) +{ + isns_attr_list_t keys = ISNS_ATTR_LIST_INIT; + isns_object_t *obj = NULL; + va_list ap; + + va_start(ap, tmpl); + while (1) { + const isns_tag_type_t *tag_type; + isns_value_t value; + uint32_t tag; + + tag = va_arg(ap, unsigned int); + if (tag == 0) + break; + + tag_type = isns_tag_type_by_id(tag); + if (tag_type == NULL) { + isns_error("isns_db_vlookup: unknown tag %u\n", tag); + goto out; + } + + memset(&value, 0, sizeof(value)); + value.iv_type = tag_type->it_type; + switch (tag_type->it_type->it_id) { + case ISNS_ATTR_TYPE_STRING: + value.iv_string = va_arg(ap, char *); + break; + + case ISNS_ATTR_TYPE_INT32: + value.iv_int32 = va_arg(ap, int32_t); + break; + + case ISNS_ATTR_TYPE_UINT32: + value.iv_int32 = va_arg(ap, uint32_t); + break; + + case ISNS_ATTR_TYPE_IPADDR: + value.iv_ipaddr = *va_arg(ap, struct in6_addr *); + break; + + default: + isns_error("isns_db_vlookup: unsupported tag type %s\n", + value.iv_type->it_name); + goto out; + } + + isns_attr_list_append_value(&keys, tag, tag_type, &value); + } + + obj = isns_db_lookup(db, tmpl, &keys); + +out: + isns_attr_list_destroy(&keys); + va_end(ap); + return obj; +} + +/* + * Find the next matching object + * + * This implementation could be a lot simpler if the + * RFC didn't make things so awfully complicated. + * It could simply have mandated the use of the object + * index attribute, period. + */ +isns_object_t * +__isns_db_get_next(const isns_object_list_t *list, + isns_object_template_t *tmpl, + const isns_attr_list_t *current, + const isns_attr_list_t *scope) +{ + isns_object_t *next = NULL; + uint32_t tags[16]; + unsigned int i; + int num_tags; + + if (!tmpl) + return NULL; + + /* Get the search attribute tags, and sort them. + * Note, these don't have to be the standard key + * attributes for a given object type; the RFC + * also permits index attributes. + */ + num_tags = isns_db_get_key_tags(current, tags, 16); + if (num_tags < 0) + return NULL; + + /* + * 5.6.5.3. + * If the TLV length of the Message Key Attribute(s) is zero, + * then the first object entry in the iSNS database matching the + * Message Key type SHALL be returned in the Message Key of the + * corresponding DevGetNextRsp message. + */ + for (i = 0; i < current->ial_count; ++i) { + isns_attr_t *attr = current->ial_data[i]; + + if (!ISNS_ATTR_IS_NIL(attr)) + goto non_nil; + } + current = NULL; +non_nil: + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj->ie_template != tmpl) + continue; + if (scope && !isns_object_match(obj, scope)) + continue; + + /* compare returns -1 if the first list + * is "before" the second list, in terms of + * implicit ordering. */ + if (current + && isns_db_keyed_compare(obj, current, tags, num_tags) <= 0) { + /* obj less than or equal to current */ + continue; + } + + if (next == NULL + || isns_db_keyed_compare(obj, &next->ie_attrs, tags, num_tags) < 0) + next = obj; + } + + if (next) + isns_object_get(next); + return next; +} + +isns_object_t * +isns_db_get_next(isns_db_t *db, + isns_object_template_t *tmpl, + const isns_attr_list_t *current, + const isns_attr_list_t *scope, + const isns_source_t *source) +{ + return __isns_db_get_next(db->id_objects, + tmpl, current, scope); +} + +/* + * Get the search key tags + */ +static int +isns_db_get_key_tags(const isns_attr_list_t *keys, + uint32_t *tags, unsigned int max_tags) +{ + unsigned int i; + + /* Get the search attribute tags, and sort them */ + for (i = 0; i < keys->ial_count; ++i) { + if (i >= 16) + return -1; + tags[i] = keys->ial_data[i]->ia_tag_id; + } + + /* FIXME: qsort the list */ + return i; +} + +/* + * Helper function for GetNext + */ +static int +isns_db_keyed_compare(const isns_object_t *obj, + const isns_attr_list_t *attrs, + const uint32_t *tags, unsigned int num_tags) +{ + int ind = 0; + unsigned int i; + + for (i = 0; i < num_tags; ++i) { + isns_attr_t *attr1, *attr2; + uint32_t tag = tags[i]; + + if (!isns_attr_list_get_attr(&obj->ie_attrs, tag, &attr1)) + attr1 = NULL; + if (!isns_attr_list_get_attr(attrs, tag, &attr2)) + attr2 = NULL; + if (attr1 == attr2) { + ind = 0; + } else if (attr1 && attr2) { + ind = isns_attr_compare(attr1, attr2); + } else if (attr1 == NULL) { + ind = -1; + } else { + ind = 1; + } + if (ind) + break; + } + return ind; +} + +uint32_t +isns_db_allocate_index(isns_db_t *db) +{ + return db->id_last_index++; +} + +/* + * Insert an object into the database. + */ +void +__isns_db_insert(isns_db_t *db, isns_object_t *obj, unsigned int state) +{ + uint32_t idx_tag = obj->ie_template->iot_index; + + switch (obj->ie_state) { + case ISNS_OBJECT_STATE_LIMBO: + /* The object was in limbo; now it goes + * live (again). It should have an index, + * and it should be on the global id_objects + * list too. + */ + isns_assert(state == ISNS_OBJECT_STATE_MATURE); + isns_assert(obj->ie_index); + isns_assert(obj->ie_users > 1); + isns_object_list_remove(&db->id_limbo, obj); + break; + + case ISNS_OBJECT_STATE_DEAD: + /* A DevAttrReg with the F_REPLACE bit set will cause + * the key object to be removed from the DB, which may + * kill it for good. + * The subsequent call to db_insert will assign a new + * index, and re-add it to the database. + */ + + case ISNS_OBJECT_STATE_LARVAL: + /* Larval objects can go either to mature or + * limbo state. */ + obj->ie_index = db->id_last_index++; + + if (idx_tag) + isns_object_set_uint32(obj, + idx_tag, + obj->ie_index); + + isns_object_list_append(db->id_objects, obj); + break; + + case ISNS_OBJECT_STATE_MATURE: + /* If we call db_insert on a mature object, treat + this as a NOP. */ + isns_assert(state == ISNS_OBJECT_STATE_MATURE); + return; + + default: + isns_error("Internal error: unexpected object %u (%s) " + "state %u in db_insert\n", + obj->ie_index, + obj->ie_template->iot_name, + obj->ie_state); + return; + } + + obj->ie_state = state; + + /* Add it to the global scope */ + if (state == ISNS_OBJECT_STATE_MATURE) { + isns_scope_add(db->id_global_scope, obj); + obj->ie_references++; + + /* See if this object represents a relationship + * (eg a portal group). */ + if (obj->ie_template->iot_relation_type) { + if (!obj->ie_relation) { + isns_warning("DB: inserting %s object " + "without relation\n", + obj->ie_template->iot_name); + } else { + isns_relation_add(db->id_relations, + obj->ie_relation); + } + } + + isns_mark_object(obj, ISNS_SCN_OBJECT_ADDED); + } + + isns_debug_state("DB: added object %u (%s) state %u\n", + obj->ie_index, + obj->ie_template->iot_name, + obj->ie_state); + + if (db->id_backend) { + db->id_backend->idb_store(db, obj); + db->id_backend->idb_sync(db); + } +} + +void +isns_db_insert(isns_db_t *db, isns_object_t *obj) +{ + __isns_db_insert(db, obj, ISNS_OBJECT_STATE_MATURE); +} + +void +isns_db_insert_limbo(isns_db_t *db, isns_object_t *obj) +{ + isns_assert(obj->ie_state == ISNS_OBJECT_STATE_LARVAL); + __isns_db_insert(db, obj, ISNS_OBJECT_STATE_LIMBO); +} + +/* + * Save an object after updating it + */ +void +isns_db_sync(isns_db_t *db) +{ + isns_object_list_t *list = db->id_objects; + unsigned int i, saved = 0; + + if (!db->id_backend) + return; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj->ie_flags & ISNS_OBJECT_DIRTY) { + db->id_backend->idb_store(db, obj); + obj->ie_flags &= ~ISNS_OBJECT_DIRTY; + saved++; + } + } + if (saved) + db->id_backend->idb_sync(db); +} + +/* + * Remove an object from the database. + * This is slow and inefficient, due to the use + * of an object array. We should at least use + * a linked list, or maybe even a hash one day. + */ +static void +__isns_db_prepare_removal(isns_db_t *db, isns_object_t *obj) +{ + isns_object_t *child; + + obj->ie_flags |= ISNS_OBJECT_DEAD; + isns_object_get(obj); + + /* The node is dead; it's no longer interested in SCNs */ + obj->ie_scn_mask = 0; + + /* Trigger an SCN event. */ + if (obj->ie_state == ISNS_OBJECT_STATE_MATURE) + isns_mark_object(obj, ISNS_SCN_OBJECT_REMOVED); + + /* If the object represents a relation between + * two other objects, sever that relationship. + */ + if (obj->ie_relation) { + isns_relation_remove(db->id_relations, + obj->ie_relation); + isns_relation_sever(obj->ie_relation); + isns_relation_release(obj->ie_relation); + obj->ie_relation = NULL; + } + + /* Detach the object from its container */ + isns_object_detach(obj); + + /* Remove it from the database */ + if (isns_scope_remove(db->id_global_scope, obj)) { + obj->ie_references--; + } else { + isns_warning("Unable to remove object from scope\n"); + } + + /* Recursively remove all children */ + while (obj->ie_children.iol_count) { + child = obj->ie_children.iol_data[0]; + __isns_db_prepare_removal(db, child); + } + + isns_debug_state("DB: removed object %u (%s)\n", + obj->ie_index, + obj->ie_template->iot_name); + + isns_object_list_append(&db->id_deferred, obj); + isns_object_release(obj); +} + +int +isns_db_remove(isns_db_t *db, isns_object_t *obj) +{ + isns_object_t *entity; + unsigned int i; + + /* Don't even bother if the object was never added */ + if (obj->ie_index == 0) + goto out; + + /* Obtain the containing entity before removal */ + entity = isns_object_get_entity(obj); + + /* We don't remove the object for real yet; + * this will happen later during db_purge */ + __isns_db_prepare_removal(db, obj); + + /* + * 5.6.5.4. + * If all Nodes and Portals associated with a Network Entity are + * deregistered, then the Network Entity SHALL also be removed. + * + * If both the Portal and iSCSI Storage Node objects associated + * with a Portal Group object are removed, then that Portal Group + * object SHALL also be removed. The Portal Group object SHALL + * remain registered as long as either of its associated Portal + * or iSCSI Storage Node objects remain registered. If a deleted + * Storage Node or Portal object is subsequently re-registered, + * then a relationship between the re- registered object and + * an existing Portal or Storage Node object registration, + * indicated by the PG object, SHALL be restored. + */ + if (ISNS_IS_ENTITY(obj)) + goto out; + + if (entity == NULL || !ISNS_IS_ENTITY(entity)) + goto out; + + /* Don't do this for the CONTROL entity. */ + if (entity->ie_flags & ISNS_OBJECT_PRIVATE) + goto out; + + /* Step 1: Purge all relationship objects (read: portal groups) + * where both referenced objects are dead. + */ + for (i = 0; i < entity->ie_children.iol_count; ) { + isns_object_t *child = entity->ie_children.iol_data[i]; + + if (child->ie_relation + && isns_relation_is_dead(child->ie_relation)) { + __isns_db_prepare_removal(db, child); + continue; + } + + i += 1; + } + + /* Step 2: If all portals, nodes and PGs have been unregistered, + * the list of children should be empty. */ + if (entity->ie_children.iol_count == 0) { + isns_debug_state("Last portal/node unregistered, removing entity\n"); + __isns_db_prepare_removal(db, entity); + } + +out: + return ISNS_SUCCESS; +} + +/* + * Purge deregistered objects. + * If we find they're still part of some discovery + * domain, they're moved to id_limbo; otherwise we'll + * destroy them for good. + */ +void +isns_db_purge(isns_db_t *db) +{ + isns_object_list_t *list = &db->id_deferred; + unsigned int i; + + while (list->iol_count) { + isns_object_t *obj = list->iol_data[0]; + + if (obj->ie_references == 0) { + isns_debug_state("DB: destroying object %u (%s)\n", + obj->ie_index, + obj->ie_template->iot_name); + + if (db->id_backend) { + db->id_backend->idb_remove(db, obj); + /* db->id_backend->idb_sync(db); */ + } + + isns_object_list_remove(db->id_objects, obj); + obj->ie_state = ISNS_OBJECT_STATE_DEAD; + } else if (obj->ie_state != ISNS_OBJECT_STATE_LIMBO) { + isns_debug_state("DB: moving object %u (%s) to purgatory - " + "%u references left\n", + obj->ie_index, + obj->ie_template->iot_name, + obj->ie_references); + + isns_object_list_append(&db->id_limbo, obj); + obj->ie_state = ISNS_OBJECT_STATE_LIMBO; + isns_object_prune_attrs(obj); + + if (db->id_backend) { + db->id_backend->idb_store(db, obj); + db->id_backend->idb_sync(db); + } + } + + isns_object_list_remove(list, obj); + } + + /* Brute force - look at all objects in limbo and kill those + * that went out of scope */ + for (i = 0; i < db->id_limbo.iol_count; ) { + isns_object_t *obj = db->id_limbo.iol_data[i]; + + if (obj->ie_references == 0) { + isns_debug_state("DB: destroying object %u (%s)\n", + obj->ie_index, + obj->ie_template->iot_name); + + if (db->id_backend) { + db->id_backend->idb_remove(db, obj); + /* db->id_backend->idb_sync(db); */ + } + + obj->ie_state = ISNS_OBJECT_STATE_DEAD; + isns_object_list_remove(&db->id_limbo, obj); + isns_object_list_remove(db->id_objects, obj); + continue; + } + + i += 1; + } +} + +/* + * Expire old entities + * + * This code is still rather simple, but once we start + * using ESI things get rather complex quickly. + */ +time_t +isns_db_expire(isns_db_t *db) +{ + isns_object_list_t *list = db->id_objects; + time_t now = time(NULL), next_timeout; + unsigned int i = 0; + + next_timeout = now + 3600; + if (isns_config.ic_registration_period == 0) + return next_timeout; + + while (i < list->iol_count) { + isns_object_t *obj; + uint64_t stamp; + uint32_t period; + + obj = list->iol_data[i]; + if (!ISNS_IS_ENTITY(obj)) + goto next; + + if (!isns_object_get_uint32(obj, + ISNS_TAG_REGISTRATION_PERIOD, + &period)) { + isns_debug_state("No registration period for entity %u\n", + obj->ie_index); + goto next; + } + + if (!isns_object_get_uint64(obj, + ISNS_TAG_TIMESTAMP, + &stamp)) { + isns_debug_state("No timestamp for entity %u\n", + obj->ie_index); + goto next; + } + + stamp += period; + if (stamp <= now) { + /* removing the object will move one + * object from the tail to the free + * slot in the list. So don't increment + * the index here. */ + isns_debug_state("Expiring entity %u\n", obj->ie_index); + isns_db_remove(db, obj); + goto next; + } else { + isns_debug_state("Entity %u will expire in %u sec\n", + obj->ie_index, (int) (stamp - now)); + if (stamp < next_timeout) + next_timeout = stamp; + } + +next: + i += 1; + } + + /* Send out SCN notifications. + * This makes sure we won't have extraneous references + * on expired objects when we reach db_purge. */ + isns_flush_events(); + + return next_timeout; +} + +/* + * Very special function to make sure we always have a + * CONTROL entity. + */ +isns_object_t * +isns_db_get_control(isns_db_t *db) +{ + isns_attr_list_t keys = ISNS_ATTR_LIST_INIT; + isns_object_list_t *list = db->id_objects; + isns_object_t *found = NULL; + unsigned int i; + + isns_attr_list_append_string(&keys, + ISNS_TAG_ENTITY_IDENTIFIER, + ISNS_ENTITY_CONTROL); + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj; + + obj = list->iol_data[i]; + if (!ISNS_IS_ENTITY(obj)) + continue; + if (isns_object_match(obj, &keys)) { + obj->ie_users++; + found = obj; + goto done; + } + } + + found = isns_create_object(&isns_entity_template, + &keys, NULL); + found->ie_flags |= ISNS_OBJECT_PRIVATE; + isns_db_insert(db, found); + isns_db_sync(db); + +done: + return found; +} + +void +isns_db_get_domainless(isns_db_t *db, + isns_object_template_t *tmpl, + isns_object_list_t *result) +{ + isns_object_list_t *list = db->id_objects; + unsigned int i; + + if (!tmpl) + return; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj->ie_template == tmpl + && isns_bitvector_is_empty(obj->ie_membership)) + isns_object_list_append(result, obj); + } +} + +/* + * Create a relationship and store it in the DB + */ +void +isns_db_create_relation(isns_db_t *db, + isns_object_t *relating_object, + unsigned int relation_type, + isns_object_t *subordinate_object1, + isns_object_t *subordinate_object2) +{ + isns_relation_t *rel; + + rel = isns_create_relation(relating_object, + relation_type, + subordinate_object1, + subordinate_object2); + if (rel) { + isns_relation_add(db->id_relations, rel); + isns_relation_release(rel); + } +} + +/* + * Get all objects related to @left through a relation + * of type @type. + */ +void +isns_db_get_relationship_objects(isns_db_t *db, + const isns_object_t *left, + unsigned int relation_type, + isns_object_list_t *result) +{ + isns_relation_get_edge_objects(db->id_relations, + left, relation_type, + result); +} + +/* + * Get the object relating left and right. + * Usually called to find the portal group connecting + * a portal and a storage node, or a DD connecting + * two storage nodes. + */ +isns_object_t * +isns_db_get_relationship_object(isns_db_t *db, + const isns_object_t *left, + const isns_object_t *right, + unsigned int relation_type) +{ + isns_relation_t *rel; + + /* Find a relation of the given type, connecting + * the two objects. */ + rel = isns_relation_find_edge(db->id_relations, + left, right, relation_type); + + if (rel == NULL) + return NULL; + + return isns_object_get(rel->ir_object); +} + +/* + * See if a relationship exists + */ +int +isns_db_relation_exists(isns_db_t *db, + const isns_object_t *relating_object, + const isns_object_t *left, + const isns_object_t *right, + unsigned int relation_type) +{ + return isns_relation_exists(db->id_relations, + relating_object, + left, right, relation_type); +} + +/* + * Debug helper + */ +void +isns_db_print(isns_db_t *db, isns_print_fn_t *fn) +{ + const isns_object_list_t *list = db->id_objects; + unsigned int i; + + fn("Dumping database contents\n" + "Backend: %s\n" + "Last EID: %u\n" + "Last Index: %u\n" + , + db->id_backend->idb_name, + db->id_last_eid, + db->id_last_index); + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + fn("--------------\n" + "Object: index=%u type=<%s> state=%s", + obj->ie_index, + obj->ie_template->iot_name, + isns_object_state_string(obj->ie_state)); + if (obj->ie_container) + fn(" parent=%u", obj->ie_container->ie_index); + if (obj->ie_flags & ISNS_OBJECT_DIRTY) + fn(" DIRTY"); + if (obj->ie_flags & ISNS_OBJECT_PRIVATE) + fn(" PRIVATE"); + fn("\n"); + isns_attr_list_print(&obj->ie_attrs, fn); + } +} + +/* + * Generate a "random" entity identifier. This is used when + * a DevAttrReg request does not specify an entity, and the + * client's policy doesn't specify one either. + */ +const char * +isns_db_generate_eid(isns_db_t *db, char *buf, size_t size) +{ + snprintf(buf, size, "isns.entity.%04d", db->id_last_eid); + db->id_last_eid++; + return buf; +} + +/* + * Highly primitive transaction handling. + * This is really just a hack for the iSNS server code, + * which wants to go along creating objects, and back out + * if something goes wrong. + */ +void +isns_db_begin_transaction(isns_db_t *db) +{ + if (db->id_in_transaction) { + isns_error("isns_db_begin_transaction: running into pending transaction\n"); + isns_db_rollback(db); + } + db->id_in_transaction = 1; +} + +void +isns_db_commit(isns_db_t *db) +{ + /* Nothing yet */ + db->id_in_transaction = 0; +} + +void +isns_db_rollback(isns_db_t *db) +{ + /* Nothing yet */ + db->id_in_transaction = 0; +} diff --git a/utils/open-isns/db.h b/utils/open-isns/db.h new file mode 100644 index 0000000..148d930 --- /dev/null +++ b/utils/open-isns/db.h @@ -0,0 +1,147 @@ +/* + * iSNS object database + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_DB_H +#define ISNS_DB_H + +#include "attrs.h" + +typedef struct isns_db_backend isns_db_backend_t; + +/* + * In-memory portion of object database. + * Stable storage is provided by different + * backends. + */ +struct isns_db { + isns_object_list_t * id_objects; + isns_object_list_t __id_objects; + + isns_relation_soup_t * id_relations; + + uint32_t id_last_eid; + uint32_t id_last_index; + + isns_scope_t * id_global_scope; + isns_scope_t * id_default_scope; + + isns_db_backend_t * id_backend; + + unsigned int id_in_transaction : 1; + struct isns_db_trans * id_transact; + + /* This is for objects in limbo. When a client + * calls DevAttrDereg, the object will first be + * placed on the id_deferred list. + * When we're done processing the message, we + * invoke isns_db_purge, which looks at these + * objects. + * - if the reference count is 1, the object + * is deleted. + * - otherwise, we assume the object is referenced + * by a discovery domain. In this case, we prune + * the attribute list down to the key attr(s) + * plus the index attribute, and move it to + * the id_limbo list. + */ + isns_object_list_t id_deferred; + isns_object_list_t id_limbo; +}; + + +struct isns_db_backend { + char * idb_name; + + int (*idb_reload)(isns_db_t *); + int (*idb_sync)(isns_db_t *); + int (*idb_store)(isns_db_t *, + const isns_object_t *); + int (*idb_remove)(isns_db_t *, + const isns_object_t *); +}; + +extern isns_db_backend_t *isns_create_file_db_backend(const char *); +extern isns_object_t * __isns_db_get_next(const isns_object_list_t *, + isns_object_template_t *, + const isns_attr_list_t *, + const isns_attr_list_t *); + +extern isns_relation_soup_t *isns_relation_soup_alloc(void); +extern isns_relation_t *isns_create_relation(isns_object_t *relating_object, + unsigned int relation_type, + isns_object_t *subordinate_object1, + isns_object_t *subordinate_object2); +extern void isns_relation_sever(isns_relation_t *); +extern void isns_relation_release(isns_relation_t *); +extern void isns_relation_add(isns_relation_soup_t *, + isns_relation_t *); +extern void isns_relation_remove(isns_relation_soup_t *, + isns_relation_t *); +extern isns_object_t * isns_relation_get_other(const isns_relation_t *, + const isns_object_t *); +extern isns_relation_t *isns_relation_find_edge(isns_relation_soup_t *, + const isns_object_t *, + const isns_object_t *, + unsigned int); +extern void isns_relation_halfspace(isns_relation_soup_t *, + const isns_object_t *, + unsigned int, + isns_object_list_t *); +extern void isns_relation_get_edge_objects(isns_relation_soup_t *, + const isns_object_t *, + unsigned int, + isns_object_list_t *); +extern int isns_relation_exists(isns_relation_soup_t *, + const isns_object_t *relating_object, + const isns_object_t *left, + const isns_object_t *right, + unsigned int relation_type); +extern int isns_relation_is_dead(const isns_relation_t *); + +extern void isns_db_create_relation(isns_db_t *db, + isns_object_t *relating_object, + unsigned int relation_type, + isns_object_t *subordinate_object1, + isns_object_t *subordinate_object2); +extern void isns_db_get_relationship_objects(isns_db_t *, + const isns_object_t *, + unsigned int relation_type, + isns_object_list_t *); +extern isns_object_t * isns_db_get_relationship_object(isns_db_t *, + const isns_object_t *, + const isns_object_t *, + unsigned int relation_type); +extern int isns_db_relation_exists(isns_db_t *db, + const isns_object_t *relating_object, + const isns_object_t *left, + const isns_object_t *right, + unsigned int relation_type); +extern int isns_db_create_pg_relation(isns_db_t *, + isns_object_t *); + +extern isns_scope_t * isns_scope_for_call(isns_db_t *, const isns_simple_t *); +extern isns_scope_t * isns_scope_alloc(isns_db_t *); +extern void isns_scope_release(isns_scope_t *); +extern void isns_scope_add(isns_scope_t *, + isns_object_t *); +extern int isns_scope_remove(isns_scope_t *, + isns_object_t *); +extern int isns_scope_gang_lookup(isns_scope_t *, + isns_object_template_t *, + const isns_attr_list_t *, + isns_object_list_t *); +extern isns_object_t * isns_scope_get_next(isns_scope_t *, + isns_object_template_t *, + const isns_attr_list_t *current, + const isns_attr_list_t *scope); +extern void isns_scope_get_related(isns_scope_t *, + const isns_object_t *, + unsigned int, + isns_object_list_t *); +extern isns_db_t * isns_scope_get_db(const isns_scope_t *); + + +#endif /* ISNS_DB_H */ diff --git a/utils/open-isns/dd.c b/utils/open-isns/dd.c new file mode 100644 index 0000000..b392036 --- /dev/null +++ b/utils/open-isns/dd.c @@ -0,0 +1,1307 @@ +/* + * Handle DD registration/deregistration + * + * Discovery domains are weird, even in the context of + * iSNS. For once thing, all other objects have unique + * attributes; DDs attributes can appear several times. + * They should really have made each DD member an object + * in its own right. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "objects.h" +#include "message.h" +#include "security.h" +#include "util.h" +#include "db.h" + +#define DD_DEBUG + +enum { + ISNS_DD_MEMBER_ISCSI_NODE = 1, + ISNS_DD_MEMBER_IFCP_NODE, + ISNS_DD_MEMBER_PORTAL, +}; +/* Must be zero/one: */ +enum { + NOTIFY_MEMBER_ADDED = 0, + NOTIFY_MEMBER_REMOVED = 1 +}; + +typedef struct isns_dd isns_dd_t; +typedef struct isns_dd_list isns_dd_list_t; +typedef struct isns_dd_member isns_dd_member_t; + +struct isns_dd { + uint32_t dd_id; + char * dd_name; + uint32_t dd_features; + isns_dd_member_t * dd_members; + + unsigned int dd_inserted : 1; + + isns_object_t * dd_object; +}; + +struct isns_dd_member { + isns_dd_member_t * ddm_next; + unsigned int ddm_type; + isns_object_ref_t ddm_object; + + unsigned int ddm_added : 1; + union { + uint32_t ddm_index; + + /* Index must be first in all structs below. + * Yeah, I know. Aliasing is bad. */ + struct isns_dd_portal { + uint32_t index; + isns_portal_info_t info; + } ddm_portal; + struct isns_dd_iscsi_node { + uint32_t index; + char * name; + } ddm_iscsi_node; + struct isns_dd_ifcp_node { + uint32_t index; + char * name; + } ddm_ifcp_node; + }; +}; + +struct isns_dd_list { + unsigned int ddl_count; + isns_dd_t ** ddl_data; +}; + +/* + * List of all discovery domains. + * This duplicates the DD information from the database, + * but unfortunately this can't be helped - we need to + * have fast algorithms to compute the membership of a + * node, and the relative visibility of two nodes. + */ +static int isns_dd_list_initialized = 0; +static isns_dd_list_t isns_dd_list; +static uint32_t isns_dd_next_id = 1; + +static isns_dd_t * isns_dd_alloc(void); +static isns_dd_t * isns_dd_clone(const isns_dd_t *); +static void isns_dd_release(isns_dd_t *); +static int isns_dd_parse_attrs(isns_dd_t *, + isns_db_t *, const isns_attr_list_t *, + const isns_dd_t *, int); +static int isns_dd_remove_members(isns_dd_t *, + isns_db_t *, + isns_dd_t *); +static void isns_dd_notify(const isns_dd_t *, + isns_dd_member_t *, + isns_dd_member_t *, + int); +static void isns_dd_add_members(isns_dd_t *, + isns_db_t *, + isns_dd_t *); +static void isns_dd_store(isns_db_t *, const isns_dd_t *, int); +static void isns_dd_destroy(isns_db_t *, isns_dd_t *); +static void isns_dd_insert(isns_dd_t *); +static isns_dd_t * isns_dd_by_id(uint32_t); +static isns_dd_t * isns_dd_by_name(const char *); +static isns_dd_member_t * isns_dd_create_member(isns_object_t *); +static inline void isns_dd_member_free(isns_dd_member_t *); +static int isns_dd_remove_member(isns_dd_t *, isns_object_t *); +static void isns_dd_list_resize(isns_dd_list_t *, unsigned int); +static void isns_dd_list_insert(isns_dd_list_t *, isns_dd_t *); +static void isns_dd_list_remove(isns_dd_list_t *, isns_dd_t *); + +static isns_object_t * isns_dd_get_member_object(isns_db_t *, + const isns_attr_t *, const isns_attr_t *, + int); + +/* + * Create DDReg messages + */ +isns_simple_t * +isns_create_dd_registration(isns_client_t *clnt, const isns_attr_list_t *attrs) +{ + isns_simple_t *msg; + isns_attr_t *id_attr; + + msg = isns_simple_create(ISNS_DD_REGISTER, clnt->ic_source, NULL); + if (msg == NULL) + return NULL; + + /* If the caller specified a DD_ID, use it in the + * message key. */ + if (isns_attr_list_get_attr(attrs, ISNS_TAG_DD_ID, &id_attr)) + isns_attr_list_append_attr(&msg->is_message_attrs, id_attr); + + isns_attr_list_copy(&msg->is_operating_attrs, attrs); + return msg; +} + +isns_simple_t * +isns_create_dd_deregistration(isns_client_t *clnt, + uint32_t dd_id, const isns_attr_list_t *attrs) +{ + isns_simple_t *msg; + + msg = isns_simple_create(ISNS_DD_DEREGISTER, clnt->ic_source, NULL); + if (msg == NULL) + return NULL; + + isns_attr_list_append_uint32(&msg->is_message_attrs, + ISNS_TAG_DD_ID, dd_id); + + isns_attr_list_copy(&msg->is_operating_attrs, attrs); + return msg; +} + +/* + * Process a DD registration + */ +int +isns_process_dd_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_simple_t *reply = NULL; + isns_attr_list_t *keys = &call->is_message_attrs; + isns_attr_list_t *attrs = &call->is_operating_attrs; + isns_db_t *db = srv->is_db; + isns_dd_t *dd = NULL, *temp_dd = NULL; + isns_attr_t *attr; + uint32_t id = 0; + int status; + + /* + * 5.6.5.9. + * The Message Key, if used, contains the DD_ID of the Discovery + * Domain to be registered. If the Message Key contains a DD_ID + * of an existing DD entry in the iSNS database, then the DDReg + * message SHALL attempt to update the existing entry. If the + * DD_ID in the Message Key (if used) does not match an existing + * DD entry, then the iSNS server SHALL reject the DDReg message + * with a status code of 3 (Invalid Registration). + */ + switch (keys->ial_count) { + case 0: + /* Security: check if the client is allowed to + * create a discovery domain */ + if (!isns_policy_validate_object_creation(call->is_policy, + call->is_source, + &isns_dd_template, + keys, attrs, + call->is_function)) + goto unauthorized; + break; + + case 1: + attr = keys->ial_data[0]; + if (attr->ia_tag_id != ISNS_TAG_DD_ID) + goto reject; + if (ISNS_ATTR_IS_NIL(attr)) + break; + if (!ISNS_ATTR_IS_UINT32(attr)) + goto reject; + + id = attr->ia_value.iv_uint32; + if (id == 0) + goto reject; + + dd = isns_dd_by_id(id); + if (dd == NULL) { + isns_debug_state("DDReg for unknown ID=%u\n", id); + goto reject; + } + + /* Security: check if the client is allowed to + * mess with this DD. */ + isns_assert(dd->dd_object); + if (!isns_policy_validate_object_update(call->is_policy, + call->is_source, + dd->dd_object, attrs, + call->is_function)) + goto unauthorized; + + break; + + default: + goto reject; + } + + temp_dd = isns_dd_alloc(); + + /* Parse the attributes and build a DD object. */ + status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 1); + if (status != ISNS_SUCCESS) + goto out; + + if (dd == NULL) { + /* Create the DD, and copy the general information + * such asn features and symbolic name from temp_dd */ + dd = isns_dd_clone(temp_dd); + + /* Don't assign the attrs to the DD right away. + * First and foremost, they may be unsorted. Second, + * we really want to hand-pick through them due to + * the weird semantics mandated by the RFC. */ + dd->dd_object = isns_create_object(&isns_dd_template, NULL, NULL); + if (dd->dd_object == NULL) + goto reject; + + /* Insert new domain into database */ + isns_db_insert(db, dd->dd_object); + + /* Add it to the internal list. Assign DD_ID and + * symbolic name if none were given. + */ + isns_dd_insert(dd); + } else { + if (!dd->dd_id) + dd->dd_id = temp_dd->dd_id; + dd->dd_features = temp_dd->dd_features; + isns_assign_string(&dd->dd_name, temp_dd->dd_name); + } + + /* Send notifications. This must be done before merging + * the list of new members into the DD. + */ + isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members, + NOTIFY_MEMBER_ADDED); + + /* Update the DD */ + isns_dd_add_members(dd, db, temp_dd); + + /* And add it to the database. */ + isns_dd_store(db, dd, 0); + + reply = isns_simple_create(ISNS_DD_REGISTER, srv->is_source, NULL); + isns_object_extract_all(dd->dd_object, &reply->is_operating_attrs); + + status = ISNS_SUCCESS; + +out: + isns_dd_release(temp_dd); + isns_dd_release(dd); + *result = reply; + return status; + +reject: + status = ISNS_INVALID_REGISTRATION; + goto out; + +unauthorized: + status = ISNS_SOURCE_UNAUTHORIZED; + goto out; +} + +/* + * Process a DD deregistration + */ +int +isns_process_dd_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_simple_t *reply = NULL; + isns_attr_list_t *keys = &call->is_message_attrs; + isns_attr_list_t *attrs = &call->is_operating_attrs; + isns_db_t *db = srv->is_db; + isns_dd_t *dd = NULL, *temp_dd = NULL; + isns_attr_t *attr; + uint32_t id = 0; + int status; + + /* + * 5.6.5.10. + * The Message Key Attribute for a DDDereg message is the DD + * ID for the Discovery Domain being removed or having members + * removed. + */ + if (keys->ial_count != 1) + goto reject; + + attr = keys->ial_data[0]; + if (attr->ia_tag_id != ISNS_TAG_DD_ID + || ISNS_ATTR_IS_NIL(attr) + || !ISNS_ATTR_IS_UINT32(attr)) + goto reject; + + id = attr->ia_value.iv_uint32; + if (id == 0) + goto reject; + + dd = isns_dd_by_id(id); + if (dd == NULL) + goto reject; + + /* Security: check if the client is permitted to + * modify the DD object. + */ + if (!isns_policy_validate_object_update(call->is_policy, + call->is_source, + dd->dd_object, attrs, + call->is_function)) + goto unauthorized; + + /* + * 5.6.5.10. + * If the DD ID matches an existing DD and there are + * no Operating Attributes, then the DD SHALL be removed and a + * success Status Code returned. Any existing members of that + * DD SHALL remain in the iSNS database without membership in + * the just-removed DD. + */ + if (attrs->ial_count == 0) { + isns_dd_member_t *mp; + + /* Zap the membership bit */ + for (mp = dd->dd_members; mp; mp = mp->ddm_next) { + isns_object_t *obj = mp->ddm_object.obj; + + isns_object_clear_membership(obj, dd->dd_id); + } + + /* Notify all DD members that they will lose the other + * nodes. */ + isns_dd_notify(dd, NULL, dd->dd_members, NOTIFY_MEMBER_REMOVED); + + isns_dd_destroy(db, dd); + } else { + /* Parse the attributes and build a temporary DD object. */ + temp_dd = isns_dd_alloc(); + status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 0); + if (status != ISNS_SUCCESS) + goto out; + + /* Update the DD object */ + status = isns_dd_remove_members(dd, db, temp_dd); + if (status != ISNS_SUCCESS) + goto out; + + /* Send notifications. This must be done before after + * updating the DD. + */ + isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members, + NOTIFY_MEMBER_REMOVED); + + /* Store it in the database. */ + isns_dd_store(db, dd, 1); + } + + reply = isns_simple_create(ISNS_DD_DEREGISTER, srv->is_source, NULL); + status = ISNS_SUCCESS; + +out: + isns_dd_release(temp_dd); + isns_dd_release(dd); + *result = reply; + return status; + +reject: + status = ISNS_INVALID_DEREGISTRATION; + goto out; + +unauthorized: + status = ISNS_SOURCE_UNAUTHORIZED; + goto out; +} + +static isns_dd_t * +isns_dd_alloc(void) +{ + return isns_calloc(1, sizeof(isns_dd_t)); +} + +/* + * Allocate a clone of the orig_dd, but without + * copying the members. + */ +static isns_dd_t * +isns_dd_clone(const isns_dd_t *orig_dd) +{ + isns_dd_t *dd; + + dd = isns_dd_alloc(); + + dd->dd_id = orig_dd->dd_id; + dd->dd_features = orig_dd->dd_features; + dd->dd_object = isns_object_get(orig_dd->dd_object); + isns_assign_string(&dd->dd_name, orig_dd->dd_name); + + return dd; +} + +static void +isns_dd_release(isns_dd_t *dd) +{ + isns_dd_member_t *member; + + if (dd == NULL || dd->dd_inserted) + return; + + while ((member = dd->dd_members) != NULL) { + dd->dd_members = member->ddm_next; + isns_dd_member_free(member); + } + + if (dd->dd_object) + isns_object_release(dd->dd_object); + + isns_free(dd->dd_name); + isns_free(dd); +} + +static isns_dd_member_t * +isns_dd_create_member(isns_object_t *obj) +{ + isns_dd_member_t *new; + + new = isns_calloc(1, sizeof(*new)); + new->ddm_added = 1; + + if (ISNS_IS_ISCSI_NODE(obj)) + new->ddm_type = ISNS_DD_MEMBER_ISCSI_NODE; + else if (ISNS_IS_PORTAL(obj)) + new->ddm_type = ISNS_DD_MEMBER_PORTAL; + else if (ISNS_IS_FC_NODE(obj)) + new->ddm_type = ISNS_DD_MEMBER_IFCP_NODE; + else { + isns_free(new); + return NULL; + } + + isns_object_reference_set(&new->ddm_object, obj); + return new; +} + +static inline void +isns_dd_member_free(isns_dd_member_t *member) +{ + switch (member->ddm_type) { + case ISNS_DD_MEMBER_ISCSI_NODE: + isns_free(member->ddm_iscsi_node.name); + break; + + case ISNS_DD_MEMBER_IFCP_NODE: + isns_free(member->ddm_ifcp_node.name); + break; + } + + isns_object_reference_drop(&member->ddm_object); + isns_free(member); +} + +void +isns_dd_get_members(uint32_t dd_id, isns_object_list_t *list, int active_only) +{ + isns_dd_t *dd; + isns_dd_member_t *mp; + + dd = isns_dd_by_id(dd_id); + if (dd == NULL) + return; + + for (mp = dd->dd_members; mp; mp = mp->ddm_next) { + isns_object_t *obj = mp->ddm_object.obj; + + if (active_only + && obj->ie_state != ISNS_OBJECT_STATE_MATURE) + continue; + + isns_object_list_append(list, obj); + } +} + +/* + * Helper function to remove a member referencing the given object + */ +static int +isns_dd_remove_member(isns_dd_t *dd, isns_object_t *obj) +{ + isns_dd_member_t *mp, **pos; + + pos = &dd->dd_members; + while ((mp = *pos) != NULL) { + if (mp->ddm_object.obj == obj) { + *pos = mp->ddm_next; + isns_dd_member_free(mp); + return 1; + } else { + pos = &mp->ddm_next; + } + } + + return 0; +} + +static void +isns_dd_insert(isns_dd_t *dd) +{ + if (dd->dd_inserted) + return; + + if (dd->dd_id == 0) { + uint32_t id = isns_dd_next_id; + unsigned int i; + + for (i = 0; i < isns_dd_list.ddl_count; ++i) { + isns_dd_t *cur = isns_dd_list.ddl_data[i]; + + if (cur->dd_id > id) + break; + if (cur->dd_id == id) + ++id; + } + isns_debug_state("Allocated new DD_ID %d\n", id); + dd->dd_id = id; + isns_dd_next_id = id + 1; + } + + /* + * When creating a new DD, if the DD_Symbolic_Name is + * not included in the Operating Attributes, or if it + * is included with a zero-length TLV, then the iSNS + * server SHALL provide a unique DD_Symbolic_Name value + * for the created DD. The assigned DD_Symbolic_Name + * value SHALL be returned in the DDRegRsp message. + */ + if (dd->dd_name == NULL) { + char namebuf[64]; + + snprintf(namebuf, sizeof(namebuf), "isns.dd%u", dd->dd_id); + isns_assign_string(&dd->dd_name, namebuf); + } + + isns_dd_list_insert(&isns_dd_list, dd); + dd->dd_inserted = 1; + +#ifdef DD_DEBUG + /* Safety first - make sure domains are sorted by DD_ID */ + { + unsigned int i, prev_id = 0; + + for (i = 0; i < isns_dd_list.ddl_count; ++i) { + isns_dd_t *cur = isns_dd_list.ddl_data[i]; + + isns_assert(cur->dd_id > prev_id); + prev_id = cur->dd_id; + } + } +#endif +} + +/* + * Resize the DD list + */ +#define LIST_SIZE(n) (((n) + 15) & ~15) +void +isns_dd_list_resize(isns_dd_list_t *list, unsigned int last_index) +{ + unsigned int new_size, cur_size; + isns_dd_t **new_data; + + cur_size = LIST_SIZE(list->ddl_count); + new_size = LIST_SIZE(last_index + 1); + if (new_size < list->ddl_count) + return; + + /* We don't use realloc here because we need + * to zero the new pointers anyway. */ + new_data = isns_calloc(new_size, sizeof(void *)); + isns_assert(new_data); + + memcpy(new_data, list->ddl_data, + list->ddl_count * sizeof(void *)); + isns_free(list->ddl_data); + + list->ddl_data = new_data; + list->ddl_count = last_index + 1; +} + +/* + * Find the insert position for a given DD ID. + * returns true iff the DD was found in the list. + */ +static int +__isns_dd_list_find_pos(isns_dd_list_t *list, unsigned int id, + unsigned int *where) +{ + unsigned int hi, lo, md; + + lo = 0; + hi = list->ddl_count; + + /* binary search */ + while (lo < hi) { + isns_dd_t *cur; + + md = (lo + hi) / 2; + cur = list->ddl_data[md]; + + if (id == cur->dd_id) { + *where = md; + return 1; + } + + if (id < cur->dd_id) { + hi = md; + } else { + lo = md + 1; + } + } + + *where = hi; + return 0; +} + +/* + * In-order insert + */ +static void +isns_dd_list_insert(isns_dd_list_t *list, isns_dd_t *dd) +{ + unsigned int pos; + + if (__isns_dd_list_find_pos(list, dd->dd_id, &pos)) { + isns_error("Internal error in %s: DD already listed\n", + __FUNCTION__); + return; + } + + isns_dd_list_resize(list, list->ddl_count); + /* Shift the tail of the list to make room for new entry. */ + memmove(list->ddl_data + pos + 1, + list->ddl_data + pos, + (list->ddl_count - pos - 1) * sizeof(void *)); + list->ddl_data[pos] = dd; +} + +/* + * Remove DD from list + */ +void +isns_dd_list_remove(isns_dd_list_t *list, isns_dd_t *dd) +{ + unsigned int pos; + + if (!__isns_dd_list_find_pos(list, dd->dd_id, &pos)) + return; + + /* Shift the tail of the list */ + memmove(list->ddl_data + pos, + list->ddl_data + pos + 1, + (list->ddl_count - pos - 1) * sizeof(void *)); + list->ddl_count -= 1; +} + +isns_dd_t * +isns_dd_by_id(uint32_t id) +{ + unsigned int i; + + for (i = 0; i < isns_dd_list.ddl_count; ++i) { + isns_dd_t *dd = isns_dd_list.ddl_data[i]; + + if (dd && dd->dd_id == id) + return dd; + } + + return NULL; +} + +static isns_dd_t * +isns_dd_by_name(const char *name) +{ + unsigned int i; + + for (i = 0; i < isns_dd_list.ddl_count; ++i) { + isns_dd_t *dd = isns_dd_list.ddl_data[i]; + + if (dd && !strcmp(dd->dd_name, name)) + return dd; + } + + return NULL; +} + +/* + * Validate the operating attributes, which is surprisingly + * tedious for DDs. It appears as if the whole DD/DDset + * stuff has been slapped onto iSNS as an afterthought. + * + * DDReg has some funky rules about how eg iSCSI nodes + * can be identified by either name or index, and how they + * relate to each other. Unfortunately, the RFC is very vague + * in describing how to treat DDReg message that mix these + * two types of identification, except by saying they + * need to be consistent. + */ +static int +isns_dd_parse_attrs(isns_dd_t *dd, isns_db_t *db, + const isns_attr_list_t *attrs, + const isns_dd_t *orig_dd, + int is_registration) +{ + isns_dd_member_t **tail; + const isns_dd_t *conflict; + unsigned int i; + int rv = ISNS_SUCCESS; + + if (orig_dd) { + dd->dd_id = orig_dd->dd_id; + dd->dd_features = orig_dd->dd_features; + isns_assign_string(&dd->dd_name, orig_dd->dd_name); + } + + isns_assert(dd->dd_members == NULL); + tail = &dd->dd_members; + + for (i = 0; i < attrs->ial_count; ++i) { + isns_object_t *obj = NULL; + isns_attr_t *attr, *next = NULL; + const char *name; + uint32_t id; + + attr = attrs->ial_data[i]; + + if (!isns_object_attr_valid(&isns_dd_template, attr->ia_tag_id)) + return ISNS_INVALID_REGISTRATION; + + switch (attr->ia_tag_id) { + case ISNS_TAG_DD_ID: + /* Ignore this attribute in DDDereg messages */ + if (!is_registration) + continue; + + /* + * 5.6.5.9. + * A DDReg message with no Message Key SHALL result + * in the attempted creation of a new Discovery Domain + * (DD). If the DD_ID attribute (with non-zero length) + * is included among the Operating Attributes in the + * DDReg message, then the new Discovery Domain SHALL be + * assigned the value contained in that DD_ID attribute. + * + * If the DD_ID is included in both the Message + * Key and Operating Attributes, then the DD_ID + * value in the Message Key MUST be the same as + * the DD_ID value in the Operating Attributes. + * + * Implementer's note: It's not clear why the standard + * makes an exception for the DD_ID, while all other + * index attributes are read-only. + */ + if (ISNS_ATTR_IS_NIL(attr)) + break; + + id = attr->ia_value.iv_uint32; + if (dd->dd_id != 0) { + if (dd->dd_id != id) + goto invalid; + } else if ((conflict = isns_dd_by_id(id)) != NULL) { + isns_debug_state("DDReg: requested ID %d " + "clashes with existing DD (%s)\n", + id, conflict->dd_name); + goto invalid; + } + dd->dd_id = id; + break; + + case ISNS_TAG_DD_SYMBOLIC_NAME: + /* Ignore this attribute in DDDereg messages */ + if (!is_registration) + continue; + + /* + * If the DD_Symbolic_Name is an operating + * attribute and its value is unique (i.e., it + * does not match the registered DD_Symbolic_Name + * for another DD), then the value SHALL be stored + * in the iSNS database as the DD_Symbolic_Name + * for the specified Discovery Domain. If the + * value for the DD_Symbolic_Name is not unique, + * then the iSNS server SHALL reject the attempted + * DD registration with a status code of 3 + * (Invalid Registration). + */ + if (ISNS_ATTR_IS_NIL(attr)) + break; + + name = attr->ia_value.iv_string; + if (dd->dd_name && strcmp(name, dd->dd_name)) { + isns_debug_state("DDReg: symbolic name conflict: " + "id=%d name=%s requested=%s\n", + dd->dd_id, dd->dd_name, name); + goto invalid; + } + if (dd->dd_name) + break; + + if ((conflict = isns_dd_by_name(name)) != NULL) { + isns_debug_state("DDReg: requested symbolic name (%s) " + "clashes with existing DD (id=%d)\n", + name, conflict->dd_id); + goto invalid; + } + isns_assign_string(&dd->dd_name, name); + break; + + case ISNS_TAG_DD_FEATURES: + /* Ignore this attribute in DDDereg messages */ + if (!is_registration) + continue; + + /* + * When creating a new DD, if the DD_Features + * attribute is not included in the Operating + * Attributes, then the iSNS server SHALL assign + * the default value. The default value for + * DD_Features is 0. + */ + if (ISNS_ATTR_IS_UINT32(attr)) + dd->dd_features = attr->ia_value.iv_uint32; + break; + + case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR: + /* portal address must be followed by port */ + if (i + 1 >= attrs->ial_count) + goto invalid; + + next = attrs->ial_data[i + 1]; + if (next->ia_tag_id != ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT) + goto invalid; + i += 1; + /* fallthru to normal case */ + + case ISNS_TAG_DD_MEMBER_PORTAL_INDEX: + case ISNS_TAG_DD_MEMBER_ISCSI_INDEX: + case ISNS_TAG_DD_MEMBER_ISCSI_NAME: + case ISNS_TAG_DD_MEMBER_FC_PORT_NAME: + if (ISNS_ATTR_IS_NIL(attr)) + goto invalid; + + obj = isns_dd_get_member_object(db, + attr, next, + is_registration); + /* For a DD deregistration, it's okay if the + * object does not exist. */ + if (obj == NULL && is_registration) + goto invalid; + break; + + invalid: + rv = ISNS_INVALID_REGISTRATION; + continue; + + } + + if (obj) { + if (is_registration + && isns_object_test_membership(obj, dd->dd_id)) { + /* Duplicates are ignored */ + isns_debug_state("Ignoring duplicate DD registration " + "for %s %u\n", + obj->ie_template->iot_name, + obj->ie_index); + } else { + /* This just adds the member to the temporary DD object, + * without changing any state in the database. */ + isns_dd_member_t *new; + + new = isns_dd_create_member(obj); + if (new) { + *tail = new; + tail = &new->ddm_next; + } + } + isns_object_release(obj); + } + } + + return rv; +} + +/* + * Helper function: extract live nodes from the DD member list + */ +static inline void +isns_dd_get_member_nodes(isns_dd_member_t *members, isns_object_list_t *result) +{ + isns_dd_member_t *mp; + + /* Extract iSCSI nodes from both list. */ + for (mp = members; mp; mp = mp->ddm_next) { + isns_object_t *obj = mp->ddm_object.obj; + + if (ISNS_IS_ISCSI_NODE(obj) + && obj->ie_state == ISNS_OBJECT_STATE_MATURE) + isns_object_list_append(result, obj); + } +} + +void +isns_dd_notify(const isns_dd_t *dd, isns_dd_member_t *unchanged, + isns_dd_member_t *changed, int removed) +{ + isns_object_list_t dd_objects = ISNS_OBJECT_LIST_INIT; + isns_object_list_t changed_objects = ISNS_OBJECT_LIST_INIT; + unsigned int i, j, event; + + /* Extract iSCSI nodes from both list. */ + isns_dd_get_member_nodes(unchanged, &dd_objects); + isns_dd_get_member_nodes(changed, &changed_objects); + + /* Send a management SCN multicast to all + * control nodes that care. */ + event = removed? ISNS_SCN_DD_MEMBER_REMOVED_MASK : ISNS_SCN_DD_MEMBER_ADDED_MASK; + for (i = 0; i < changed_objects.iol_count; ++i) { + isns_object_t *obj = changed_objects.iol_data[i]; + + isns_object_event(obj, + event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK, + dd->dd_object); + } + +#ifdef notagoodidea + /* Not sure - it may be good to send OBJECT ADDED/REMOVED instead + * of the DD membership messages. However, right now the SCN code + * will nuke all SCN registrations for a node when it sees a + * REMOVE event for it. + */ + event = removed? ISNS_SCN_OBJECT_REMOVED_MASK : ISNS_SCN_OBJECT_ADDED_MASK; +#endif + + /* If we added an iscsi node, loop over all members + * and send unicast events to each iscsi node, + * informing them that a new member has been added/removed. + */ + for (j = 0; j < changed_objects.iol_count; ++j) { + isns_object_t *changed = changed_objects.iol_data[j]; + + for (i = 0; i < dd_objects.iol_count; ++i) { + isns_object_t *obj = dd_objects.iol_data[i]; + + /* For member removal, do not send notifications + * if the two nodes are still visible to each + * other through a different discovery domain */ + if (removed && isns_object_test_visibility(obj, changed)) + continue; + + /* Inform the old node that the new node was + * added/removed. */ + isns_unicast_event(obj, changed, event, NULL); + + /* Inform the new node that the old node became + * (in)accessible to it. */ + isns_unicast_event(changed, obj, event, NULL); + } + + /* Finally, inform each changed node of the other + * DD members that became (in)accessible to it. */ + for (i = 0; i < changed_objects.iol_count; ++i) { + isns_object_t *obj = changed_objects.iol_data[i]; + + if (obj == changed) + continue; + + if (removed && isns_object_test_visibility(obj, changed)) + continue; + + isns_unicast_event(changed, obj, event, NULL); + } + } +} + +void +isns_dd_add_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *new_dd) +{ + isns_dd_member_t *mp, **tail; + + for (mp = new_dd->dd_members; mp; mp = mp->ddm_next) { + const char *node_name; + isns_object_t *obj = mp->ddm_object.obj; + + /* + * If the Operating Attributes contain a DD + * Member iSCSI Name value for a Storage Node + * that is currently not registered in the iSNS + * database, then the iSNS server MUST allocate an + * unused iSCSI Node Index for that Storage Node. + * The assigned iSCSI Node Index SHALL be returned + * in the DDRegRsp message as the DD Member iSCSI + * Node Index. The allocated iSCSI Node Index + * value SHALL be assigned to the Storage Node + * if and when it registers in the iSNS database. + * [And likewise for portals] + */ + if (obj->ie_index == 0) + isns_db_insert_limbo(db, obj); + mp->ddm_index = obj->ie_index; + + /* Record the fact that the object is a member of + * this DD */ + isns_object_mark_membership(obj, dd->dd_id); + + switch (mp->ddm_type) { + case ISNS_DD_MEMBER_ISCSI_NODE: + if (isns_object_get_string(obj, ISNS_TAG_ISCSI_NAME, &node_name)) + isns_assign_string(&mp->ddm_iscsi_node.name, node_name); + + break; + + case ISNS_DD_MEMBER_IFCP_NODE: + if (isns_object_get_string(obj, ISNS_TAG_FC_PORT_NAME_WWPN, &node_name)) + isns_assign_string(&mp->ddm_ifcp_node.name, node_name); + + break; + + case ISNS_DD_MEMBER_PORTAL: + isns_portal_from_object(&mp->ddm_portal.info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + obj); + break; + } + } + + /* Find the tail of the DD member list */ + tail = &dd->dd_members; + while ((mp = *tail) != NULL) + tail = &mp->ddm_next; + + /* Append the new list of members */ + *tail = new_dd->dd_members; + new_dd->dd_members = NULL; +} + +/* + * Remove members from a DD + */ +int +isns_dd_remove_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *temp_dd) +{ + isns_dd_member_t *mp; + + for (mp = temp_dd->dd_members; mp; mp = mp->ddm_next) { + isns_object_t *obj = mp->ddm_object.obj; + + /* Clear the membership bit. If the object wasn't in this + * DD to begin with, bail out right away. */ + if (!isns_object_clear_membership(obj, dd->dd_id)) { + isns_debug_state("DD dereg: object %d is not in this DD\n", + obj->ie_index); + continue; + } + + if (!isns_dd_remove_member(dd, obj)) + isns_error("%s: DD member not found in internal list\n", + __FUNCTION__); + } + + return ISNS_SUCCESS; +} + +void +isns_dd_store(isns_db_t *db, const isns_dd_t *dd, int rewrite) +{ + isns_object_t *obj = dd->dd_object; + isns_dd_member_t *member; + + if (rewrite) + isns_object_prune_attrs(obj); + + isns_object_set_uint32(obj, ISNS_TAG_DD_ID, dd->dd_id); + isns_object_set_string(obj, ISNS_TAG_DD_SYMBOLIC_NAME, dd->dd_name); + isns_object_set_uint32(obj, ISNS_TAG_DD_FEATURES, dd->dd_features); + + for (member = dd->dd_members; member; member = member->ddm_next) { + struct isns_dd_iscsi_node *node; + struct isns_dd_portal *portal; + + if (!member->ddm_added && !rewrite) + continue; + + switch (member->ddm_type) { + case ISNS_DD_MEMBER_ISCSI_NODE: + node = &member->ddm_iscsi_node; + + isns_object_set_uint32(obj, + ISNS_TAG_DD_MEMBER_ISCSI_INDEX, + node->index); + if (node->name) + isns_object_set_string(obj, + ISNS_TAG_DD_MEMBER_ISCSI_NAME, + node->name); + break; + + case ISNS_DD_MEMBER_PORTAL: + portal = &member->ddm_portal; + + isns_object_set_uint32(obj, + ISNS_TAG_DD_MEMBER_PORTAL_INDEX, + portal->index); + if (portal->info.addr.sin6_family != AF_UNSPEC) { + isns_portal_to_object(&portal->info, + ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR, + ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT, + obj); + } + break; + } + + member->ddm_added = 0; + } +} + +/* + * Destroy a DD + * The caller should call isns_dd_release to free the DD object. + */ +void +isns_dd_destroy(isns_db_t *db, isns_dd_t *dd) +{ + isns_db_remove(db, dd->dd_object); + isns_dd_list_remove(&isns_dd_list, dd); + dd->dd_inserted = 0; +} + +int +isns_dd_load_all(isns_db_t *db) +{ + isns_object_list_t list = ISNS_OBJECT_LIST_INIT; + unsigned int i; + int rc; + + if (isns_dd_list_initialized) + return ISNS_SUCCESS; + + rc = isns_db_gang_lookup(db, &isns_dd_template, NULL, &list); + if (rc != ISNS_SUCCESS) + return rc; + + for (i = 0; i < list.iol_count; ++i) { + isns_object_t *obj = list.iol_data[i]; + isns_dd_t *dd = NULL, *temp_dd = NULL; + isns_dd_member_t *mp; + + temp_dd = isns_dd_alloc(); + + rc = isns_dd_parse_attrs(temp_dd, db, &obj->ie_attrs, NULL, 1); + if (rc) { + if (temp_dd->dd_id == 0) { + isns_error("Problem converting DD object (index 0x%x). No DD_ID\n", + obj->ie_index); + goto next; + } + isns_error("Problem converting DD %u. Proceeding anyway.\n", + temp_dd->dd_id); + } else { + isns_debug_state("Loaded DD %d from database\n", temp_dd->dd_id); + } + + dd = isns_dd_clone(temp_dd); + + dd->dd_object = isns_object_get(obj); + + isns_dd_insert(dd); + isns_dd_add_members(dd, db, temp_dd); + + /* Clear the ddm_added flag for all members, to + * prevent all information from being duplicated + * to the DB on the next DD modification. */ + for (mp = dd->dd_members; mp; mp = mp->ddm_next) + mp->ddm_added = 0; + +next: + isns_dd_release(temp_dd); + } + + isns_object_list_destroy(&list); + isns_dd_list_initialized = 1; + return ISNS_SUCCESS; +} + +isns_object_t * +isns_dd_get_member_object(isns_db_t *db, const isns_attr_t *key1, + const isns_attr_t *key2, + int create) +{ + isns_attr_list_t query = ISNS_ATTR_LIST_INIT; + isns_object_template_t *tmpl = NULL; + isns_object_t *obj; + isns_portal_info_t portal_info; + const char *key_string = NULL; + uint32_t key_index = 0; + + switch (key1->ia_tag_id) { + case ISNS_TAG_DD_MEMBER_ISCSI_INDEX: + key_index = key1->ia_value.iv_uint32; + isns_attr_list_append_uint32(&query, + ISNS_TAG_ISCSI_NODE_INDEX, + key_index); + tmpl = &isns_iscsi_node_template; + break; + + case ISNS_TAG_DD_MEMBER_ISCSI_NAME: + key_string = key1->ia_value.iv_string; + isns_attr_list_append_string(&query, + ISNS_TAG_ISCSI_NAME, + key_string); + tmpl = &isns_iscsi_node_template; + break; + + case ISNS_TAG_DD_MEMBER_FC_PORT_NAME: + key_string = key1->ia_value.iv_string; + isns_attr_list_append_string(&query, + ISNS_TAG_FC_PORT_NAME_WWPN, + key_string); + tmpl = &isns_fc_port_template; + break; + + case ISNS_TAG_DD_MEMBER_PORTAL_INDEX: + key_index = key1->ia_value.iv_uint32; + isns_attr_list_append_uint32(&query, + ISNS_TAG_PORTAL_INDEX, + key_index); + tmpl = &isns_portal_template; + break; + + case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR: + if (!isns_portal_from_attr_pair(&portal_info, key1, key2) + || !isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + &query)) + return NULL; + + key_string = isns_portal_string(&portal_info); + tmpl = &isns_portal_template; + break; + + default: + return NULL; + } + + obj = isns_db_lookup(db, tmpl, &query); + if (!obj && create) { + if (!key_string) { + isns_debug_state("Attempt to register %s DD member " + "with unknown index %u\n", + tmpl->iot_name, key_index); + goto out; + } + + obj = isns_create_object(tmpl, &query, NULL); + if (obj != NULL) + isns_debug_state("Created limbo object for " + "%s DD member %s\n", + tmpl->iot_name, key_string); + } + +out: + isns_attr_list_destroy(&query); + return obj; + +} diff --git a/utils/open-isns/deregister.c b/utils/open-isns/deregister.c new file mode 100644 index 0000000..3a7b7a6 --- /dev/null +++ b/utils/open-isns/deregister.c @@ -0,0 +1,271 @@ +/* + * Handle iSNS Device Deregistration + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "objects.h" +#include "message.h" +#include "security.h" +#include "util.h" +#include "db.h" + +extern isns_source_t * isns_server_source; + + +/* + * Create a registration, and set the source name + */ +static isns_simple_t * +__isns_create_deregistration(isns_source_t *source, const isns_attr_list_t *attrs) +{ + isns_simple_t *simp; + + simp = isns_simple_create(ISNS_DEVICE_DEREGISTER, source, NULL); + if (simp && attrs) + isns_attr_list_copy(&simp->is_operating_attrs, attrs); + return simp; +} + +isns_simple_t * +isns_create_deregistration(isns_client_t *clnt, const isns_attr_list_t *attrs) +{ + return __isns_create_deregistration(clnt->ic_source, attrs); +} + +/* + * Get the next object identified by the operating attrs. + */ +static int +isns_deregistration_get_next_object(isns_db_t *db, + struct isns_attr_list_scanner *st, + isns_object_list_t *result) +{ + isns_object_t *current; + int status; + + status = isns_attr_list_scanner_next(st); + if (status) + return status; + + /* + * 5.6.5.4. + * Valid Operating Attributes for DevDereg + * --------------------------------------- + * Entity Identifier + * Portal IP-Address & Portal TCP/UDP Port + * Portal Index + * iSCSI Name + * iSCSI Index + * FC Port Name WWPN + * FC Node Name WWNN + * + * In other words, deregistration is restricted to Entity, + * portal, and node + */ + if (st->tmpl != &isns_entity_template + && st->tmpl != &isns_iscsi_node_template + && st->tmpl != &isns_portal_template) + return ISNS_INVALID_DEREGISTRATION; + + /* Only key attrs allowed */ + if (st->attrs.ial_count) { + /* MS Initiators send the Entity protocol along + * with the Entity Identifier. */ + isns_debug_protocol("Client included invalid operating attrs " + "with %s:\n", st->tmpl->iot_name); + isns_attr_list_print(&st->attrs, isns_debug_protocol); + /* return ISNS_INVALID_DEREGISTRATION; */ + } + + /* + * 5.6.5.4 + * Attempted deregistration of non-existing entries SHALL not + * be considered an isns_error. + */ + current = isns_db_lookup(db, st->tmpl, &st->keys); + if (current != NULL) { + isns_object_list_append(result, current); + isns_object_release(current); + } + + return ISNS_SUCCESS; +} + +/* + * Extract the list of objects to be deregistered from + * the list of operating attributes. + */ +static int +isns_deregistration_get_objects(isns_simple_t *reg, isns_db_t *db, + isns_object_list_t *result) +{ + struct isns_attr_list_scanner state; + int status = ISNS_SUCCESS; + + isns_attr_list_scanner_init(&state, NULL, ®->is_operating_attrs); + state.index_acceptable = 1; + state.source = reg->is_source; + + while (state.pos < state.orig_attrs.ial_count) { + status = isns_deregistration_get_next_object(db, + &state, result); + + if (status == 0) + continue; + + /* Translate error codes */ + if (status == ISNS_NO_SUCH_ENTRY) + status = ISNS_SUCCESS; + else + if (status == ISNS_INVALID_REGISTRATION) + status = ISNS_INVALID_DEREGISTRATION; + break; + } + + isns_attr_list_scanner_destroy(&state); + return status; +} + +/* + * Process a deregistration + * + * Normally, you would expect that a deregistration removes the + * object from the database, and that's the end of the story. + * Unfortunately, someone added Discovery Domains to the protocol, + * requiring _some_ information to survive as long as an object + * is referenced by a discovery domain. Specifically, we need to + * retain the relationship between key attributes (eg iscsi node + * name) and the object index. + * + * Thus, deregistration consists of the following steps + * - the object is removed from the database's global scope, + * so that it's no longer visible to DB lookups. + * + * - the object is detached from its containing Network + * Entity. + * + * - all attributes except the key attr(s) and the index + * attribute are removed. + */ +int +isns_process_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_simple_t *reply = NULL; + isns_db_t *db = srv->is_db; + int status, dereg_status; + unsigned int i; + + /* Get the objects to deregister */ + status = isns_deregistration_get_objects(call, db, &objects); + if (status != ISNS_SUCCESS) + goto done; + + /* + * 5.6.5.4 + * + * For messages that change the contents of the iSNS database, + * the iSNS server MUST verify that the Source Attribute + * identifies either a Control Node or a Storage Node that is + * a part of the Network Entity containing the added, deleted, + * or modified objects. + */ + /* + * Implementation note: this can be implemented either by + * explicitly checking the object's owner in isns_db_remove + * (which is what we do right now), or by matching only + * those objects that have the right owner anyway. + * + * The latter sounds like a better choice if the client + * uses NIL attributes, because it limits the scope of + * the operation; but then the RFC doesn't say whether + * this kind of deregistration would be valid at all. + */ + + /* Success: create a new simple message, and + * send it in our reply. */ + reply = __isns_create_deregistration(srv->is_source, NULL); + if (reply == NULL) { + status = ISNS_INTERNAL_ERROR; + goto done; + } + + dereg_status = ISNS_SUCCESS; + for (i = 0; i < objects.iol_count; ++i) { + isns_object_t *obj = objects.iol_data[i]; + + /* Policy: check that the client is permitted + * to deregister this object */ + if (!isns_policy_validate_object_access(call->is_policy, + call->is_source, obj, + call->is_function)) + status = ISNS_SOURCE_UNAUTHORIZED; + + if (status == ISNS_SUCCESS) + status = isns_db_remove(db, obj); + if (status != ISNS_SUCCESS) { + /* + * 5.7.5.4 + * + * In the event of an error, this response message + * contains the appropriate status code as well + * as a list of objects from the original DevDereg + * message that were not successfully deregistered + * from the iSNS database. This list of objects + * is contained in the Operating Attributes + * of the DevDeregRsp message. Note that an + * attempted deregistration of a non-existent + * object does not constitute an isns_error, and + * non-existent entries SHALL not be returned + * in the DevDeregRsp message. + */ + /* + * Implementation: right now this doesn't work + * at all, because isns_msg_set_error will + * discard the entire message except for the + * status word. + */ + isns_debug_message("Failed to deregister object: %s (0x%04x)\n", + isns_strerror(status), status); + + isns_object_extract_all(obj, &reply->is_operating_attrs); + dereg_status = status; + continue; + } + + /* + * 5.7.5.4 + * If all Nodes and Portals associated with a Network + * Entity are deregistered, then the Network Entity + * SHALL also be removed. + * [...] + * If both the Portal and iSCSI Storage Node objects + * associated with a Portal Group object are removed, + * then that Portal Group object SHALL also be removed. + * The Portal Group object SHALL remain registered + * as long as either of its associated Portal or + * iSCSI Storage Node objects remain registered. If a + * deleted Storage Node or Portal object is subsequently + * re-registered, then a relationship between the re- + * registered object and an existing Portal or Storage + * Node object registration, indicated by the PG object, + * SHALL be restored. + */ + /* isns_db_remove takes care of removing dead entities, + * and dead portal groups. + */ + } + + if (status == ISNS_SUCCESS) + status = dereg_status; + +done: + isns_object_list_destroy(&objects); + *result = reply; + return status; +} diff --git a/utils/open-isns/doc/isns_config.5 b/utils/open-isns/doc/isns_config.5 new file mode 100644 index 0000000..5fbd26e --- /dev/null +++ b/utils/open-isns/doc/isns_config.5 @@ -0,0 +1,387 @@ +.TH ISNS_CONFIG 8 "11 May 2007" +.SH NAME +isns_config - iSNS configuration file +.SH SYNOPSIS +.B /etc/isns/isnsadm.conf +.br +.B /etc/isns/isnsd.conf +.br +.B /etc/isns/isnsdd.conf + +.SH DESCRIPTION +All Open-iSNS utilities read their configuration +from a file in +.BR /etc/isns . +There is a separate configuration file for each application, +.BR isnsd ", " isnsadm ", and " isnsdd . +The syntax and the set of supported options is identical, +even though some options are specific to e.g. the server. +Unless indicated, options are applicable to all utilities. +.PP +An Open-iSNS configuration file contains keyword-argument pairs, +one per line. All keywords are case insensitive. +.PP +A +.B # +character introduces a comment, which extends until the +end of the line. Empty lines are ignored. +.PP +There are no line continuations, and you cannot use quotes +around arguments. +.PP +Some options specify timeout values, which are given in +units of seconds by default. You can specify an explicit +unit, however, such as +.BR d " (days), +.BR h " (hours), +.BR m " (minutes), or +.BR s " (seconds). +.\" ------------------------------------------------------------------ +.SS Generic Options +.TP +.BR HostName +By default, Open-iSNS applications will retrieve the machine's +hostname using the +.BR gethostname (3) +system call, and use a DNS lookup to look up the canonical name. +Using the +.BR HostName +option, you can overried this. This option is rarely needed. +.TP +.BR SourceName +This option is mandatory for all Open-iSNS applications. +This should be a name which identifies the client uniquely. +There are two readings of RFC 4171; one requires that this +is an iSCSI qualified name such as +.BR iqn.2001-04.com.example.host , +whereas other language in the RFC suggests that this is +pretty much a free-format string that just has to be +unique (using e.g. the client's fully qualified domain name). +.IP +When using DSA authentication, Open-iSNS currently requires the source +name to match the key identifier (SPI) of the client's public +key. +.IP +If left empty, the source name is derived from the client's hostname. +.TP +.BR ServerAddress " (client): +This options specifies the host name or address of +the iSNS server to talk to. It can optionally be followed +by a colon, and a port number. +.IP +Instead of a hostname, IPv4 or IPv6 addresses can be used. +In order to avoid ambiguities, literal +IPv6 addresses must be surrounded by square brackets, +as in +.BR [2001:4e5f::1] . +.IP +When specifying a port number, you can use either the +numeric port, or a string name to be looked up in +.BR /etc/services . +When the port is omitted, it defaults to 3205, the IANA +assigned port number of iSNS. +.IP +If the special string +.B SLP: +is used, the client will try to locate the iSNS server +through SLP. +.TP +.BR SLPRegister " (server): +If set to 1, the iSNS daemon will register itself will +the SLP service. This allows clients to contact the +server without having to configure its address +statically. +.TP +.BR PIDFile " (server): +This specifies the name of the server's PID file, which is +.B /var/run/isnsd.pid +by default. +.\" ------------------------------------------------------------------ +.SS Database Related Options +These options apply to the iSNS server only, and control operation +of the iSNS database. +.TP +.BR Database +This option is used to specify how the database is stored. +Setting this to an absolute path name will make +.B isnsd +keep its database in the specified directory. +.IP +If you leave this empty, +.B isnsd +will keep its database in memory. +This is also the default setting. +.TP +.BR DefaultDiscoveryDomain +iSNS scopes visibility of other nodes using so-called +Discovery Domains. A storage node A will only "see" +storage node B, if both are members of the same +discovery domain. +.IP +So if a storage node is registered which is not part of +any discovery domain, it will not see any other nodes. +.IP +By setting +.BR DefaultDiscoveryDomain=1 , +you can tell isnsd to create a virtual "default discovery domain", which +holds all nodes that are not part of any administratively configured +discovery domain. +.IP +By default, there is no default discovery domain. +.TP +.BR RegistrationPeriod +The iSNS server can purge registered entities after a certain period +of inactivity. This is called the registration period. Clients who +register objects are supposed to refresh their registration within +this period. +.IP +The default value is 1 hour. Setting it to 0 disables expiry +of entities from the database. +.TP +.BR ESIRetries +Open-iSNS is able to monitor the reachability of storage nodes +and their portals by using a protocol feature called ESI +(Entity status inquiry). Clients request ESI monitoring by +registering an ESI port along with each portal. The server +will send ESI messages to these portals at regular intervals. +If the portal fails to reply several times in a row, it is +considered dead, and will be removed from the database. +.IP +.B ESIRetries +specifies the maximum number of attempts the server will make +at contacting the portal before pronouncing it dead. If set +to 0, the server will disable ESI and reject any registrations +that specify an ESI port with an error code of "ESI not +supported". +.IP +The default value is 3. +.TP +.BR ESIMinInterval +This timeout value specifies the minimum ESI interval. +If a client requests an ESI interval less than this value, +it is silently rounded up. +.IP +The default value is 60 seconds. +.TP +.BR ESIMaxInterval +This timeout value specifies the maximum ESI interval. +If a client requests an ESI interval greater than this value, +it is silently rounded down. +.IP +The default value is 10 minutes. +.IP +The maximum ESI interval must not exceed half the value +of the registration period. +.TP +.B SCNRetries +iSNS clients can register to receive State Change Notification +(SCN) messages to learn about changes in the iSNS database. +This value specifies how often the server will try to retransmit +an SCN message until giving up. +.IP +The default value is 3. +.TP +.B SCNCallout +This is the path name of a helper program that +.B isnsdd +will invoke whenever it processes a state change notification from the +server. The helper program will be invoked with an argument indicating +the type of event, being one of +.BR add ", " update ", or " remove . +This is followed by a list of attributes in +.IB name = value +notation, using the names and conventions described in +.BR isnsadm (8). +.\" ------------------------------------------------------------------ +.SS Security Related Options +The iSNS standard defines an authentication method based on +the DSA algorithm. Participants in a message exchange authenticate +messages by adding an "authentication block" containing a time stamp, +a string identifying the key used, and a digital signature of the +message. The same method is also used by SLP, the Service Location +Protocol. +.PP +The string contained in the authentication block is referred to +as the +.IR "Security Policy Index" (SPI). +This string can be used by the server to look up the client's public +key by whatever mechanism; so the string could be used as the name of +a public key file in a directory, or to retrieve an X509 certificate +from LDAP. +.PP +From the perspective of Open-iSNS client applications, there are +only two keys: the client's own (private) key, used to sign the +messages it sends to the server, and the server's public key, +used to verify the signatures of incoming server messages. +.PP +The iSNS server needs, in addition to its own private key, access to all +public keys of clients that will communicate to it. The latter are kept +in what is called a key store. Key stores and their operation will +be discussed in section +.B Key Stores and Policy +below. +.PP +The following configuration options control authentication: +.TP +.BR Security +This enables or disables DSA authentication. +When set to 1, the client will sign all messages, and expect all server +messages to be signed. +.IP +When enabling security in the server, incoming messages are checked +for the presence of an auth block. If none is present, or if the server +cannot find a public key corresponding to the SPI, the message is treated +as originating from an anonymous source. If the SPI is known but the +signature is incorrect, the message is dropped silently. +.IP +Messages from an anonymous source will be assigned a very restrictive +policy that allows database queries only. +.IP +Setting this option to 0 will turn off authentication. +.IP +The default value is -1, which tells iSNS to use authentication +if the required keys are installed, and use unauthenticated iSNS +otherwise. +.TP +.BR AuthName +This is the string that will be used as the SPI in all outgoing +messages that have an auth block. It defaults to the host name +(please refer to option +.BR HostName ). +.TP +.BR AuthKeyFile +This is the path name of a file containing a PEM encoded DSA key. +This key is used to sign outgoing messages. +The default is +.BR /etc/isns/auth_key . +.TP +.BR ServerKeyFile +This option is used by client applications only, and specifies +the path name of a file containing a PEM encoded DSA key. +This key is used to authenticate the server's replies. +The default is +.BR /etc/isns/server_key.pub . +.TP +.BR KeyStore +This server-side option specifies the key store to use, +described in the next section. +.PP +The following two options control how iSNS will verify the +time stamp contained in the authentication block, which +is supposed to prevent replay attacks. +.TP +.B Auth.ReplayWindow +In order to compensate for clock drift between two hosts exchanging +iSNS messages, Open-iSNS will apply a little fuzz when comparing +the time stamp contained in the message +to the local system time. If the difference between +time stamp and local system time is less than the number of seconds +given by this option, the message is acceptable. Otherwise, it is +rejected. +.IP +The default value is +.BR 5m . +.TP +.B Auth.TimestampJitter +When verifying incoming messages, Open-iSNS checks that the time +stamps sent by the peer are increasing monotonically. In order to +compensate for the reordering of messages by the network (eg when +using UDP as transport), a certain time stamp jitter is accepted. +If the time stamp of an incoming messages is no earlier than +.B TimestampJitter +seconds before the last time stamp received, then the message is acceptable. +Otherwise, it is rejected. +.IP +The default value is +.BR 1s . +.\" ------------------------------------------------------------------ +.SS Key Stores and Policy +The current implementation supports two types of key stores. +.PP +The simple key store uses a flat directory to store public keys, each +key in a file of its own. The file is expected to hold the client's +PEM-encoded public key, and it must use the client's SPI as the name. +This type of key store is not really recommended, as it does not +store any policy information. +.PP +A simple key store can be configured by setting the +.B KeyStore +option to the path name of the directory. +.PP +The recommended approach is to use the database as key store. This +uses vendor-specific policy objects to tie SPI string, public key, +entity name, source name and other bits of policy together, and +store them in a persistent way. +.PP +The database key store is configured by setting the +.B KeyStore +option to the reserved value +.BR DB: , +which is also the default. +.PP +Currently, Open-iSNS policy objects have the following attributes, +besides the SPI: +.TP +Source: +This is the source node name the client must use. It defaults to +the SPI string. +.TP +Functions: +This is a bitmap detailing which functions the client is permitted +to invoke. The bit names correspond to the shorthand names used in +RFC 4711, such as +.BR DevAttrReg , +.BR DevAttrQry , +etc. The default is to allow registration, query and deregistration, +as well as SCNRegister. +.TP +Entity name: +This is the entity name assigned to the client. If set, a registration +by the client is not permitted to use a different entity name. If +the client sends a registration without Entity identifier, the +server will assign the entity name given in the policy. +The default is to not restrict the entity name. +.TP +Object access: +This is a bitfield describing access permissions for each object type. +For each object type, you can grant Read and/or Write permissions. +Read access applies to the Query and GetNext calls; all other operations +require write permission. +The default grants read and write access to objects of type Entity, Storage +Node, Portal and Portal Group; and read access to Discovery Domains. +.TP +Node types: +This bitfield describes which types of storage nodes a client is +allowed to register; the valid bit names are +.BR target ", " initiator " and " control . +The default is to restrict nodes to register initiators only. +.\" ------------------------------------------------------------------ +.SS Network Related Options +.TP +.BR Network.MaxSockets +This is the number of incoming connections accepted, and defaults to +1024. This usually applies to server side only, but is relevant if you +create a passive TCP socket for ESI or SCN. +.TP +.BR Network.ConnectTimeout +This is a timeout value, which specifies the time to wait for a TCP +connection to be established. It defaults to +.BR 60s . +.TP +.BR Network.ReconnectTimeout +When a connection attempt failed, we wait for a short time before we +try connecting again. This is intended to take the pressure off +overloaded servers. The default value is +.BR 10s . +.TP +.BR Network.CallTimeout +Total amount of time to wait before timing out a call to the iSNS server. +The default value is +.BR 60s . +.\" ------------------------------------------------------------------ +.SH SEE ALSO +RFC 4171, +.BR isnsd (8), +.BR isnsadm (8). +.SH AUTHORS +Olaf Kirch <olaf.kirch@oracle.com> diff --git a/utils/open-isns/doc/isnsadm.8 b/utils/open-isns/doc/isnsadm.8 new file mode 100644 index 0000000..c3e2b83 --- /dev/null +++ b/utils/open-isns/doc/isnsadm.8 @@ -0,0 +1,672 @@ +'\" t +.TH ISNSADM 8 "11 May 2007" +.SH NAME +isnsadm \- iSNS client utility +.SH SYNOPSIS +.B isnsadm +.RI [ options... ] +.RI --register " object... +.PP +.B isnsadm +.RB [ ... ] +.RI --query " attr" [= value ] +.PP +.B isnsadm +.RB [ ... ] +.RI --deregister " attr=value +.PP +.B isnsadm +.RB [ ... ] +.RI --list " type attr=value +.PP +.B isnsadm +.RB [ ... ] +.RI --dd-register " attr=value +.PP +.B isnsadm +.RB [ ... ] +.RI --enroll " client-name attr=value +.PP +.B isnsadm +.RB [ ... ] +.RI --edit-policy " attr=value + +.SH DESCRIPTION +.B Isnsadm +is a command line utility for interacting with an iSNS +server. It operates in one of several modes, which are +mutually exclusive. +Currently, +.B isnsadm +supports registration, query, and deregistration. +.SH OPTIONS +By default, +.B isnsadm +will take most of its settings from the configuration +file +.BR /etc/isns/isnsadm.conf , +with the exception of the following options: +.TP +.BI \--config " filename\fR, " \-c " filename +This option overrides the default configuration file. +.TP +.BI \--debug " facility\fR, " \-d " facility +enables debugging. Valid facilities are +.PP +.TS +tab(,),box,center; +lb|lr. +socket,network send/receive +auth,authentication and security related information +message,iSNS protocol layer +state,database state +scn,SCN (state change notification) messages +esi,ESI (entity status inquiry) messages +all,all of the above +.TE +.PP +.TP +.BI \--local +makes +.B isnsadm +use a Local (aka Unix) socket when talking to the iSNS +server. This can be used by the administrator to perform +management tasks, such as enrolling new clients, editing +access control and so on. Local mode is only available +to the super user. +.TP +.BI \--control +makes +.B isnsadm +assume the identity of a control node. Control nodes are +special in that they have more rights in accessing and +modifying the database than normal storage nodes have. +.PP +When using this option, +.B isnsadm +will use the source name and DSA key specified by the +.BR Control.SourceName " and " Control.AuthKeyFile +configuration options, respectively. +.PP +.TP +.BI \--key " attr" = value +This option is recognized in registration mode only, and +lets you specify an object key. For a more detailed explanation, +refer to section +.BR "Registration mode" . +.TP +.BI \--keyfile= filename +When creating a policy for a new iSNS client, +.B isnsadm +is able to generate a DSA key for the client. The public +part of the key is stored in a policy object in the iSNS +server's database, whereas the private portion is stored in the +file specified by the +.B keyfile +option. +.B +.TP +.BI \--help +This will print a help message and exit. +.\"--------------------------- +.SS Built-in help +.B Isnsadm +has built-in help functions. When invoked with +.BR \--help , +it will print a general help message showing all supported +command modes, and exit. Specific help on an individual +command mode is available by invoking that mode with a +single argument of +.BR help , +like this: +.PP +.B isnsadm --register help +.PP +This will print a help message describing how to use this +command mode, followed by a list of attributes this command supports +and a help text describing the attribute. +.\"--------------------------- +.SS Supported attributes +Most command modes take a list of attributes as arguments on the +command line. The naming and syntax of these attributes as +the same for all commands modes, however certain modes support +only a limited set of attributes. +.PP +Attributes are usually given as +.IB name = value +pairs. Where empty (or NIL) attributes are supported, the +attribute name by itself can be given. +.PP +The syntax of attribute +.I value +depends on the attribute type. For strings and numeric values, +no special conventions apply, but bitfields have a special syntax +described below. +.PP +The attribute name is usually preceded by the object +type it applies to (such as +.BR entity ), +followed by a hyphen and the name itself. However, where the +context clearly determines a specific object type, the prefix +can be omitted. For instance, when editing a policy object +using +.BR \--edit-policy , +it is acceptable to use +.B node-type +as shorthand for +.BR policy-node-type . +.PP +Likewise, in a query command, it is not permitted to mix attributes +from different object types. Thus, the first attribute of a +query string establishes a type context, so that the following +two invocations are equivalent: +.PP +.B isnsadm --query pg-name=iqn.com.foo pg-addr=10.1.1.1 pg-port=860/tcp +.br +.B isnsadm --query pg-name=iqn.com.foo addr=10.1.1.1 port=860/tcp +.PP +.B Isnsadm +currently supports the following attributes: +.PP +.TS +tab(,),box,center; +li|lilili +lt|lbrlb. +Context,Attribute,iSNS tag,Aliases +_ +Network Entity,entity-id,1,eid +\^,entity-prot,2 +\^,entity-index,7 +iSCSI Storage Node,iscsi-name,32 +\^,iscsi-node-type,33 +\^,iscsi-alias,34 +\^,iscsi-idx,36 +\^,iscsi-authmethod,42 +Portal,portal-addr,16 +\^,portal-port,17 +\^,portal-name,18 +\^,portal-esi-port,20 +\^,portal-esi-interval,21 +\^,portal-idx,22 +\^,portal-scn-port,23 +Portal Group,portal-group-index,52 +\^,pg-name,48 +\^,pg-addr,49 +\^,pg-port,50 +\^,pg-tag,51,pgt +\^,pg-idx,52 +Discovery Domain,dd-id,2065 +\^,dd-name,2066 +\^,dd-member-iscsi-idx,2067 +\^,dd-member-name,2068 +\^,dd-member-fc-name,2069, +\^,dd-member-portal-idx,2070, +\^,dd-member-addr,2071, +\^,dd-member-port,2072, +\^,dd-features,2078, +Policy Object,policy-name,-,spi +\^,policy-key,- +\^,policy-entity,- +\^,policy-node-type,- +\^,policy-object-type,- +\^,policy-functions,- +.TE +.PP +.\"--------------------------- +.SS Portal attributes +Portal information is conveyed by two separate attributes +in iSNS; an address attribute holding the IP address, and +a TCP/UDP port attribute holding the port number and an indication +of the protocol to be used (TCP or UDP). +.PP +When parsing a TCP/UDP port, Open-iSNS will expect a port number, +optionally followed by a slash and the protocol. Port names +such as "iscsi-target" are not supported. +.PP +As a convenience, +.B isnsadm +supports a notation representing a portal as one pseudo-attribute. +Separating address and port by a colon. Thus, the following two +are equivalent, with the latter being the shorthand representation +of the former: +.PP +.BI addr= <address> " port=" <port> [/ protocol ] \fR. +.BI portal= <adress> : port [/ protocol ] +.PP +This notation can be used in any context where an +.BR addr / port +attribute pair can appear, and may be prefixed by a type name, +as in +.BR pg-portal=... . +.PP +When using literal IPv6 addresses, the address has to be surrounded +by square brackets, otherwise the embedded colons would create +ambiguity: +.BR portal=[2001:5c0:0:2::24]:860/tcp +.PP +.\"--------------------------- +.SS Bitfield attributes +Some iSNS attributes are words representing a bit field. +.B Isnsadm +displays and parses these attributes in human-readable form +rather than using the numerical value. The names of the bit +values are displayed by built-in help facilities. When specifying +a bitfield attribute on the command line, you can combine them +using the plus (\fB+\fP) or comma (\fB,\fR) character, like this: +.PP +.B node-type=control+initiator +.PP +.\"--------------------------- +.SS Registration mode +Registration mode is selected by using the +.B --register +option, followed by a list of one or more objects +to register with the iSNS server. +By default, this will create a network entity for the +client (if none exists), and place the new objects inside +it. Usually, you register all objects for +a network entity in one operation, rather than each +one separately. +.PP +Each object is specified as a type, optionally followed +by a comma-separated list of attributes, such as +this: +.PP +.B target=iqn.2005-01.org.open-iscsi.foo:disk1,alias=disk1 +.PP +The following object types are currently supported: +.TP +.BI entity= name +Tells the server to group all objects in the specified +Network Entity container object. +Normally, the iSNS server will automatically assign an +entity name that is in line with its policies, and there is +no need to specify it explicitly. +.TP +.BI initiator[= name ] +This will register an iSCSI storage node of type initiator. +By default, the name is set to the iSNS source name. +.IP +This can be followed by any number of iSCSI storage node +attributes. +.TP +.BI target[= name ] +This will register an iSCSI storage node of type target. +By default, the name is set to the iSNS source name. +.IP +This object accepts the same set of attributes as +.BR initiator . +.TP +.BI control[= name ] +This will register an iSCSI storage node of type control. +By default, the name is set to the iSNS source name. +Only management nodes should be registered as control +nodes, as this gives a node complete control over the +iSNS database. +.IP +This object accepts the same set of attributes as +.BR initiator . +.TP +.BI portal=[ address:port/proto ] +This will register a portal using the given address, +port and protocol triple. If the triple is omitted, +.B isnsadm +will use the client host's IP address. If the portal +is preceded by an initiator registration (on the command +line), the port defaults to 860/tcp; if it is preceded by +a target registration, the port defaults to 3260/tcp. +For multi-homed hosts, the choice of address is +implementation dependant. +.IP +This can be followed by any number of portal attributes. +.TP +.B pg +This will register a portal group joining the preceding +portal and node. Portal groups can be used to describe +the preferred portals for a given node; please refer +to RFC 4711 for details. +.IP +This can be followed by any number of portal group attributes. +The attribute list must specify a portal group tag (PGT) +via the +.BR pgt +attribute. +.PP +There are two additional command line options of interest, +which are used exclusively with Registration mode. One is +.BR \--replace . +Normally, registration mode will +.I add +new objects to the network entity associated with the client +host. If you specify +.B \--replace +on the command line, the server will wipe the network +entity completely, and remove all portals and storage +nodes it contained. Then it will create a new network +entity, and place the portals and storage nodes provided +by the caller inside. +.PP +In addition, it is possible to replace just parts of a +network entity. This is achieved by using the command line +option +.B \--key +to specify the object that should be replaced. +.PP +For instance, assume a network entity +contains the portal +.BR 10.1.1.1:860 , +and the client's network address changed to +.BR 10.2.7.7 . +Then the following command will atomically update the +database, replacing just the portal without touching the +registered storage nodes: +.PP +.B " isnsadm --replace --key portal=10.1.1.1:860 portal=10.2.7.7:860 +.PP +The +.B \--key +option recognizes only a subset of the usual attributes: +.RS +.TS +tab(,),box; +li|li +lb|lb. +Object type,Syntax +_ +Entity,eid=\fIidentifier +Portal,portal=\fIaddress\fP:\fPport +iSCSI Node,iscsi-name=\fIname +.TE +.RE +.PP +To get a list of supported attributes, invoke +.BR "isnsadm --register help" . +.\"--------------------------- +.SS Query mode +Query mode is selected by using the +.B --query +option. A query consists of a list of +.BR attr = \fI value +pairs. All attributes must belong to the same object type, +i.e. queries that mix a Network Entity attribute with e.g. +a Portal attribute will be rejected. +.PP +It is also possible to specify an attribute name without +value (i.e. just +.BR attr ), +which will +will match any object that has such an attribute, regardless +of its value. This is useful when you want to query for all +objects of a given type. +.PP +To obtain a list of supported attributes, invoke +.BR "isnsadm --query help" . +.\"--------------------------- +.SS List Mode +In this mode, +.B isnsadm +will display all objects of a given type, optionally +restricted to those matching certain attribute values. +.PP +The arguments to list mode are a +.IR "type name" , +optionally followed by one or more +.IB attr = value +pairs. Only attributes pertaining to the given +type are permitted; for instance, if you specify a +type name of +.BR portals , +only portal attributes are permitted. +.PP +Possible type names are: +.BR entities , +.BR nodes , +.BR portals , +.BR dds , +.BR ddsets , +.BR portal-groups ", and " +.BR policies . +.PP +Additional information is available via +.BR "isnsadm --list help" . +.\"--------------------------- +.SS Deregistration mode +In this mode, you can deregister objects previously registered. +Only the node which registered an entity in the first place is +permitted to remove it, or any of its child objects. (Control +nodes are not bound by this restriction). +.PP +In deregistration mode, the argument list consists of a list of +.IB attr = value +pairs. Deregistration supports the same set of attributes as +query mode. +.\"--------------------------- +.SS Discovery Domain Registration +This mode, allows to register a discovery domain or to add +new members to an existing discovery domain. Again, attributes +are specified as a list of +.IB attr = value +pairs. Only discovery domain attributes are recognized. +.PP +Note, in order to add members to an existing domain, you must +specify the domain's numeric ID. The domain's symbolic name +is not a valid handle when referring to a discovery domain. +.\"--------------------------- +.SS Client Enrollment +This mode only works when the server recognizes the client +as having control node capabilities, which is possible in +two ways: +.TP +Invoke +.B isnsadm \--local +as super user on the host +.B isnsd +is running on. The +.B \--local +options tells it to communicate with the server through +the local control socket. +.TP +Invoke +.BR "isnsadm \--control" , +which tells it to assume the identity of a control node. +When given this option, +.B isnsadm +will use the source name and DSA key specified by the +.BR Control.SourceName " and " Control.AuthKeyFile +configuration options, respectively. +The server must be configured to grant this identity +control node status. +.PP +To enroll a client, use the +.B \--enroll +option, followed by the (source) name of the client to enroll. +This string will be used as the name of the security policy +the client will use to identify itself. +.PP +This is followed by a list of attribute/value pairs, where the +following set of attributes is supported: +.PP +.TS +tab(,),box,center; +li|lilili +lb|lrlb. +Attribute,Description,Aliases +_ +name,Policy Name,spi +key,Client's DSA public key +entity,Assigned Entity Identifier +node-type,Permitted node type(s) +node-name,Permitted node name(s) +functions,Bitmap of permitted functions +object-type,Object access mask +.TE +.PP +The +.B key +attribute is used to specify the DSA +public key that the server should use to authenticate +messages from this client. You can either provide a +file name; in which case +.B isnsadm +will try to read the PEM encoded public key from that file. +If no +.B key +attribute is given, or when using +.BR key=gen ", " isnsadm +will generate a DSA key. The private portion of the newly +generated key will be stored in the file specified by +.BI --keyfile= filename \fR. +.PP +The +.B object-type +attribute is used to specify which object types the client +is permitted to access. This is a comma separated list of +.IB type : perm +pairs, where +.I type +can be any of +.BR entity ", " iscsi-node ", " portal ", " portal-group ", " dd ", " ddset ", and " policy . +The permissions can be either +.BR rw ", or " r . +.PP +The +.B functions +attribute can be used to restrict which functions the client is +permitted to invoke. This is a bitfield, using the standard function +names from RFC 4171, such as +.BR DevAttrReg ", " DevAttrQry ", etc." +.PP +For a description of the open-isns security model +and policies, please refer to the +.BR isns_config (5) +manual page. +.PP +.BR "Important note" : +In order to generate a DSA key, you have to have a set of DSA +parameters installed. By default, +.B isnsadm +expects to find them in +.BR /etc/isns/dsa.params . +These parameters are created by calling +.B isnsd \--init +once on the server machine. Alternatively, you can use +the following command: +.PP +.ti +8 +openssl dsaparam 1024 -out /etc/isns/dsa.params +.ti -8 +.PP +where 1024 is the chosen DSA key size, in bits. +.SH EXAMPLES +If you want to use Open-iSNS in authenticated mode, +you first need to initialize the server's DSA key and +DSA parameters. This can be done conveniently by using +.PP +.B isnsd --init +.PP +This will create the server's private and public key, +and place them in +.B /etc/isns/auth_key +and +.BR auth_key.pub , +respectively. +.PP +The following command will create a policy object for a +node named +.B isns.control , +and grant it control privileges: +.PP +.B isnsadm --local --keyfile=control.key +.B --enroll isns.control \(rs +.br +.B " node-type=ALL functions=ALL object-type=ALL +.PP +In the process of entrolling the client, this will generate +a DSA key pair, and place the private key portion in the +file +.BR control.key . +This file must be installed as +.BR /etc/isns/control.key +on the host you wish to use as an iSNS management station. +.PP +Next, you need to create a storage node object for the +management station: +.PP +.B isnsadm --local --register control +.PP +On the management station, you can then enroll additional +hosts: +.PP +.B isnsadm --control --keyfile=somehost.key +.B --enroll iqn.2005-01.org.open-iscsi.somehost \(rs +.br +.B " node-type=target+initiator +.PP +Again, this will generate a DSA key pair and store the private +key portion in auth_key. Note the use of the +.B \--control +option that tells +.B isnsadm +to use the identity of the control node instead of the default +key and source name. +.PP +You then need to copy +.B somehost.key +to the client host and install it as +.BR /etc/isns/auth_key . +Likewise, the server's public key (which resides in +.BR /etc/isns/auth_key.pub +on the server) needs to be copied to the client machine, +and placed in +.BR /etc/isns/server_key.pub . +.PP +By default, when a client registers a storage node (be +it initiator or target) with iSNS, the client will not be +able to see any other storage nodes. In order for targets +to be visible to a given initiator, you need to create +so-called Discovery Domains (or DDs for short). +.PP +Currently, domain membership operations require administrator +privilege. Future extensions may allow iSNS clients to +add themselves to one or more DDs upon registration. +.PP +To create a discovery domain, and add nodes to it, you can +use +.PP +.B isnsadm --control --dd-register dd-name=mydomain \(rs +.br +.B " member-name=iqn.org.bozo.client iqn.org.bozo.jbod ... +.PP +In order to add members to an existing DD, you have to +specify the numeric domain ID - using the DD name is not +sufficient, unfortunately (this is a requirement of the +RFC, not an implementation issue): +.PP +.B isnsadm --control --dd-register dd-id=42 \(rs +.br +.B " member-name=iqn.com.foo member-name=iqn.com.bar +.PP +The DD ID can be obtained by doing a query for the DD name: +.PP +.B isnsadm --control --query dd-name=mydomain +.PP +In management mode, you can also register and deregister +nodes and portals manually, in case you want to fix up +an inconsisteny in the database. For instance, this will +register a node and portal on a host named client.bozo.org: +.PP +.B isnsadm --control --register entity=client.bozo.org \(rs +.br +.B " initiator=iqn.org.bozo.client portal=191.168.7.1:860 +.PP +Note that this registration explicitly specifies the network +entity in which to place the new objects. If you omit this, +the new objects will be placed in an entity named +.BR CONTROL , +which is decidedly not what you want. +.SH SEE ALSO +RFC 4171, +.BR isnsd (8), +.BR isns_config (5). +.SH AUTHORS +Olaf Kirch <olaf.kirch@oracle.com> diff --git a/utils/open-isns/doc/isnsd.8 b/utils/open-isns/doc/isnsd.8 new file mode 100644 index 0000000..84b3913 --- /dev/null +++ b/utils/open-isns/doc/isnsd.8 @@ -0,0 +1,93 @@ +.TH ISNSD 8 "11 May 2007" +.SH NAME +isnsd \- iSNS server daemon +.SH SYNOPSIS +.B isnsd +.RB [ "\-f" ] +.RB [ "\-4" ] +.RB [ "\-6" ] +.RB [ "\-c \fIfilename" ] +.RB [ "\-d \fIdebug-facility" ] +.RB [ \--dump-db ] +.RB [ \--init ] + +.SH DESCRIPTION +.B Isnsd +implements the iSNS protocol as defined in RFC 4171. +iSNS is a discovery protocol for iSCSI and iFCP. +.SH OPTIONS +By default, +.B isnsd +will take most of its settings from the configuration +file +.BR /etc/isns/isnsd.conf , +with the exception of the following options: +.TP +.BI \--config " filename\fR, " \-c " filename +This option overrides the default configuration file. +.TP +.BR \--foreground , \-f +By default, +.B isnsd +will put itself into the background. By specifying this option, you can +tell it to run in the foreground. Any error messages or debug output +will be printed to the console rather than being sent to syslog. +.TP +.BI \-4 +tells +.B isnsd +to create an IPv4 socket only. Normally, it defaults +to IPv6 (which will accept both IPv4 and IPv6 connections). +.TP +.BI \-6 +tells +.B isnsd +explicitly +to create an IPv6 socket only. Since it defaults +to IPv6 anyway, this is really a no-op. +.TP +.BI \--debug " facility\fR, " \-d " facility +enables debugging. Valid facilities are +.PP +.TS +tab(,),box,center; +lb|lr. +socket,network send/receive +auth,authentication and security related information +message,iSNS protocol layer +state,database state +scn,SCN (state change notification) messages +esi,ESI (entity status inquiry) messages +all,all of the above +.TE +.PP +.TP +.B \--dump-db +This is a helper function that will read the database from the +file system, and display it in human readable form. When using +this option, +.B isnsd +will not open any sockets, and terminate immediately after display +the database. +.IP +This option is intended to be used by the administrator when suspecting +that the database contains bad/inconsistent information. +.TP +.B \--init +This option will create the server's authentication key, and +the required DSA parameters. The private key is stored in the +file specified by the +.B AuthKey +option (usually +.BR /etc/isns/auth_key ). +The public portion of the key is written to same directory, +with the suffix +.B .pub +appended to the key file name. +.SH SEE ALSO +RFC 4171, +.BR isnsadm (8), +.BR isnsdd (8), +.BR isns_config (5). +.SH AUTHORS +Olaf Kirch <olaf.kirch@oracle.com> diff --git a/utils/open-isns/doc/isnsdd.8 b/utils/open-isns/doc/isnsdd.8 new file mode 100644 index 0000000..6088e28 --- /dev/null +++ b/utils/open-isns/doc/isnsdd.8 @@ -0,0 +1,75 @@ +.TH ISNSDD 8 "11 May 2007" +.SH NAME +isnsdd \- iSNS discovery daemon +.SH SYNOPSIS +.B isnsdd +.RB [ "\-f" ] +.RB [ "\-c \fIfilename" ] +.RB [ "\-d \fIdebug-facility" ] + +.SH DESCRIPTION +.B Isnsdd +is a client side daemon for iSNS. It registers storage +nodes and portals with the iSNS service, and refreshes +these registrations in a timely manner. +.PP +The daemon also registers itself to receive SCN notifications, +and processes these. It can be configured to invoke an +external helper application for each status notification +received. The path name of the helper application can be +specified via the +.B SCNCallout +option in the configuration file. +.SH OPTIONS +By default, +.B isnsd +will take most of its settings from the configuration +file +.BR /etc/isns/isnsdd.conf , +with the addition of the following command line options: +.TP +.BI \--config " filename\fR, " \-c " filename +This option overrides the default configuration file. +.TP +.BR \--foreground , \-f +By default, +.B isnsd +will put itself into the background. By specifying this option, you can +tell it to run in the foreground. Any error messages or debug output +will be printed to the console rather than being sent to syslog. +.TP +.BI \--role " role +This tells the discovery daemon in which capacity is should register itself +with the iSNS server. +.I Role +can be either +.BR initiator ", or " control . +The default is to register as an initiator. +.IP +Registering target nodes needs to use a different mechanism, as +the iSCSI target server needs to inform the discovery daemon +about each exported target separately. This is not implemented +yet. +.TP +.BI \--debug " facility\fR, " \-d " facility +enables debugging. Valid facilities are +.PP +.TS +tab(,),box,center; +lb|lr. +socket,network send/receive +auth,authentication and security related information +message,iSNS protocol layer +state,database state +scn,SCN (state change notification) messages +esi,ESI (entity status inquiry) messages +all,all of the above +.TE +.PP +.SH SEE ALSO +RFC 4171, +.BR isnsd (8), +.BR isnsadm (8), +.BR isns_config (5). +.SH AUTHORS +Olaf Kirch <olaf.kirch@oracle.com> diff --git a/utils/open-isns/doc/rfc2608.txt b/utils/open-isns/doc/rfc2608.txt new file mode 100644 index 0000000..fa7de62 --- /dev/null +++ b/utils/open-isns/doc/rfc2608.txt @@ -0,0 +1,3027 @@ + + + + + + +Network Working Group E. Guttman +Request for Comments: 2608 C. Perkins +Updates: 2165 Sun Microsystems +Category: Standards Track J. Veizades + @Home Network + M. Day + Vinca Corporation + June 1999 + + + Service Location Protocol, Version 2 + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (1999). All Rights Reserved. + +Abstract + + The Service Location Protocol provides a scalable framework for the + discovery and selection of network services. Using this protocol, + computers using the Internet need little or no static configuration + of network services for network based applications. This is + especially important as computers become more portable, and users + less tolerant or able to fulfill the demands of network system + administration. + +Table of Contents + + 1. Introduction 3 + 1.1. Applicability Statement . . . . . . . . . . . . . . . 3 + 2. Terminology 4 + 2.1. Notation Conventions . . . . . . . . . . . . . . . . . 4 + 3. Protocol Overview 5 + 4. URLs used with Service Location 8 + 4.1. Service: URLs . . . . . . . . . . . . . . . . . . . . 9 + 4.2. Naming Authorities . . . . . . . . . . . . . . . . . 10 + 4.3. URL Entries . . . . . . . . . . . . . . . . . . . . . 10 + 5. Service Attributes 10 + 6. Required Features 12 + 6.1. Use of Ports, UDP, and Multicast . . . . . . . . . . 13 + + + +Guttman, et al. Standards Track [Page 1] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + 6.2. Use of TCP . . . . . . . . . . . . . . . . . . . . . 14 + 6.3. Retransmission of SLP messages . . . . . . . . . . . 15 + 6.4. Strings in SLP messages . . . . . . . . . . . . . . . 16 + 6.4.1. Scope Lists in SLP . . . . . . . . . . . . . . 16 + 7. Errors 17 + 8. Required SLP Messages 17 + 8.1. Service Request . . . . . . . . . . . . . . . . . . . 19 + 8.2. Service Reply . . . . . . . . . . . . . . . . . . . . 21 + 8.3. Service Registration . . . . . . . . . . . . . . . . . 22 + 8.4. Service Acknowledgment . . . . . . . . . . . . . . . . 23 + 8.5. Directory Agent Advertisement. . . . . . . . . . . . . 24 + 8.6. Service Agent Advertisement. . . . . . . . . . . . . . 25 + 9. Optional Features 26 + 9.1. Service Location Protocol Extensions . . . . . . . . . 27 + 9.2. Authentication Blocks . . . . . . . . . . . . . . . . 28 + 9.2.1. SLP Message Authentication Rules . . . . . . . 29 + 9.2.2. DSA with SHA-1 in Authentication Blocks . . . 30 + 9.3. Incremental Service Registration . . . . . . . . . . 30 + 9.4. Tag Lists . . . . . . . . . . . . . . . . . . . . . . 31 + 10. Optional SLP Messages 32 + 10.1. Service Type Request . . . . . . . . . . . . . . . . 32 + 10.2. Service Type Reply . . . . . . . . . . . . . . . . . 32 + 10.3. Attribute Request . . . . . . . . . . . . . . . . . . 33 + 10.4. Attribute Reply . . . . . . . . . . . . . . . . . . . 34 + 10.5. Attribute Request/Reply Examples . . . . . . . . . . . 34 + 10.6. Service Deregistration . . . . . . . . . . . . . . . 36 + 11. Scopes 37 + 11.1. Scope Rules . . . . . . . . . . . . . . . . . . . . . 37 + 11.2. Administrative and User Selectable Scopes. . . . . . . 38 + 12. Directory Agents 38 + 12.1. Directory Agent Rules . . . . . . . . . . . . . . . . 39 + 12.2. Directory Agent Discovery . . . . . . . . . . . . . . 39 + 12.2.1. Active DA Discovery . . . . . . . . . . . . . 40 + 12.2.2. Passive DA Advertising . . . . . . . . . . . . 40 + 12.3. Reliable Unicast to DAs and SAs. . . . . . . . . . . . 41 + 12.4. DA Scope Configuration . . . . . . . . . . . . . . . 41 + 12.5. DAs and Authentication Blocks. . . . . . . . . . . . . 41 + 13. Protocol Timing Defaults 42 + 14. Optional Configuration 43 + 15. IANA Considerations 44 + 16. Internationalization Considerations 45 + 17. Security Considerations 46 + A. Appendix: Changes to the Service Location Protocol from + v1 to v2 48 + B. Appendix: Service Discovery by Type: Minimal SLPv2 Features 48 + C. Appendix: DAAdverts with arbitrary URLs 49 + D. Appendix: SLP Protocol Extensions 50 + D.1. Required Attribute Missing Option . . . . . . . . . . 50 + + + +Guttman, et al. Standards Track [Page 2] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + E. Acknowledgments 50 + F. References 51 + G. Authors' Addresses 53 + H. Full Copyright Statement 54 + +1. Introduction + + The Service Location Protocol (SLP) provides a flexible and scalable + framework for providing hosts with access to information about the + existence, location, and configuration of networked services. + Traditionally, users have had to find services by knowing the name of + a network host (a human readable text string) which is an alias for a + network address. SLP eliminates the need for a user to know the name + of a network host supporting a service. Rather, the user supplies + the desired type of service and a set of attributes which describe + the service. Based on that description, the Service Location + Protocol resolves the network address of the service for the user. + + SLP provides a dynamic configuration mechanism for applications in + local area networks. Applications are modeled as clients that need + to find servers attached to any of the available networks within an + enterprise. For cases where there are many different clients and/or + services available, the protocol is adapted to make use of nearby + Directory Agents that offer a centralized repository for advertised + services. + + This document updates SLPv1 [RFC 2165], correcting protocol errors, + adding some enhancements and removing some requirements. This + specification has two parts. The first describes the required + features of the protocol. The second describes the extended features + of the protocol which are optional, and allow greater scalability. + +1.1. Applicability Statement + + SLP is intended to function within networks under cooperative + administrative control. Such networks permit a policy to be + implemented regarding security, multicast routing and organization of + services and clients into groups which are not be feasible on the + scale of the Internet as a whole. + + SLP has been designed to serve enterprise networks with shared + services, and it may not necessarily scale for wide-area service + discovery throughout the global Internet, or in networks where there + are hundreds of thousands of clients or tens of thousands of + services. + + + + + + +Guttman, et al. Standards Track [Page 3] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +2. Terminology + + User Agent (UA) + A process working on the user's behalf to establish + contact with some service. The UA retrieves service + information from the Service Agents or Directory Agents. + + Service Agent (SA) A process working on the behalf of one or more + services to advertise the services. + + Directory Agent (DA) A process which collects service + advertisements. There can only be one DA present per + given host. + + Service Type Each type of service has a unique Service Type + string. + + Naming Authority The agency or group which catalogues given + Service Types and Attributes. The default Naming + Authority is IANA. + + Scope A set of services, typically making up a logical + administrative group. + + URL A Universal Resource Locator [8]. + +2.1. Notation Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [9]. + + Syntax Syntax for string based protocols follow the + conventions defined for ABNF [11]. + + Strings All strings are encoded using the UTF-8 [23] + transformation of the Unicode [6] character set and + are NOT null terminated when transmitted. Strings + are preceded by a two byte length field. + + <string-list> A comma delimited list of strings with the + following syntax: + + string-list = string / string `,' string-list + + In format diagrams, any field ending with a \ indicates a variable + length field, given by a prior length field in the protocol. + + + + +Guttman, et al. Standards Track [Page 4] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +3. Protocol Overview + + The Service Location Protocol supports a framework by which client + applications are modeled as 'User Agents' and services are advertised + by 'Service Agents.' A third entity, called a 'Directory Agent' + provides scalability to the protocol. + + The User Agent issues a 'Service Request' (SrvRqst) on behalf of the + client application, specifying the characteristics of the service + which the client requires. The User Agent will receive a Service + Reply (SrvRply) specifying the location of all services in the + network which satisfy the request. + + The Service Location Protocol framework allows the User Agent to + directly issue requests to Service Agents. In this case the request + is multicast. Service Agents receiving a request for a service which + they advertise unicast a reply containing the service's location. + + +------------+ ----Multicast SrvRqst----> +---------------+ + | User Agent | | Service Agent | + +------------+ <----Unicast SrvRply------ +---------------+ + + In larger networks, one or more Directory Agents are used. The + Directory Agent functions as a cache. Service Agents send register + messages (SrvReg) containing all the services they advertise to + Directory Agents and receive acknowledgements in reply (SrvAck). + These advertisements must be refreshed with the Directory Agent or + they expire. User Agents unicast requests to Directory Agents + instead of Service Agents if any Directory Agents are known. + + +-------+ -Unicast SrvRqst-> +-----------+ <-Unicast SrvReg- +--------+ + | User | | Directory | |Service | + | Agent | | Agent | | Agent | + +-------+ <-Unicast SrvRply- +-----------+ -Unicast SrvAck-> +--------+ + + User and Service Agents discover Directory Agents two ways. First, + they issue a multicast Service Request for the 'Directory Agent' + service when they start up. Second, the Directory Agent sends an + unsolicited advertisement infrequently, which the User and Service + Agents listen for. In either case the Agents receive a DA + Advertisement (DAAdvert). + + +---------------+ --Multicast SrvRqst-> +-----------+ + | User or | <--Unicast DAAdvert-- | Directory | + | Service Agent | | Agent | + +---------------+ <-Multicast DAAdvert- +-----------+ + + + + + +Guttman, et al. Standards Track [Page 5] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + Services are grouped together using 'scopes'. These are strings + which identify services which are administratively identified. A + scope could indicate a location, administrative grouping, proximity + in a network topology or some other category. Service Agents and + Directory Agents are always assigned a scope string. + + A User Agent is normally assigned a scope string (in which case the + User Agent will only be able to discover that particular grouping of + services). This allows a network administrator to 'provision' + services to users. Alternatively, the User Agent may be configured + with no scope at all. In that case, it will discover all available + scopes and allow the client application to issue requests for any + service available on the network. + + +---------+ Multicast +-----------+ Unicast +-----------+ + | Service | <--SrvRqst-- | User | --SrvRqst-> | Directory | + | Agent | | Agent | | Agent | + | Scope=X | Unicast | Scope=X,Y | Unicast | Scope=Y | + +---------+ --SrvRply--> +-----------+ <-SrvRply-- +-----------+ + + In the above illustration, the User Agent is configured with scopes X + and Y. If a service is sought in scope X, the request is multicast. + If it is sought in scope Y, the request is unicast to the DA. + Finally, if the request is to be made in both scopes, the request + must be both unicast and multicast. + + Service Agents and User Agents may verify digital signatures provided + with DAAdverts. User Agents and Directory Agents may verify service + information registered by Service Agents. The keying material to use + to verify digital signatures is identified using a SLP Security + Parameter Index, or SLP SPI. + + Every host configured to generate a digital signature includes the + SLP SPI used to verify it in the Authentication Block it transmits. + Every host which can verify a digital signature must be configured + with keying material and other parameters corresponding with the SLP + SPI such that it can perform verifying calculations. + + SAs MUST accept multicast service requests and unicast service + requests. SAs MAY accept other requests (Attribute and Service Type + Requests). SAs MUST listen for multicast DA Advertisements. + + The features described up to this point are required to implement. A + minimum implementation consists of a User Agent, Service Agent or + both. + + There are several optional features in the protocol. Note that DAs + MUST support all these message types, but DA support is itself + + + +Guttman, et al. Standards Track [Page 6] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + optional to deploy on networks using SLP. UAs and SAs MAY support + these message types. These operations are primarily for interactive + use (browsing or selectively updating service registrations.) UAs + and SAs either support them or not depending on the requirements and + constraints of the environment where they will be used. + + Service Type Request A request for all types of service on the + network. This allows generic service browsers + to be built. + + Service Type Reply A reply to a Service Type Request. + + Attribute Request A request for attributes of a given type of + service or attributes of a given service. + + Attribute Reply A reply to an Attribute Request. + + Service Deregister A request to deregister a service or some + attributes of a service. + + Service Update A subsequent SrvRqst to an advertisement. + This allows individual dynamic attributes to + be updated. + + SA Advertisement In the absence of Directory Agents, a User + agent may request Service Agents in order + to discover their scope configuration. The + User Agent may use these scopes in requests. + + In the absence of Multicast support, Broadcast MAY be used. The + location of DAs may be staticly configured, discovered using SLP as + described above, or configured using DHCP. If a message is too large, + it may be unicast using TCP. + + A SLPv2 implementation SHOULD support SLPv1 [22]. This support + includes: + + 1. SLPv2 DAs are deployed, phasing out SLPv1 DAs. + + 2. Unscoped SLPv1 requests are considered to be of DEFAULT scope. + SLPv1 UAs MUST be reconfigured to have a scope if possible. + + 3. There is no way for an SLPv2 DA to behave as an unscoped SLPv1 + DA. SLPv1 SAs MUST be reconfigured to have a scope if possible. + + 4. SLPv2 DAs answer SLPv1 requests with SLPv1 replies and SLPv2 + requests with SLPv2 replies. + + + + +Guttman, et al. Standards Track [Page 7] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + 5. SLPv2 DAs use registrations from SLPv1 and SLPv2 in the same + way. That is, incoming requests from agents using either version + of the protocol will be matched against this common set of + registered services. + + 6. SLPv2 registrations which use Language Tags which are greater + than 2 characters long will be inaccessible to SLPv1 UAs. + + 7. SLPv2 DAs MUST return only service type strings in SrvTypeRply + messages which conform to SLPv1 service type string syntax, ie. + they MUST NOT return Service Type strings for abstract service + types. + + 8. SLPv1 SrvRqsts and AttrRqsts by Service Type do not match Service + URLs with abstract service types. They only match Service URLs + with concrete service types. + + SLPv1 UAs will not receive replies from SLPv2 SAs and SLPv2 UAs will + not receive replies from SLPv1 SAs. In order to interoperate UAs and + SAs of different versions require a SLPv2 DA to be present on the + network which supports both protocols. + + The use of abstract service types in SLPv2 presents a backward + compatibility issue for SLPv1. It is possible that a SLPv1 UA will + request a service type which is actually an abstract service type. + Based on the rules above, the SLPv1 UA will never receive an abstract + Service URL reply. For example, the service type 'service:x' in a + SLPv1 AttrRqst will not return the attributes of 'service:x:y://orb'. + If the request was made with SLPv2, it would return the attributes of + this service. + +4. URLs used with Service Location + + A Service URL indicates the location of a service. This URL may be + of the service: scheme [13] (reviewed in section 4.1), or any other + URL scheme conforming to the URI standard [8], except that URLs + without address specifications SHOULD NOT be advertised by SLP. The + service type for an 'generic' URL is its scheme name. For example, + the service type string for "http://www.srvloc.org" would be "http". + + Reserved characters in URLs follow the rules in RFC 2396 [8]. + + + + + + + + + + +Guttman, et al. Standards Track [Page 8] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +4.1. Service: URLs + + Service URL syntax and semantics are defined in [13]. Any network + service may be encoded in a Service URL. + + This section provides an introduction to Service URLs and an example + showing a simple application of them, representing standard network + services. + + A Service URL may be of the form: + + "service:"<srvtype>"://"<addrspec> + + The Service Type of this service: URL is defined to be the string up + to (but not including) the final `:' before <addrspec>, the address + specification. + + <addrspec> is a hostname (which should be used if possible) or dotted + decimal notation for a hostname, followed by an optional `:' and + port number. + + A service: scheme URL may be formed with any standard protocol name + by concatenating "service:" and the reserved port [1] name. For + example, "service:tftp://myhost" would indicate a tftp service. A + tftp service on a nonstandard port could be + "service:tftp://bad.glad.org:8080". + + Service Types SHOULD be defined by a "Service Template" [13], which + provides expected attributes, values and protocol behavior. An + abstract service type (also described in [13]) has the form + + "service:<abstract-type>:<concrete-type>". + + The service type string "service:<abstract-type>" matches all + services of that abstract type. If the concrete type is included + also, only these services match the request. For example: a SrvRqst + or AttrRqst which specifies "service:printer" as the Service Type + will match the URL service:printer:lpr://hostname and + service:printer:http://hostname. If the requests specified + "service:printer:http" they would match only the latter URL. + + An optional substring MAY follow the last `.' character in the + <srvtype> (or <abstract-type> in the case of an abstract service type + URL). This substring is the Naming Authority, as described in Section + 9.6. Service types with different Naming Authorities are quite + distinct. In other words, service:x.one and service:x.two are + different service types, as are service:abstract.one:y and + service:abstract.two:y. + + + +Guttman, et al. Standards Track [Page 9] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +4.2. Naming Authorities + + A Naming Authority MAY optionally be included as part of the Service + Type string. The Naming Authority of a service defines the meaning + of the Service Types and attributes registered with and provided by + Service Location. The Naming Authority itself is typically a string + which uniquely identifies an organization. IANA is the implied + Naming Authority when no string is appended. "IANA" itself MUST NOT + be included explicitly. + + Naming Authorities may define Service Types which are experimental, + proprietary or for private use. Using a Naming Authority, one may + either simply ignore attributes upon registration or create a local- + use only set of attributes for one's site. The procedure to use is + to create a 'unique' Naming Authority string and then specify the + Standard Attribute Definitions as described above. This Naming + Authority will accompany registration and queries, as described in + Sections 8.1 and 8.3. Service Types SHOULD be registered with IANA + to allow for Internet-wide interoperability. + +4.3. URL Entries + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved | Lifetime | URL Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |URL len, contd.| URL (variable length) \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |# of URL auths | Auth. blocks (if any) \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + SLP stores URLs in protocol elements called URL Entries, which + associate a length, a lifetime, and possibly authentication + information along with the URL. URL Entries, defined as shown above, + are used in Service Replies and Service Registrations. + +5. Service Attributes + + A service advertisement is often accompanied by Service Attributes. + These attributes are used by UAs in Service Requests to select + appropriate services. + + The allowable attributes which may be used are typically specified by + a Service Template [13] for a particular service type. Services + which are advertised according to a standard template MUST register + all service attributes which the standard template requires. URLs + with schemes other than "service:" MAY be registered with attributes. + + + +Guttman, et al. Standards Track [Page 10] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + Non-standard attribute names SHOULD begin with "x-", because no + standard attribute name will ever have those initial characters. + + An attribute list is a string encoding of the attributes of a + service. The following ABNF [11] grammar defines attribute lists: + + attr-list = attribute / attribute `,' attr-list + attribute = `(' attr-tag `=' attr-val-list `)' / attr-tag + attr-val-list = attr-val / attr-val `,' attr-val-list + attr-tag = 1*safe-tag + attr-val = intval / strval / boolval / opaque + intval = [-]1*DIGIT + strval = 1*safe-val + boolval = "true" / "false" + opaque = "\FF" 1*escape-val + safe-val = ; Any character except reserved. + safe-tag = ; Any character except reserved, star and bad-tag. + reserved = `(' / `)' / `,' / `\' / `!' / `<' / `=' / `>' / `~' / CTL + escape-val = `\' HEXDIG HEXDIG + bad-tag = CR / LF / HTAB / `_' + star = `*' + + The <attr-list>, if present, MUST be scanned prior to evaluation for + all occurrences of the escape character `\'. Reserved characters + MUST be escaped (other characters MUST NOT be escaped). All escaped + characters must be restored to their value before attempting string + matching. For Opaque values, escaped characters are not converted - + they are interpreted as bytes. + + Boolean Strings which have the form "true" or "false" can + only take one value and may only be compared with + '='. Booleans are case insensitive when compared. + + Integer Strings which take the form [-] 1*<digit> and fall + in the range "-2147483648" to "2147483647" are + considered to be Integers. These are compared using + integer comparison. + + String All other Strings are matched using strict lexical + ordering (see Section 6.4). + + Opaque Opaque values are sequences of bytes. These are + distinguished from Strings since they begin with + the sequence "\FF". This, unescaped, is an illegal + UTF-8 encoding, indicating that what follows is a + sequence of bytes expressed in escape notation which + constitute the binary value. For example, a '0' byte + is encoded "\FF\00". + + + +Guttman, et al. Standards Track [Page 11] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + A string which contains escaped values other than from the reserved + set of characters is illegal. If such a string is included in an + <attr-list>, <tag-list> or search filter, the SA or DA which receives + it MUST return a PARSE_ERROR to the message. + + A keyword has only an <attr-tag>, and no values. Attributes can have + one or multiple values. All values are expressed as strings. + + When values have been advertised by a SA or are registered in a DA, + they can take on implicit typing rules for matching incoming + requests. + + Stored values must be consistent, i.e., x=4,true,sue,\ff\00\00 is + disallowed. A DA or SA receiving such an <attr-list> MUST return an + INVALID_REGISTRATION error. + +6. Required Features + + This section defines the minimal implementation requirements for SAs + and UAs as well as their interaction with DAs. A DA is not required + for SLP to function, but if it is present, the UA and SA MUST + interact with it as defined below. + + A minimal implementation may consist of either a UA or SA or both. + The only required features of a UA are that it can issue SrvRqsts + according to the rules below and interpret DAAdverts, SAAdverts and + SrvRply messages. The UA MUST issue requests to DAs as they are + discovered. An SA MUST reply to appropriate SrvRqsts with SrvRply or + SAAdvert messages. The SA MUST also register with DAs as they are + discovered. + + UAs perform discovery by issuing Service Request messages. SrvRqst + messages are issued, using UDP, following these prioritized rules: + + 1. A UA issues a request to a DA which it has been configured with + by DHCP. + + 2. A UA issues requests to DAs which it has been statically + configured with. + + 3. UA uses multicast/convergence SrvRqsts to discover DAs, then uses + that set of DAs. A UA that does not know of any DAs SHOULD retry + DA discovery, increasing the waiting interval between subsequent + attempts exponentially (doubling the wait interval each time.) + The recommended minimum waiting interval is CONFIG_DA_FIND + seconds. + + + + + +Guttman, et al. Standards Track [Page 12] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + 4. A UA with no knowledge of DAs sends requests using multicast + convergence to SAs. SAs unicast replies to UAs according to the + multicast convergence algorithm. + + UAs and SAs are configured with a list of scopes to use according to + these prioritized rules: + + 1. With DHCP. + + 2. With static configuration. The static configuration may be + explicitly set to NO SCOPE for UAs, if the User Selectable Scope + model is used. See section 11.2. + + 3. In the absence of configuration, the agent's scope is "DEFAULT". + + A UA MUST issue requests with one or more of the scopes it has been + configured to use. + + A UA which has been statically configured with NO SCOPE LIST will use + DA or SA discovery to determine its scope list dynamically. In this + case it uses an empty scope list to discover DAs and possibly SAs. + Then it uses the scope list it obtains from DAAdverts and possibly + SAAdverts in subsequent requests. + + The SA MUST register all its services with any DA it discovers, if + the DA advertises any of the scopes it has been configured with. A + SA obtains information about DAs as a UA does. In addition, the SA + MUST listen for multicast unsolicited DAAdverts. The SA registers by + sending SrvReg messages to DAs, which reply with SrvReg messages to + indicate success. SAs register in ALL the scopes they were + configured to use. + +6.1. Use of Ports, UDP, and Multicast + + DAs MUST accept unicast requests and multicast directory agent + discovery service requests (for the service type "service:directory- + agent"). + + SAs MUST accept multicast requests and unicast requests both. The SA + can distinguish between them by whether the REQUEST MCAST flag is set + in the SLP Message header. + + The Service Location Protocol uses multicast for discovering DAs and + for issuing requests to SAs by default. + + The reserved listening port for SLP is 427. This is the destination + port for all SLP messages. SLP messages MAY be transmitted on an + ephemeral port. Replies and acknowledgements are sent to the port + + + +Guttman, et al. Standards Track [Page 13] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + from which the request was issued. The default maximum transmission + unit for UDP messages is 1400 bytes excluding UDP and other headers. + + If a SLP message does not fit into a UDP datagram it MUST be + truncated to fit, and the OVERFLOW flag is set in the reply message. + A UA which receives a truncated message MAY open a TCP connection + (see section 6.2) with the DA or SA and retransmit the request, using + the same XID. It MAY also attempt to make use of the truncated reply + or reformulate a more restrictive request which will result in a + smaller reply. + + SLP Requests messages are multicast to The Administratively Scoped + SLP Multicast [17] address, which is 239.255.255.253. The default + TTL to use for multicast is 255. + + In isolated networks, broadcasts will work in place of multicast. To + that end, SAs SHOULD and DAs MUST listen for broadcast Service + Location messages at port 427. This allows UAs which do not support + multicast the use of Service Location on isolated networks. + + Setting multicast TTL to less than 255 (the default) limits the range + of SLP discovery in a network, and localizes service information in + the network. + +6.2. Use of TCP + + A SrvReg or SrvDeReg may be too large to fit into a datagram. To + send such large SLP messages, a TCP (unicast) connection MUST be + established. + + To avoid the need to implement TCP, one MUST insure that: + + - UAs never issue requests larger than the Path MTU. SAs can omit + TCP support only if they never have to receive unicast requests + longer than the path MTU. + + - UAs can accept replies with the 'OVERFLOW' flag set, and make use + of the first result included, or reformulate the request. + + - Ensure that a SA can send a SrvRply, SrvReg, or SrvDeReg in + a single datagram. This means limiting the size of URLs, + the number of attributes and the number of authenticators + transmitted. + + DAs MUST be able to respond to UDP and TCP requests, as well as + multicast DA Discovery SrvRqsts. SAs MUST be able to respond to TCP + unless the SA will NEVER receive a request or send a reply which will + exceed a datagram in size (e.g., some embedded systems). + + + +Guttman, et al. Standards Track [Page 14] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + A TCP connection MAY be used for a single SLP transaction, or for + multiple transactions. Since there are length fields in the message + headers, SLP Agents can send multiple requests along a connection and + read the return stream for acknowledgments and replies. + + The initiating agent SHOULD close the TCP connection. The DA SHOULD + wait at least CONFIG_CLOSE_CONN seconds before closing an idle + connection. DAs and SAs SHOULD close an idle TCP connection after + CONFIG_CLOSE_CONN seconds to ensure robust operation, even when the + initiating agent neglects to close it. See Section 13 for timing + rules. + +6.3. Retransmission of SLP messages + + Requests which fail to elicit a response are retransmitted. The + initial retransmission occurs after a CONFIG_RETRY wait period. + Retransmissions MUST be made with exponentially increasing wait + intervals (doubling the wait each time). This applies to unicast as + well as multicast SLP requests. + + Unicast requests to a DA or SA should be retransmitted until either a + response (which might be an error) has been obtained, or for + CONFIG_RETRY_MAX seconds. + + Multicast requests SHOULD be reissued over CONFIG_MC_MAX seconds + until a result has been obtained. UAs need only wait till they + obtain the first reply which matches their request. That is, + retransmission is not required if the requesting agent is prepared to + use the 'first reply' instead of 'as many replies as possible within + a bounded time interval.' + + When SLP SrvRqst, SrvTypeRqst, and AttrRqst messages are multicast, + they contain a <PRList> of previous responders. Initially the + <PRList> is empty. When these requests are unicast, the <PRList> is + always empty. + + Any DA or SA which sees its address in the <PRList> MUST NOT respond + to the request. + + The message SHOULD be retransmitted until the <PRList> causes no + further responses to be elicited or the previous responder list and + the request will not fit into a single datagram or until + CONFIG_MC_MAX seconds elapse. + + UAs which retransmit a request use the same XID. This allows a DA or + SA to cache its reply to the original request and then send it again, + should a duplicate request arrive. This cached information should + only be held very briefly. XIDs SHOULD be randomly chosen to avoid + + + +Guttman, et al. Standards Track [Page 15] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + duplicate XIDs in requests if UAs restart frequently. + +6.4. Strings in SLP messages + + The escape character is a backslash (UTF-8 0x5c) followed by the two + hexadecimal digits of the escaped character. Only reserved + characters are escaped. For example, a comma (UTF-8 0x29) is escaped + as `\29', and a backslash `\' is escaped as `\5c'. String lists used + in SLP define the comma to be the delimiter between list elements, so + commas in data strings must be escaped in this manner. Backslashes + are the escape character so they also must always be escaped when + included in a string literally. + + String comparison for order and equality in SLP MUST be case + insensitive inside the 0x00-0x7F subrange of UTF-8 (which corresponds + to ASCII character encoding). Case insensitivity SHOULD be supported + throughout the entire UTF-8 encoded Unicode [6] character set. + + The case insensitivity rule applies to all string matching in SLPv2, + including Scope strings, SLP SPI strings, service types, attribute + tags and values in query handling, language tags, previous responder + lists. Comparisons of URL strings, however, is case sensitive. + + White space (SPACE, CR, LF, TAB) internal to a string value is folded + to a single SPACE character for the sake of string comparisons. + White space preceding or following a string value is ignored for the + purposes of string comparison. For example, " Some String " + matches "SOME STRING". + + String comparisons (using comparison operators such as `<=' or `>=') + are done using lexical ordering in UTF-8 encoded characters, not + using any language specific rules. + + The reserved character `*' may precede, follow or be internal to a + string value in order to indicate substring matching. The query + including this character matches any character sequence which + conforms to the letters which are not wildcarded. + +6.4.1. Scope Lists in SLP + + Scope Lists in SLPv2 have the following grammar: + + scope-list = scope-val / scope-val `,' scope-list + scope-val = 1*safe + safe = ; Any character except reserved. + reserved = `(' / `)' / `,' / `\' / `!' / `<' / `=' / `>' / `~' / CTL + / `;' / `*' / `+' + escape-val = `\' HEXDIG HEXDIG + + + +Guttman, et al. Standards Track [Page 16] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + Scopes which include any reserved characters must replace the escaped + character with the escaped-val format. + +7. Errors + + If the Error Code in a SLP reply message is nonzero, the rest of the + message MAY be truncated. No data is necessarily transmitted or + should be expected after the header and the error code, except + possibly for some optional extensions to clarify the error, for + example as in section D.1. + + Errors are only returned for unicast requests. Multicast requests + are silently discarded if they result in an error. + + LANGUAGE_NOT_SUPPORTED = 1: There is data for the service type in + the scope in the AttrRqst or SrvRqst, but not in the requested + language. + PARSE_ERROR = 2: The message fails to obey SLP syntax. + INVALID_REGISTRATION = 3: The SrvReg has problems -- e.g., a zero + lifetime or an omitted Language Tag. + SCOPE_NOT_SUPPORTED = 4: The SLP message did not include a scope in + its <scope-list> supported by the SA or DA. + AUTHENTICATION_UNKNOWN = 5: The DA or SA receives a request for an + unsupported SLP SPI. + AUTHENTICATION_ABSENT = 6: The DA expected URL and ATTR + authentication in the SrvReg and did not receive it. + AUTHENTICATION_FAILED = 7: The DA detected an authentication error in + an Authentication block. + VER_NOT_SUPPORTED = 9: Unsupported version number in message header. + INTERNAL_ERROR = 10: The DA (or SA) is too sick to respond. + DA_BUSY_NOW = 11: UA or SA SHOULD retry, using exponential back off. + OPTION_NOT_UNDERSTOOD = 12: The DA (or SA) received an unknown option + from the mandatory range (see section 9.1). + INVALID_UPDATE = 13: The DA received a SrvReg without FRESH set, for + an unregistered service or with inconsistent Service Types. + MSG_NOT_SUPPORTED = 14: The SA received an AttrRqst or SrvTypeRqst + and does not support it. + REFRESH_REJECTED = 15: The SA sent a SrvReg or partial SrvDereg to a + DA more frequently than the DA's min-refresh-interval. + +8. Required SLP Messages + + All length fields in SLP messages are in network byte order. Where ' + tuples' are defined, these are sequences of bytes, in the precise + order listed, in network byte order. + + SLP messages all begin with the following header: + + + + +Guttman, et al. Standards Track [Page 17] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Version | Function-ID | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length, contd.|O|F|R| reserved |Next Ext Offset| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Extension Offset, contd.| XID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Language Tag Length | Language Tag \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Message Type Abbreviation Function-ID + + Service Request SrvRqst 1 + Service Reply SrvRply 2 + Service Registration SrvReg 3 + Service Deregister SrvDeReg 4 + Service Acknowledge SrvAck 5 + Attribute Request AttrRqst 6 + Attribute Reply AttrRply 7 + DA Advertisement DAAdvert 8 + Service Type Request SrvTypeRqst 9 + Service Type Reply SrvTypeRply 10 + SA Advertisement SAAdvert 11 + + SAs and UAs MUST support SrvRqst, SrvRply and DAAdvert. SAs MUST + also support SrvReg, SAAdvert and SrvAck. For UAs and SAs, support + for other messages are OPTIONAL. + + - Length is the length of the entire SLP message, header included. + - The flags are: OVERFLOW (0x80) is set when a message's length + exceeds what can fit into a datagram. FRESH (0x40) is set on + every new SrvReg. REQUEST MCAST (0x20) is set when multicasting + or broadcasting requests. Reserved bits MUST be 0. + - Next Extension Offset is set to 0 unless extensions are used. + The first extension begins at 'offset' bytes, from the message's + beginning. It is placed after the SLP message data. See + Section 9.1 for how to interpret unrecognized SLP Extensions. + - XID is set to a unique value for each unique request. If the + request is retransmitted, the same XID is used. Replies set + the XID to the same value as the xid in the request. Only + unsolicited DAAdverts are sent with an XID of 0. + - Lang Tag Length is the length in bytes of the Language Tag field. + - Language Tag conforms to [7]. The Language Tag in a reply MUST + be the same as the Language Tag in the request. This field must + be encoded 1*8ALPHA *("-" 1*8ALPHA). + + + + +Guttman, et al. Standards Track [Page 18] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + If an option is specified, and not included in the message, the + receiver MUST respond with a PARSE_ERROR. + +8.1. Service Request + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvRqst = 1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <PRList> | <PRList> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <service-type> | <service-type> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <scope-list> | <scope-list> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of predicate string | Service Request <predicate> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <SLP SPI> string | <SLP SPI> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + In order for a Service to match a SrvRqst, it must belong to at least + one requested scope, support the requested service type, and match + the predicate. If the predicate is present, the language of the + request (ignoring the dialect part of the Language Tag) must match + the advertised service. + + <PRList> is the Previous Responder List. This <string-list> contains + dotted decimal notation IP (v4) addresses, and is iteratively + multicast to obtain all possible results (see Section 6.3). UAs + SHOULD implement this discovery algorithm. SAs MUST use this to + discover all available DAs in their scope, if they are not already + configured with DA addresses by some other means. + + A SA silently drops all requests which include the SA's address in + the <PRList>. An SA which has multiple network interfaces MUST check + if any of the entries in the <PRList> equal any of its interfaces. + An entry in the PRList which does not conform to an IPv4 dotted + decimal address is ignored: The rest of the <PRList> is processed + normally and an error is not returned. + + Once a <PRList> plus the request exceeds the path MTU, multicast + convergence stops. This algorithm is not intended to find all + instances; it finds 'enough' to provide useful results. + + The <scope-list> is a <string-list> of configured scope names. SAs + and DAs which have been configured with any of the scopes in this + list will respond. DAs and SAs MUST reply to unicast requests with a + + + +Guttman, et al. Standards Track [Page 19] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + SCOPE_NOT_SUPPORTED error if the <scope-list> is omitted or fails to + include a scope they support (see Section 11). The only exceptions + to this are described in Section 11.2. + + The <service-type> string is discussed in Section 4. Normally, a + SrvRqst elicits a SrvRply. There are two exceptions: If the + <service-type> is set to "service:directory-agent", DAs respond to + the SrvRqst with a DAAdvert (see Section 8.5.) If set to + "service:service-agent", SAs respond with a SAAdvert (see Section + 8.6.) If this field is omitted, a PARSE_ERROR is returned - as this + field is REQUIRED. + + The <predicate> is a LDAPv3 search filter [14]. This field is + OPTIONAL. Services may be discovered simply by type and scope. + Otherwise, services are discovered which satisfy the <predicate>. If + present, it is compared to each registered service. If the attribute + in the filter has been registered with multiple values, the filter is + compared to each value and the results are ORed together, i.e., + "(x=3)" matches a registration of (x=1,2,3); "(!(Y=0))" matches + (y=0,1) since Y can be nonzero. Note the matching is case + insensitive. Keywords (i.e., attributes without values) are matched + with a "presence" filter, as in "(keyword=*)". + + An incoming request term MUST have the same type as the attribute in + a registration in order to match. Thus, "(x=33)" will not match ' + x=true', etc. while "(y=foo)" will match 'y=FOO'. + "(|(x=33)(y=foo))" will be satisfied, even though "(x=33)" cannot be + satisfied, because of the `|' (boolean disjunction). + + Wildcard matching MUST be done with the '=' filter. In any other + case, a PARSE_ERROR is returned. Request terms which include + wildcards are interpreted to be Strings. That is, (x=34*) would + match 'x=34foo', but not 'x=3432' since the first value is a String + while the second value is an Integer; Strings don't match Integers. + + Examples of Predicates follow. <t> indicates the service type of the + SrvRqst, <s> gives the <scope-list> and <p> is the predicate string. + + <t>=service:http <s>=DEFAULT <p>= (empty string) + This is a minimal request string. It matches all http + services advertised with the default scope. + + <t>=service:pop3 <s>=SALES,DEFAULT <p>=(user=wump) + This is a request for all pop3 services available in + the SALES or DEFAULT scope which serve mail to the user + `wump'. + + + + + +Guttman, et al. Standards Track [Page 20] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + <t>=service:backup <s>=BLDG 32 <p>=(&(q<=3)(speed>=1000)) + This returns the backup service which has a queue length + less than 3 and a speed greater than 1000. It will + return this only for services registered with the BLDG 32 + scope. + + <t>=service:directory-agent <s>=DEFAULT <p>= + This returns DAAdverts for all DAs in the DEFAULT scope. + + DAs are discovered by sending a SrvRqst with the service type set to + "service:directory-agent". If a predicate is included in the + SrvRqst, the DA SHOULD respond only if the predicate can be satisfied + with the DA's attributes. The <scope-list> MUST contain all scopes + configured for the UA or SA which is discovering DAs. + + The <SLP SPI> string indicates a SLP SPI that the requester has been + configured with. If this string is omitted, the responder does not + include any Authentication Blocks in its reply. If it is included, + the responder MUST return a reply which has an associated + authentication block with the SLP SPI in the SrvRqst. If no replies + may be returned because the SLP SPI is not supported, the responder + returns an AUTHENTICATION_UNKNOWN error. + +8.2. Service Reply + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvRply = 2) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Error Code | URL Entry count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | <URL Entry 1> ... <URL Entry N> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The service reply contains zero or more URL entries (see Section + 4.3). A service reply with zero URL entries MUST be returned in + response to a unicast Service Request, if no matching URLs are + present. A service reply with zero URL entries MUST NOT be sent in + response to a multicast or broadcast service request (instead, if + there was no match found or an error processing the request, the + service reply should not be generated at all). + + If the reply overflows, the UA MAY simply use the first URL Entry in + the list. A URL obtained by SLP may not be cached longer than + Lifetime seconds, unless there is a URL Authenticator block present. + + + + + +Guttman, et al. Standards Track [Page 21] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + In that case, the cache lifetime is indicated by the Timestamp in the + URL Authenticator (see Section 9.2). + + An authentication block is returned in the URL Entries, including the + SLP SPI in the SrvRqst. If no SLP SPI was included in the request, + no Authentication Blocks are returned in the reply. URL + Authentication Blocks are defined in Section 9.2.1. + + If a SrvRply is sent by UDP, a URL Entry MUST NOT be included unless + it fits entirely without truncation. + +8.3. Service Registration + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvReg = 3) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | <URL-Entry> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of service type string | <service-type> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <scope-list> | <scope-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of attr-list string | <attr-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |# of AttrAuths |(if present) Attribute Authentication Blocks...\ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The <entry> is a URL Entry (see section 4.3). The Lifetime defines + how long a DA can cache the registration. SAs SHOULD reregister + before this lifetime expires (but SHOULD NOT more often than once per + second). The Lifetime MAY be set to any value between 0 and 0xffff + (maximum, around 18 hours). Long-lived registrations remain stale + longer if the service fails and the SA does not deregister the + service. + + The <service-type> defines the service type of the URL to be + registered, regardless of the scheme of the URL. The <scope-list> + MUST contain the names of all scopes configured for the SA, which the + DA it is registering with supports. The default value for the + <scope-list> is "DEFAULT" (see Section 11). + + The SA MUST register consistently with all DAs. If a SA is + configured with scopes X and Y and there are three DAs, whose scopes + are "X", "Y" and "X,Y" respectively, the SA will register the with + all three DAs in their respective scopes. All future updates and + deregistrations of the service must be sent to the same set of DAs in + + + +Guttman, et al. Standards Track [Page 22] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + the same scopes the service was initially registered in. + + The <attr-list>, if present, specifies the attributes and values to + be associated with the URL by the DA (see Section 5). + + A SA configured with the ability to sign service registrations MUST + sign each of the URLs and Attribute Lists using each of the keys it + is configured to use, and the DA it is registering with accepts. + (The SA MUST acquire DAAdverts for all DAs it will register with to + obtain the DA's SLP SPI list and attributes, as described in Section + 8.5). The SA supplies a SLP SPI in each authentication block + indicating the SLP SPI configuration required to verify the digital + signature. The format of the digital signatures used is defined in + section 9.2.1. + + Subsequent registrations of previously registered services MUST + contain the same list of SLP SPIs as previous ones or else DAs will + reject them, replying with an AUTHENTICATION_ABSENT error. + + A registration with the FRESH flag set will replace *entirely* any + previous registration for the same URL in the same language. If the + FRESH flag is not set, the registration is an "incremental" + registration (see Section 9.3). + +8.4. Service Acknowledgment + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvAck = 5) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Error Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + A DA returns a SrvAck to an SA after a SrvReg. It carries only a two + byte Error Code (see Section 7). + + + + + + + + + + + + + + + +Guttman, et al. Standards Track [Page 23] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +8.5. Directory Agent Advertisement + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = DAAdvert = 8) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Error Code | DA Stateless Boot Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |DA Stateless Boot Time,, contd.| Length of URL | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + \ URL \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <scope-list> | <scope-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <attr-list> | <attr-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <SLP SPI List> | <SLP SPI List> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | # Auth Blocks | Authentication block (if any) \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The Error Code is set to 0 when the DAAdvert is multicast. If the + DAAdvert is being returned due to a unicast SrvRqst (ie. a request + without the REQUEST MCAST flag set) the DA returns the same errors a + SrvRply would. + + The <scope-list> of the SrvRqst must either be omitted or include a + scope which the DA supports. The DA Stateless Boot Timestamp + indicates the state of the DA (see section 12.1). + + The DA MAY include a list of its attributes in the DAAdvert. This + list SHOULD be kept short, as the DAAdvert must fit into a datagram + in order to be multicast. + + A potential scaling problem occurs in SLPv2 if SAs choose too low a + Lifetime. In this case, an onerous amount of reregistration occurs + as more services are deployed. SLPv2 allows DAs to control SAs + frequency of registration. A DA MAY reissue a DAAdvert with a new + set of attributes at any time, to change the reregistration behavior + of SAs. These apply only to subsequent registrations; existing + service registrations with the DA retain their registered lifetimes. + + If the DAAdvert includes the attribute "min-refresh-interval" it MUST + be set to a single Integer value indicating a number of seconds. If + this attribute is present SAs MUST NOT refresh any particular service + advertisement more frequently than this value. If SrvReg with the + FRESH FLAG not set or SrvDereg with a non-empty tag list updating a + + + +Guttman, et al. Standards Track [Page 24] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + particular service are received more often than the value for the + DA's advertised "min-refresh-interval" attribute the DA SHOULD reject + the message and return a REFRESH_REJECTED error in the SrvAck. + + The URL is "service:directory-agent://"<addr> of the DA, where <addr> + is the dotted decimal numeric address of the DA. The <scope-list> of + the DA MUST NOT be NULL. + + The SLP SPI List is the list of SPIs that the DA is capable of + verifying. SAs MUST NOT register services with authentication blocks + for those SLP SPIs which are not on the list. DAs will reject + service registrations which they cannot verify, returning an + AUTHENTICATION_UNKNOWN error. + + The format of DAAdvert signatures is defined in Section 9.2.1. + + The SLP SPI which is used to verify the DAAdvert is included in the + Authentication Block. When DAAdverts are multicast, they may have to + transmit multiple DAAdvert Authentication Blocks. If the DA is + configured to be able to generate signatures for more than one SPI, + the DA MUST include one Authentication Block for each SPI. If all + these Authentication Blocks do not fit in a single datagram (to + multicast or broadcast) the DA MUST send separate DAAdverts so that + Authentication Blocks for all the SPIs the DA is capable of + generating are sent. + + If the DAAdvert is being sent in response to a SrvRqst, the DAAdvert + contains only the authentication block with the SLP SPI in the + SrvRqst, if the DA is configured to be able to produce digital + signatures using that SLP SPI. If the SrvRqst is unicast to the DA + (the REQUEST MCAST flag in the header is not set) and an unsupported + SLP SPI is included, the DA replies with a DAAdvert with the Error + Code set to an AUTHENTICATION_UNKNOWN error. + + UAs SHOULD be configured with SLP SPIs that will allow them to verify + DA Advertisements. If the UA is configured with SLP SPIs and + receives a DAAdvert which fails to be verified using one of them, the + UA MUST discard it. + +8.6. Service Agent Advertisement + + User Agents MUST NOT solicit SA Advertisements if they have been + configured to use a particular DA, if they have been configured with + a <scope-list> or if DAs have been discovered. UAs solicit SA + Advertisements only when they are explicitly configured to use User + Selectable scopes (see Section 11.2) in order to discover the scopes + that SAs support. This allows UAs without scope configuration to + make use of either DAs or SAs without any functional difference + + + +Guttman, et al. Standards Track [Page 25] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + except performance. + + A SA MAY be configured with attributes, and SHOULD support the + attribute 'service-type' whose value is all the service types of + services represented by the SA. SAs MUST NOT respond if the SrvRqst + predicate is not satisfied. For example, only SAs offering 'nfs' + services SHOULD respond with a SAAdvert to a SrvRqst for service type + "service:service-agent" which includes a predicate "(service- + type=nfs)". + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SAAdvert = 11) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of URL | URL \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <scope-list> | <scope-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <attr-list> | <attr-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | # auth blocks | authentication block (if any) \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The SA responds only to multicast SA discovery requests which either + include no <scope-list> or a scope which they are configured to use. + + The SAAdvert MAY include a list of attributes the SA supports. This + attribute list SHOULD be kept short so that the SAAdvert will not + exceed the path MTU in size. + + The URL is "service:service-agent://"<addr> of the SA, where <addr> + is the dotted decimal numeric address of the SA. The <scope-list> of + the SA MUST NOT be null. + + The SAAdvert contains one SAAdvert Authentication block for each SLP + SPI the SA can produce Authentication Blocks for. If the UA can + verify the Authentication Block of the SAAdvert, and the SAAdvert + fails to be verified, the UA MUST discard it. + +9. Optional Features + + The features described in this section are not mandatory. Some are + useful for interactive use of SLP (where a user rather than a program + will select services, using a browsing interface for example) and for + scalability of SLP to larger networks. + + + + + +Guttman, et al. Standards Track [Page 26] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +9.1. Service Location Protocol Extensions + + The format of a Service Location Extension is: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Extension ID | Next Extension Offset | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Offset, contd.| Extension Data \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Extension IDs are assigned in the following way: + + 0x0000-0x3FFF Standardized. Optional to implement. Ignore if + unrecognized. + 0x4000-0x7FFF Standardized. Mandatory to implement. A UA or SA + which receives this option in a reply and does not understand + it MUST silently discard the reply. A DA or SA which receives + this option in a request and does not understand it MUST return + an OPTION_NOT_UNDERSTOOD error. + 0x8000-0x8FFF For private use (not standardized). Optional to + implement. Ignore if unrecognized. + 0x9000-0xFFFF Reserved. + + The three byte offset to next extension indicates the position of the + next extension as offset from the beginning of the SLP message. + + The offset value is 0 if there are no extensions following the + current extension. + + If the offset is 0, the length of the current Extension Data is + determined by subtracting total length of the SLP message as given in + the SLP message header minus the offset of the current extension. + + Extensions defined in this document are in Section D. See section 15 + for procedures that are required when specifying new SLP extensions. + + + + + + + + + + + + + + +Guttman, et al. Standards Track [Page 27] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +9.2. Authentication Blocks + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Block Structure Descriptor | Authentication Block Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SLP SPI String Length | SLP SPI String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Structured Authentication Block ... \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Authentication blocks are returned with certain SLP messages to + verify that the contents have not been modified, and have been + transmitted by an authorized agent. The authentication data + (contained in the Structured Authentication Block) is typically case + sensitive. Even though SLP registration data (e.g., attribute + values) are typically are not case sensitive, the case of the + registration data has to be preserved by the registering DA so that + UAs will be able to verify the data used for calculating digital + signature data. + + The Block Structure Descriptor (BSD) identifies the format of the + Authenticator which follows. BSDs 0x0000-0x7FFF will be maintained + by IANA. BSDs 0x8000-0x8FFF are for private use. + + The Authentication Block Length is the length of the entire block, + starting with the BSD. + + The Timestamp is the time that the authenticator expires (to prevent + replay attacks.) The Timestamp is a 32-bit unsigned fixed-point + number of seconds relative to 0h on 1 January 1970. SAs use this + value to indicate when the validity of the digital signature expires. + This Timestamp will wrap back to 0 in the year 2106. Once the value + of the Timestamp wraps, the time at which the Timestamp is relative + to resets. For example, after 06h28 and 16 seconds 5 February 2106, + all Timestamp values will be relative to that epoch date. + + The SLP Security Parameters Index (SPI) string identifies the key + length, algorithm parameters and keying material to be used by agents + to verify the signature data in the Structured Authentication Block. + The SLP SPI string has the same grammar as the <scope-val> defined in + Section 6.4.1. + + Reserved characters in SLP SPI strings must be escaped using the same + convention as used throughout SLPv2. + + + +Guttman, et al. Standards Track [Page 28] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + SLP SPIs deployed in a site MUST be unique. An SLP SPI used for + BSD=0x0002 must not be the same as used for some other BSD. + + All SLP agents MUST implement DSA [20] (BSD=0x0002). SAs MUST + register services with DSA authentication blocks, and they MAY + register them with other authentication blocks using other + algorithms. SAs MUST use DSA authentication blocks in SrvDeReg + messages and DAs MUST use DSA authentication blocks in unsolicited + DAAdverts. + +9.2.1. SLP Message Authentication Rules + + The sections below define how to calculate the value to apply to the + algorithm identified by the BSD value. The components listed are + used as if they were a contiguous single byte aligned buffer in the + order given. + + URL + 16-bit Length of SLP SPI String, SLP SPI String. + 16-bit Length of URL, URL, + 32-bit Timestamp. + + Attribute List + 16-bit Length of SLP SPI String, SLP SPI String, + 16-bit length of <attr-list>, <attr-list>, + 32-bit Timestamp. + + DAAdvert + 16-bit Length of SLP SPI String, SLP SPI String, + 32-bit DA Stateless Boot Timestamp, + 16-bit Length of URL, URL, + 16-bit Length of <attr-list>, <attr-list>, + 16-bit Length of DA's <scope-list>, DA's <scope-list>, + 16-bit Length of DA's <SLP SPI List>, DA's <SLP SPI List>, + 32-bit Timestamp. + + The first SLP SPI is the SLP SPI in the Authentication + Block. This SLP SPI indicates the keying material and other + parameters to use to verify the DAAdvert. The SLP SPI List is + the list of SLP SPIs the DA itself supports, and is able to + verify. + + SAAdvert + 16-bit Length of SLP SPI String, SLP SPI String, + 16-bit Length of URL, URL, + 16-bit Length of <attr-list>, <attr-list>, + 16-bit Length of <scope-list>, <scope-list>, + 32-bit Timestamp. + + + +Guttman, et al. Standards Track [Page 29] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +9.2.2 DSA with SHA-1 in Authentication Blocks + + BSD=0x0002 is defined to be DSA with SHA-1. The signature + calculation is defined by [20]. The signature format conforms to + that in the X.509 v3 certificate: + + 1. The signature algorithm identifier (an OID) + 2. The signature value (an octet string) + 3. The certificate path. + + All data is represented in ASN.1 encoding: + + id-dsa-with-sha1 ID ::= { + iso(1) member-body(2) us(840) x9-57 (10040) + x9cm(4) 3 } + + i.e., the ASN.1 encoding of 1.2.840.10040.4.3 followed immediately + by: + + Dss-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER } + + i.e., the binary ASN.1 encoding of r and s computed using DSA and + SHA-1. This is followed by a certificate path, as defined by X.509 + [10], [2], [3], [4], [5]. + + Authentication Blocks for BSD=0x0002 have the following format. In + the future, BSDs may be assigned which have different formats. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ASN.1 encoded DSA signature \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +9.3. Incremental Service Registration + + Incremental registrations update attribute values for a previously + registered service. Incremental service registrations are useful + when only a single attribute has changed, for instance. In an + incremental registration, the FRESH flag in the SrvReg header is NOT + set. + + The new registration's attributes replace the previous + registration's, but do not affect attributes which were included + previously and are not present in the update. + + + + +Guttman, et al. Standards Track [Page 30] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + For example, suppose service:x://a.org has been registered with + attributes A=1, B=2, C=3. If an incremental registration comes for + service:x://a.org with attributes C=30, D=40, then the attributes for + the service after the update are A=1, B=2, C=30, D=40. + + Incremental registrations MUST NOT be performed for services + registered with Authentication Blocks. These must be registered with + ALL attributes, with the FRESH flag in the SrvReg header set. DAs + which receive such registration messages return an + AUTHENTICATION_FAILED error. + + If the FRESH flag is not set and the DA does not have a prior + registration for the service, the incremental registration fails with + error code INVALID_UPDATE. + + The SA MUST use the same <scope-list> in an update message as was + used in the prior registration. If this is not done, the DA returns + a SCOPE_NOT_SUPPORTED error. In order to change the scope of a + service advertisement it MUST be deregistered first and reregistered + with a new <scope-list>. + + The SA MUST use the same <service-type> in an update message as was + used in a prior registration of the same URL. If this is not done, + the DA returns an INVALID_UPDATE error. + +9.4. Tag Lists + + Tag lists are used in SrvDeReg and AttrReq messages. The syntax of a + <tag-list> item is: + + tag-filter = simple-tag / substring + simple-tag = 1*filt-char + substring = [initial] any [final] + initial = 1*filt-char + any = `*' *(filt-char `*') + final = 1*filt-char + filt-char = Any character excluding <reserved> and <bad-tag> (see + grammar in Section 5). + + Wild card characters in a <tag-list> item match arbitrary sequences + of characters. For instance "*bob*" matches "some bob I know", + "bigbob", "bobby" and "bob". + + + + + + + + + +Guttman, et al. Standards Track [Page 31] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +10. Optional SLP Messages + + The additional requests provide features for user interaction and for + efficient updating of service advertisements with dynamic attributes. + +10.1. Service Type Request + + The Service Type Request (SrvTypeRqst) allows a UA to discover all + types of service on a network. This is useful for general purpose + service browsers. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvTypeRqst = 9) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of PRList | <PRList> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of Naming Authority | <Naming Authority String> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <scope-list> | <scope-list> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The <PRList> list and <scope-list> are interpreted as in Section 8.1. + + The Naming Authority string, if present in the request, will limit + the reply to Service Type strings with the specified Naming + Authority. If the Naming Authority string is absent, the IANA + registered service types will be returned. If the length of the + Naming Authority is set to 0xFFFF, the Naming Authority string is + omitted and ALL Service Types are returned, regardless of Naming + Authority. + +10.2. Service Type Reply + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvTypeRply = 10) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Error Code | length of <srvType-list> | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | <srvtype--list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The service-type Strings (as described in Section 4.1) are provided + in <srvtype-list>, which is a <string-list>. + + + + +Guttman, et al. Standards Track [Page 32] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + If a service type has a Naming Authority other than IANA it MUST be + returned following the service type string and a `.' character. + Service types with the IANA Naming Authority do not include a Naming + Authority string. + +10.3. Attribute Request + + The Attribute Request (AttrRqst) allows a UA to discover attributes + of a given service (by supplying its URL) or for an entire service + type. The latter feature allows the UA to construct a query for an + available service by selecting desired features. The UA may request + that all attributes are returned, or only a subset of them. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = AttrRqst = 6) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of PRList | <PRList> String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of URL | URL \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <scope-list> | <scope-list> string \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <tag-list> string | <tag-list> string \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | length of <SLP SPI> string | <SLP SPI> string \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The <PRList>, <scope-list> and <SLP SPI> string are interpreted as in + Section 8.1. + + The URL field can take two forms. It can simply be a Service Type + (see Section 4.1), such as "http" or "service:tftp". In this case, + all attributes and the full range of values for each attribute of all + services of the given Service Type is returned. + + The URL field may alternatively be a full URL, such as + "service:printer:lpr://igore.wco.ftp.com:515/draft" or + "nfs://max.net/znoo". In this, only the registered attributes for + the specified URL are returned. + + The <tag-list> field is a <string-list> of attribute tags, as defined + in Section 9.4 which indicates the attributes to return in the + AttrRply. If <tag-list> is omitted, all attributes are returned. + <tag-list> MUST be omitted and a full URL MUST be included when + attributes when a SLP SPI List string is included, otherwise the DA + will reply with an AUTHENTICATION_FAILED error. + + + +Guttman, et al. Standards Track [Page 33] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +10.4. Attribute Reply + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = AttrRply = 7) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Error Code | length of <attr-list> | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | <attr-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |# of AttrAuths | Attribute Authentication Block (if present) \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The format of the <attr-list> and the Authentication Block is as + specified for SrvReg (see Section 9.2.1). + + Attribute replies SHOULD be returned with the original case of the + string registration intact, as they are likely to be human readable. + In the case where the AttrRqst was by service type, all attributes + defined for the service type, and all their values are returned. + + Although white space is folded for string matching, attribute tags + and values MUST be returned with their original white space + preserved. + + Only one copy of each attribute tag or String value should be + returned, arbitrarily choosing one version (with respect to upper and + lower case and white space internal to the strings): Duplicate + attributes and values SHOULD be removed. An arbitrary version of the + string value and tag name is chosen for the merge. For example: + "(A=a a,b)" merged with "(a=A A,B)" may yield "(a=a a,B)". + +10.5. Attribute Request/Reply Examples + + Suppose that printer services have been registered as follows: + + Registered Service: + URL = service:printer:lpr://igore.wco.ftp.com/draft + scope-list = Development + Lang. Tag = en + Attributes = (Name=Igore),(Description=For developers only), + (Protocol=LPR),(location-description=12th floor), + (Operator=James Dornan \3cdornan@monster\3e), + (media-size=na-letter),(resolution=res-600),x-OK + + URL = service:printer:lpr://igore.wco.ftp.com/draft + scope-list = Development + + + +Guttman, et al. Standards Track [Page 34] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + Lang. Tag = de + Attributes = (Name=Igore),(Description=Nur fuer Entwickler), + (Protocol=LPR),(location-description=13te Etage), + (Operator=James Dornan \3cdornan@monster\3e), + (media-size=na-letter),(resolution=res-600),x-OK + + URL = service:printer:http://not.wco.ftp.com/cgi-bin/pub-prn + scope-list = Development + Lang. Tag = en + Attributes = (Name=Not),(Description=Experimental IPP printer), + (Protocol=http),(location-description=QA bench), + (media-size=na-letter),(resolution=other),x-BUSY + + Notice the first printer, "Igore" is registered in both English and + German. The `<' and `>' characters in the Operator attribute value + which are part of the Email address had to be escaped, as they are + reserved characters for values. + + Attribute tags are not translated, though attribute values may be, + see [13]. + + The attribute Request: + + URL = service:printer:lpr://igore.wco.ftp.com/draft + scope-list = Development + Lang. Tag = de + tag-list = resolution,loc* + + receives the Attribute Reply: + + (location-description=13te Etage),(resolution=res-600) + + The attribute Request: + + URL = service:printer + scope-list = Development + Lang. Tag = en + tag-list = x-*,resolution,protocol + + receives an Attribute Reply containing: + + (protocols=http,LPR),(resolution=res-600,other),x-OK,x-BUSY + + The first request is by service instance and returns the requested + values, in German. The second request is by abstract service type + (see Section 4) and returns values from both "Igore" and "Not". + + + + + +Guttman, et al. Standards Track [Page 35] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + An attribute Authentication Block is returned if an authentication + block with the SLP SPI in the AttrRqst can be returned. Note that + the <attr-list> returned from a DA with an Authentication Block MUST + be identical to the <attr-list> registered by a SA, in order for the + authentication verification calculations to be possible. + + A SA or DA only returns an Attribute Authentication Block if the + AttrRqst included a full URL in the request and no tag list. + + If an SLP SPI is specified in a unicast request (the REQUEST MCAST + flag in the header is not set) and the SA or DA cannot return an + Authentication Block with that SLP SPI, an AUTHENTICATION_UNKNOWN + error is returned. The # of Attr Auths field is set to 0 if there no + Authentication Block is included, or 1 if an Authentication Block + follows. + +10.6. Service Deregistration + + A DA deletes a service registration when its Lifetime expires. + Services SHOULD be deregistered when they are no longer available, + rather than leaving the registrations to time out. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Service Location header (function = SrvDeReg = 4) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <scope-list> | <scope-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | URL Entry \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length of <tag-list> | <tag-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The <scope-list> is a <string-list> (see section 2.1). + + The SA MUST retry if there is no response from the DA, see Section + 12.3. The DA acknowledges a SrvDeReg with a SrvAck. Once the SA + receives an acknowledgment indicating success, the service and/or + attributes are no longer advertised by the DA. The DA deregisters the + service or service attributes from every scope specified in the + SrvDeReg which it was previously registered in. + + The SA MUST deregister all services with the same scope list used to + register the service with a DA. If this is not done in the SrvDeReg + message, the DA returns a SCOPE_NOT_SUPPORTED error. The Lifetime + field in the URL Entry is ignored for the purposes of the SrvDeReg. + + + + +Guttman, et al. Standards Track [Page 36] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + The <tag-list> is a <string-list> of attribute tags to deregister as + defined in Section 9.4. If no <tag-list> is present, the SrvDeReg + deregisters the service in all languages it has been registered in. + If the <tag-list> is present, the SrvDeReg deregisters the attributes + whose tags are listed in the tag spec. Services registered with + Authentication Blocks MUST NOT include a <tag-list> in a SrvDeReg + message: A DA will respond with an AUTHENTICATION_FAILED error in + this case. + + If the service to be deregistered was registered with an + authentication block or blocks, a URL authentication block for each + of the SLP SPIs registered must be included in the SrvDeReg. + Otherwise, the DA returns an AUTHENTICATION_ABSENT error. If the + message fails to be verified by the DA, an AUTHENTICATION_FAILED + error is returned by the DA. + +11. Scopes + + Scopes are sets of services. The primary use of Scopes is to provide + the ability to create administrative groupings of services. A set of + services may be assigned a scope by network administrators. A client + seeking services is configured to use one or more scopes. The user + will only discover those services which have been configured for him + or her to use. By configuring UAs and SAs with scopes, + administrators may provision services. Scopes strings are case + insensitive. The default SCOPE string is "DEFAULT". + + Scopes are the primary means an administrator has to scale SLP + deployments to larger networks. When DAs with NON-DEFAULT scopes are + present on the network, further gains can be had by configuring UAs + and SAs to have a predefined non-default scope. These agents can + then perform DA discovery and make requests using their scope. This + will limit the number of replies. + +11.1. Scope Rules + + SLP messages which fail to contain a scope that the receiving Agent + is configured to use are dropped (if the request was multicast) or a + SCOPE_NOT_SUPPORTED error is returned (if the request was unicast). + Every SrvRqst (except for DA and SA discovery requests), SrvReg, + AttrRqst, SrvTypeRqst, DAAdvert, and SAAdvert message MUST include a + <scope-list>. + + A UA MUST unicast its SLP messages to a DA which supports the desired + scope, in preference to multicasting a request to SAs. A UA MAY + multicast the request if no DA is available in the scope it is + configured to use. + + + + +Guttman, et al. Standards Track [Page 37] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +11.2. Administrative and User Selectable Scopes + + All requests and services are scoped. The two exceptions are + SrvRqsts for "service:directory-agent" and "service:service-agent". + These MAY have a zero-length <scope-list> when used to enable the + user to make scope selections. In this case UAs obtain their scope + list from DAAdverts (or if DAs are not available, from SAAdverts.) + + Otherwise, if SAs and UAs are to use any scope other than the default + (i.e., "DEFAULT"), the UAs and SAs are configured with lists of + scopes to use by system administrators, perhaps automatically by way + of DHCP option 78 or 79 [21]. Such administrative scoping allows + services to be provisioned, so that users will only see services they + are intended to see. + + User configurable scopes allow a user to discover any service, but + require them to do their own selection of scope. This is similar to + the way AppleTalk [12] and SMB [19] networking allow user selection + of AppleTalk Zone or workgroups. + + Note that the two configuration choices are not compatible. One + model allows administrators control over service provision. The + other delegates this to users (who may not be prepared to do any + configuration of their system). + +12. Directory Agents + + DAs cache service location and attribute information. They exist to + enhance the performance and scalability of SLP. Multiple DAs provide + further scalability and robustness of operation, since they can each + store service information for the same SAs, in case one of the DAs + fails. + + A DA provides a centralized store for service information. This is + useful in a network with several subnets or with many SLP Agents. + The DA address can be dynamically configured with UAs and SAs using + DHCP, or by using static configuration. + + SAs configured to use DAs with DHCP or static configuration MUST + unicast a SrvRqst to the DA, when the SA is initialized. The SrvRqst + omits the scope list and sets the service type of the request to + "service:directory-agent". The DA will return a DAAdvert with its + attributes, SLP SPI list, and other parameters which are essential + for proper SA to DA communication. + + Passive detection of DAs by SAs enables services to be advertised + consistently among DAs of the same scope. Advertisements expire if + not renewed, leaving only transient stale registrations in DAs, even + + + +Guttman, et al. Standards Track [Page 38] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + in the case of a failure of a SA. + + A single DA can support many UAs. UAs send the same requests to DAs + that they would send to SAs and expect the same results. DAs reduce + the load on SAs, making simpler implementations of SAs possible. + + UAs MUST be prepared for the possibility that the service information + they obtain from DAs is stale. + +12.1. Directory Agent Rules + + When DAs are present, each SA MUST register its services with DAs + that support one or more of its scope(s). + + UAs MUST unicast requests directly to a DA (when scoping rules + allow), hence avoiding using the multicast convergence algorithm, to + obtain service information. This decreases network utilization and + increases the speed at which UAs can obtain service information. + + DAs MUST flush service advertisements once their lifetime expires or + their URL Authentication Block "Timestamp" of expiration is past. + + DAAdverts MUST include DA Stateless Boot Timestamp, in the same + format as the Authentication Block (see Section 9.2). The Timestamp + in the Authentication Block indicates the time at which all previous + registrations were lost (i.e., the last stateless reboot). The + Timestamp is set to 0 in a DAAdvert to notify UAs and SAs that the DA + is going down. DAs MUST NOT use equal or lesser Boot Timestamps to + previous ones, if they go down and restart without service + registration state. This would mislead SAs to not reregister with + the DA. + + DAs which receive a multicast SrvRqst for the service type + "service:directory-agent" MUST silently discard it if the <scope- + list> is (a) not omitted and (b) does not include a scope they are + configured to use. Otherwise the DA MUST respond with a DAAdvert. + + DAs MUST respond to AttrRqst and SrvTypeRqst messages (these are + OPTIONAL only for SAs, not DAs.) + +12.2. Directory Agent Discovery + + UAs can discover DAs using static configuration, DHCP options 78 and + 79, or by multicasting (or broadcasting) Service Requests using the + convergence algorithm in Section 6.3. + + + + + + +Guttman, et al. Standards Track [Page 39] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + See Section 6 regarding unsolicited DAAdverts. Section 12.2.2 + describes how SAs may reduce the number of times they must reregister + with DAs in response to unsolicited DAAdverts. + + DAs MUST send unsolicited DAAdverts once per CONFIG_DA_BEAT. An + unsolicited DAAdvert has an XID of 0. SAs MUST listen for DAAdverts, + passively, as described in Section 8.5. UAs MAY do this. If they do + not listen for unsolicited DAAdverts, however, they will not discover + DAs as they become available. UAs SHOULD, in this case, do periodic + active DA discovery, see Section 6. + + A URL with the scheme "service:directory-agent" indicates the DA's + location as defined in Section 8.5. For example: + "service:directory-agent://foobawooba.org". + + The following sections suggest timing algorithms which enhance the + scalability of SLP. + +12.2.1. Active DA Discovery + + After a UA or SA restarts, its initial DA discovery request SHOULD be + delayed for some random time uniformly distributed from 0 to + CONFIG_START_WAIT seconds. + + The UA or SA sends the DA Discovery request using a SrvRqst, as + described in Section 8.1. DA Discovery requests MUST include a + Previous Responder List. SrvRqsts for Active DA Discovery SHOULD NOT + be sent more than once per CONFIG_DA_FIND seconds. + + After discovering a new DA, a SA MUST wait a random time between 0 + and CONFIG_REG_ACTIVE seconds before registering their services. + +12.2.2. Passive DA Advertising + + A DA MUST multicast (or broadcast) an unsolicited DAAdvert every + CONFIG_DA_BEAT seconds. CONFIG_DA_BEAT SHOULD be specified to + prevent DAAdverts from using more than 1% of the available bandwidth. + + All UAs and SAs which receive the unsolicited DAAdvert SHOULD examine + its DA stateless Boot Timestamp. If it is set to 0, the DA is going + down and no further messages should be sent to it. + + If a SA detects a DA it has never encountered (with a nonzero + timestamp,) the SA must register with it. SAs MUST examine the + DAAdvert's timestamp to determine if the DA has had a stateless + reboot since the SA last registered with it. If so it registers with + the DA. SAs MUST wait a random interval between 0 and + CONFIG_REG_PASSIVE before beginning DA registration. + + + +Guttman, et al. Standards Track [Page 40] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +12.3. Reliable Unicast to DAs and SAs + + If a DA or SA fails to respond to a unicast UDP message in + CONFIG_RETRY seconds, the message should be retried. The wait + interval for each subsequent retransmission MUST exponentially + increase, doubling each time. If a DA or SA fails to respond after + CONFIG_RETRY_MAX seconds, the sender should consider the receiver to + have gone down. The UA should use a different DA. If no such DA + responds, DA discovery should be used to find a new DA. If no DA is + available, multicast requests to SAs are used. + +12.4. DA Scope Configuration + + By default, DAs are configured with the "DEFAULT" scope. + Administrators may add other configured scopes, in order to support + UAs and SAs in non default scopes. The default configuration MUST + NOT be removed from the DA unless: + + - There are other DAs which support the "DEFAULT" scope, or + + - All UAs and SAs have been configured with non-default scopes. + + Non-default scopes can be phased-in as the SLP deployment grows. + Default scopes should be phased out only when the non-default scopes + are universally configured. + + If a DA and SA are coresident on a host (quite possibly implemented + by the same process), configuration of the host is considerably + simplified if the SA supports only scopes also supported by the DA. + That is, the SA SHOULD NOT advertise services in any scopes which are + not supported by the coresident DA. This means that incoming requests + can be answered by a single data store; the SA and DA registrations + do not need to be kept separately. + +12.5. DAs and Authentication Blocks + + DAs are not configured to sign service registrations or attribute + lists. They simply cache services registered by Service Agents. DAs + MUST NOT accept registrations including authentication blocks for SLP + SPIs which it is not configured with, see Section 8.5. + + A DA protects registrations which are made with authentication blocks + using SLP SPIs it is configured to use. If a service S is + registered, a subsequent registration (which will replace the + adertisement) or a deregistration (which will remove it) MUST include + an Authentication Block with the corresponding SLP SPI, see Section + 8.3 and Section 10.6. + + + + +Guttman, et al. Standards Track [Page 41] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + Example: + + A DA is configured to be able to verify Authentication Blocks with + SLP SPIs "X,Y", that is X and Y. + + An SA registers a service with an Authentication Block with SPI "Z". + The DA stores the registration, but discards the Authentication + Block. If a UA requests a service with an SLP SPI string "Z", the DA + will respond with an AUTHENTICATION_UNKNOWN error. + + An SA registers a service S with Authentication Blocks including SLP + SPIs "X" and "Y". If a UA requests a service with an SLP SPI string + "X" the DA will be able to return S (if the service type, language, + scope and predicate of the SrvRqst match S) The DA will also return + the Authentication Block with SLP SPI set to "X". If the DA receives + a subsequent SrvDeReg for S (which will remove the advertisement) or + a subsequent SrvReg for S (which will replace it), the message must + include two URL Authentication Blocks, one each for SPIs "X" and "Y". + If either of these were absent, the DA would return an + AUTHENTICATION_ABSENT error. + +13. Protocol Timing Defaults + +Interval name Section Default Value Meaning +------------------- ------- ------------- ------------------------ +CONFIG_MC_MAX 6.3 15 seconds Max time to wait for a + complete multicast query + response (all values.) +CONFIG_START_WAIT 12.2.1 3 seconds Wait to perform DA + discovery on reboot. +CONFIG_RETRY 12.3 2 seconds Wait interval before + initial retransmission + of multicast or unicast + requests. +CONFIG_RETRY_MAX 12.3 15 seconds Give up on unicast + request retransmission. +CONFIG_DA_BEAT 12.2.2 3 hours DA Heartbeat, so that SAs + passively detect new DAs. +CONFIG_DA_FIND 12.3 900 seconds Minimum interval to wait + before repeating Active + DA discovery. +CONFIG_REG_PASSIVE 12.2 1-3 seconds Wait to register services + on passive DA discovery. +CONFIG_REG_ACTIVE 8.3 1-3 seconds Wait to register services + on active DA discovery. +CONFIG_CLOSE_CONN 6.2 5 minutes DAs and SAs close idle + connections. + + + + +Guttman, et al. Standards Track [Page 42] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +14. Optional Configuration + + Broadcast Only + Any SLP agent SHOULD be configurable to use broadcast + only. See Sections 6.1 and 12.2. + + Predefined DA + A UA or SA SHOULD be configurable to use a predefined DA. + + No DA Discovery + The UA or SA SHOULD be configurable to ONLY use + predefined and DHCP-configured DAs and perform no active + or passive DA discovery. + + Multicast TTL + The default multicast TTL is 255. Agents SHOULD be + configurable to use other values. A lower value will + focus the multicast convergence algorithm on smaller + subnetworks, decreasing the number of responses and + increases the performance of service location. This + may result in UAs obtaining different results for the + identical requests depending on where they are connected + to the network. + + Timing Values + Time values other than the default MAY be configurable. + See Section 13. + + Scopes + A UA MAY be configurable to support User Selectable + scopes by omitting all predefined scopes. See + Section 11.2. A UA or SA MUST be configurable to use + specific scopes by default. Additionally, a UA or SA + MUST be configurable to use specific scopes for requests + for and registrations of specific service types. The + scope or scopes of a DA MUST be configurable. The + default value for a DA is to have the scope "DEFAULT" if + not otherwise configured. + + DHCP Configuration + DHCP options 78 and 79 may be used to configure SLP. If + DA locations are configured using DHCP, these SHOULD + be used in preference to DAs discovered actively or + passively. One or more of the scopes configured using + DHCP MUST be used in requests. The entire configured + <scope-list> MUST be used in registration and DA + configuration messages. + + + + +Guttman, et al. Standards Track [Page 43] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + Service Template + UAs and SAs MAY be configured by using Service Templates. + Besides simplifying the specification of attribute + values, this also allows them to enforce the inclusion + of 'required' attributes in SrvRqst, SrvReg and SrvDeReg + messages. DAs MAY be configured with templates to + allow them to WARN UAs and SAs in these cases. See + Section 10.4. + + SLP SPI for service discovery + Agents SHOULD be configurable to support SLP SPIs using + the following parameters: BSD=2 (DSA with SHA-1) and + a public key identified by the SLP SPI String. In + the future, when a Public Key Infrastructure exists, + SLP Agents may be able to obtain public keys and + cryptographic parameters corresponding to the names used + in SLP SPI Strings. + + Note that if the SLP SPI string chosen is identical + to a scope string, it is effectively the same as a + Protected Scope in SLPv1. Namely, every SA advertising + in that scope would be configured with the same Private + Key. Every DA and UA of that scope would be configured + with the appropriate Public Key to verify signatures + produced by those SAs. This is a convenient way to + configure SLP deployments in the absence of a Public Key + Infrastructure. Currently, it would be too difficult to + manage the keying of UAs and DAs if each SA had its own + key. + + SLP SPI for Directory Agent discovery + Agents SHOULD be configurable to support SLP SPIs as + above, to be used when discovering DAs. This SPI SHOULD + be sent in SrvRqsts to discover DAs and be used to verify + multicast DAAdvert messages. + + SA and DA Private Key + SAs and DAs which can generate digital signatures require + a Private Key and a corresponding SLP SPI indentifier + to include in the Authentication Block. The SLP SPI + identifies the Public Key to use to verify the digital + signature in the Authentication Block. + +15. IANA Considerations + + SLP includes four sets of identifiers which may be registered with + IANA. The policies for these registrations (See [18]) are noted in + each case. + + + +Guttman, et al. Standards Track [Page 44] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + The Block Structure Descriptor (BSD) identifies the format of the + Authenticator which follows. BSDs 0x8000-0x8FFF are for Private Use. + + Further Block Structured Descriptor (BSD) values, from the range + 0x0003-0x7FFF may be standardized in the future by submitting a + document which describes: + + - The data format of the Structured Authenticator block. + + - Which cryptographic algorithm to use (including a reference + to a technical specification of the algorithm.) + + - The format of any keying material required for + preconfiguring UAs, DAs and SAs. Also include any + considerations regarding key distribution. + + - Security considerations to alert others to the strengths and + weaknesses of the approach. + + The IANA will assign Cryptographic BSD numbers on the basis of IETF + Consenus. + + New function-IDs, in the range 12-255, may be standardized by the + method of IETF Consensus. + + New SLP Extensions with types in the range 2-65535 may be registered + following review by a Designated Expert. + + New error numbers in the range 15-65535 are assigned on the basis of + a Standards Action. + + Protocol elements used with Service Location Protocol may also + require IANA registration actions. SLP is used in conjunction with + "service:" URLs and Service Templates [13]. These are standardized + by review of a Designated Expert and a mailing list (See [13].) + +16. Internationalization Considerations + + SLP messages support the use of multiple languages by providing a + Language Tag field in the common message header (see Section 8). + + Services MAY be registered in multiple languages. This provides + attributes so that users with different language skills may select + services interactively. + + Attribute tags are not translated. Attribute values may be + translated unless the Service Template [13] defines the attribute + values to be 'literal'. + + + +Guttman, et al. Standards Track [Page 45] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + A service which is registered in multiple languages may be queried in + multiple languages. The language of the SrvRqst or AttrRqst is used + to satisfy the request. If the requested language is not supported, + a LANGUAGE_NOT_SUPPORTED error is returned. SrvRply and AttrRply + messages are always in the same language of the request. + + A DA or SA MAY be configured with translations of Service Templates + [13] for the same service type. This will allow the DA or SA to + translate a request (say in Italian) to the language of the service + advertisement (say in English) and then translate the reply back to + Italian. Similarly, a UA MAY use templates to translate outgoing + requests and incoming replies. + + The dialect field in the Language Tag MAY be used: Requests which + can be fulfilled by matching a language and dialect will be preferred + to those which match only the language portion. Otherwise, dialects + have no effect on matching requests. + +17. Security Considerations + + SLP provides for authentication of service URLs and service + attributes. This provides UAs and DAs with knowledge of the + integrity of service URLs and attributes included in SLP messages. + The only systems which can generate digital signatures are those + which have been configured by administrators in advance. Agents + which verify signed data may assume it is 'trustworthy' inasmuch as + administrators have ensured the cryptographic keying of SAs and DAs + reflects 'trustworthiness.' + + Service Location does not provide confidentiality. Because the + objective of this protocol is to advertise services to a community of + users, confidentiality might not generally be needed when this + protocol is used in non-sensitive environments. Specialized schemes + might be able to provide confidentiality, if needed in the future. + Sites requiring confidentiality should implement the IP Encapsulating + Security Payload (ESP) [3] to provide confidentiality for Service + Location messages. + + If Agents are not configured to generate Authentication Blocks and + Agents are not configured to verify them, an adversary might easily + use this protocol to advertise services on servers controlled by the + adversary and thereby gain access to users' private information. + Further, an adversary using this protocol will find it much easier to + engage in selective denial of service attacks. Sites that are in + potentially hostile environments (e.g., are directly connected to the + Internet) should consider the advantages of distributing keys + associated with SLP SPIs prior to deploying the sensitive directory + agents or service agents. + + + +Guttman, et al. Standards Track [Page 46] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + SLP is useful as a bootstrap protocol. It may be used in + environments in which no preconfiguration is possible. In such + situations, a certain amount of "blind faith" is required: Without + any prior configuration it is impossible to use any of the security + mechanisms described above. SLP will make use of the mechanisms + provided by the Security Area of the IETF for key distribution as + they become available. At this point it would only be possible to + gain the benefits associated with the use of Authentication Blocks if + cryptographic information and SLP SPIs can be preconfigured with the + end systems before they use SLP. + + SLPv2 enables a number of security policies with the mechanisms it + includes. A SLPv2 UA could, for instance, reject any SLP message + which did not carry an authentication block which it could verify. + This is not the only policy which is possible to implement. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Guttman, et al. Standards Track [Page 47] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +A. Appendix: Changes to the Service Location Protocol from v1 to v2 + + SLP version 2 (SLPv2) corrects race conditions present in SLPv1 [22]. + In addition, authentication has been reworked to provide more + flexibility and protection (especially for DA Advertisements). SLPv2 + also changes the formats and definition of many flags and values and + reduces the number of 'required features.' SLPv2 clarifies and + changes the use of 'Scopes', eliminating support for 'unscoped + directory agents' and 'unscoped requests'. SLPv2 uses LDAPv3 + compatible string encodings of attributes and search filters. Other + changes (such as Language and Character set handling) adopt practices + recommended by the Internet Engineering Steering Group. + + Effort has been made to make SLPv2 operate the same whether DAs are + present or not. For this reason, a new message (the SAAdvert) has + been added. This allows UAs to discover scope information in the + absence of administrative configuration and DAs. This was not + possible in SLPv1. + + SLPv2 is incompatible in some respects with SLPv1. If a DA which + supports both SLPv1 and SLPv2 with the same scope is present, + services advertised by SAs using either version of the protocol will + be available to both SLPv1 and SLPv2 UAs. SLPv1 DAs SHOULD be phased + out and replace with SLPv2 DAs which support both versions of the + protocol. + + SLPv1 allows services to be advertised and requested without a scope. + Further, DAs can be configured without a scope. This is incompatible + with SLPv2 and presents scalability problems. To facilitate this + forward migration, SLPv1 agents MUST use scopes for all registrations + and requests. SLPv1 DAs MUST be configured with a scope list. This + constitutes a revision of RFC 2165 [22]. + +B. Appendix: Service Discovery by Type: Minimal SLPv2 Features + + Service Agents may advertise services without attributes. This will + enable only discovery of services by type. Service types discovered + this way will have a Service Template [13] defined which specifies + explicitly that no attributes are associated with the service + advertisement. Service types associated with Service Templates which + specify attributes MUST NOT be advertised by SAs which do not support + attributes. + + While discovery of service by service type is a subset of the + features possible using SLPv2 this form of discovery is consistent + with the current generation of products that allow simple browsing of + all services in a 'zone' or 'workgroup' by type. In some cases, + attribute discovery, security and feature negotiation is handled by + + + +Guttman, et al. Standards Track [Page 48] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + application layer protocols - all that is required is the basic + discovery of services that support a certain service. + + UAs requesting only service of that service type would only need to + support service type and scope fields of the Service Request. UAs + would still perform DA discovery and unicast SLPv2 SrvRqst messages + to DAs in their scope once they were discovered instead of + multicasting them. + + SAs would also perform DA discovery and use a SLPv2 SrvReg to + register all their advertised services with SLPv2 DAs in their scope. + These advertisements would needless to say contain no attribute + string. + + These minimal SAs could ignore the Language Tag in requests since + SrvRqst messages would contain no attributes, hence no strings would + be internationalized. Further, any non-null predicate string would + fail to match a service advertisement with no attributes, so these + SAs would not have to parse and interpret search filters. Overflow + will never occur in SrvRqst, SrvRply or SrvReg messages so TCP + message handling would not have to be implemented. Finally, all + AttrRqst messages could be dropped by the SA, since no attributes are + supported. + +C. Appendix: DAAdverts with arbitrary URLs + + Using Active DA Discovery, a SrvRqst with its service type field set + to "service:directory-agent". DAs will respond with a DAAdvert + containing a URL with the "service:directory-agent:" scheme. This is + the same DAAdvert that such a DA would multicast in unsolicited DA + advertisements. + + A UA or SA which receives an unsolicited DAAdvert MUST examine the + URL to determine if it has a recognized scheme. If the UA or SA does + not recognize the DAAdvert's URL scheme, the DAAdvert is silently + discarded. This document specifies only how to use URLs with the + "service:directory-agent:" scheme. + + This provides the possibility for forward compatibility with future + versions of SLP and enables other services to advertise their ability + to serve as a clearinghouse for service location information. + + For example, if LDAPv3 [15] is used for service registration and + discovery by a set of end systems, they could interpret a LDAP URL + [16] to passively discover the LDAP server to use for this purpose. + This document does not specify how this is done: SLPv2 agents + without further support would simply discard this DAAdvert. + + + + +Guttman, et al. Standards Track [Page 49] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +D. Appendix: SLP Protocol Extensions + +D.1. Required Attribute Missing Option + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Extension Type = 0x0001 | Extension Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Template IDVer Length | Template IDVer String \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Required Attr <tag-list> Length| Required Attr <tag-list> \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Required attributes and the format of the IDVer string are defined by + [13]. + + If a SA or DA receives a SrvRqst or a SrvReg which fails to include a + Required Attribute for the requested Service Type (according to the + Service Template), it MAY return the Required Attribute Extension in + addition to the reply corresponding to the message. The sender + SHOULD reissue the message with a search filter including the + attributes listed in the returned Required Attribute Extension. + Similarly, the Required Attribute Extension may be returned in + response to a SrvDereg message that contains a required attribute + tag. + + The Template IDVer String is the name and version number string of + the Service Template which defines the given attribute as required. + It SHOULD be included, but can be omitted if a given SA or DA has + been individually configured to have 'required attributes.' + + The Required Attribute <tag-list> MUST NOT include wild cards. + +E. Acknowledgments + + This document incorporates ideas from work on several discovery + protocols, including RDP by Perkins and Harjono, and PDS by Michael + Day. We are grateful for contributions by Ye Gu and Peter Ford. + John Veizades was instrumental in the standardization of the Service + Location Protocol. Implementors at Novell, Axis Communications and + Sun Microsystems have contributed significantly to make this a much + clearer and more consistent document. + + + + + + + + +Guttman, et al. Standards Track [Page 50] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +F. References + + [1] Port numbers, July 1997. + ftp://ftp.isi.edu/in-notes/iana/assignments/port-numbers. + + [2] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment + DAM 4 to ISO/IEC 9594-2, December 1996. + + [3] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment + DAM 2 to ISO/IEC 9594-6, December 1996. + + [4] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment + DAM 1 to ISO/IEC 9594-7, December 1996. + + [5] ISO/IEC JTC1/SC 21. Certificate Extensions. Draft Amendment + DAM 1 to ISO/IEC 9594-8, December 1996. + + [6] Unicode Technical Report #8. The Unicode Standard, version 2.1. + Technical report, The Unicode Consortium, 1998. + + [7] Alvestrand, H., "Tags for the Identification of Languages", + RFC 1766, March 1995. + + [8] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform + Resource Identifiers (URI): Generic Syntax", RFC 2396, + August 1998. + + [9] Bradner, S., "Key Words for Use in RFCs to Indicate Requirement + Levels", BCP 14, RFC 2119, March 1997. + + [10] CCITT. The Directory Authentication Framework. Recommendation + X.509, 1988. + + [11] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", RFC 2234, November 1997. + + [12] S. Gursharan, R. Andrews, and A. Oppenheimer. Inside AppleTalk. + Addison-Wesley, 1990. + + [13] Guttman, E., Perkins, C. and J. Kempf, "Service Templates and + service: Schemes", RFC 2609, June 1999. + + [14] Howes, T., "The String Representation of LDAP Search Filters", + RFC 2254, December 1997. + + [15] Wahl, M., Howes, T. and S. Kille, "Lightweight Directory + Access Protocol (v3)", RFC 2251, December 1997. + + + + +Guttman, et al. Standards Track [Page 51] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + + [16] Howes, T. and M. Smith, "The LDAP URL Format", RFC 2255, + December 1997. + + [17] Meyer, D., "Administratively Scoped IP Multicast", RFC 2365, + July 1998. + + [18] Narten, T. and H. Alvestrand, "Guidelines for Writing + an IANA Considerations Section in RFCs, BCP 26, RFC 2434, + October 1998. + + [19] Microsoft Networks. SMB File Sharing Protocol Extensions 3.0, + Document Version 1.09, November 1989. + + [20] National Institute of Standards and Technology. Digital + signature standard. Technical Report NIST FIPS PUB 186, U.S. + Department of Commerce, May 1994. + + [21] Perkins, C. and E. Guttman, "DHCP Options for Service Location + Protocol", RFC 2610, June 1999. + + [22] Veizades, J., Guttman, E., Perkins, C. and S. Kaplan, "Service + Location Protocol", RFC 2165, July 1997. + + [23] Yergeau, F., "UTF-8, a transformation format of ISO 10646", + RFC 2279, January 1998. + + + + + + + + + + + + + + + + + + + + + + + + + + +Guttman, et al. Standards Track [Page 52] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +G. Authors' Addresses + + Erik Guttman + Sun Microsystems + Bahnstr. 2 + 74915 Waibstadt + Germany + + Phone: +49 7263 911 701 + EMail: Erik.Guttman@sun.com + + + Charles Perkins + Sun Microsystems + 901 San Antonio Road + Palo Alto, CA 94040 + USA + + Phone: +1 650 786 6464 + EMail: cperkins@sun.com + + + John Veizades + @Home Network + 425 Broadway + Redwood City, CA 94043 + USA + + Phone: +1 650 569 5243 + EMail: veizades@home.net + + + Michael Day + Vinca Corporation. + 1201 North 800 East + Orem, Utah 84097 USA + + Phone: +1 801 376-5083 + EMail: mday@vinca.com + + + + + + + + + + + + +Guttman, et al. Standards Track [Page 53] + +RFC 2608 Service Location Protocol, Version 2 June 1999 + + +H. Full Copyright Statement + + Copyright (C) The Internet Society (1999). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE." + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Guttman, et al. Standards Track [Page 54] + diff --git a/utils/open-isns/doc/rfc3279.txt b/utils/open-isns/doc/rfc3279.txt new file mode 100644 index 0000000..04e7b07 --- /dev/null +++ b/utils/open-isns/doc/rfc3279.txt @@ -0,0 +1,1515 @@ + + + + + + +Network Working Group W. Polk +Request for Comments: 3279 NIST +Obsoletes: 2528 R. Housley +Category: Standards Track RSA Laboratories + L. Bassham + NIST + April 2002 + + Algorithms and Identifiers for the + Internet X.509 Public Key Infrastructure + Certificate and Certificate Revocation List (CRL) Profile + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2002). All Rights Reserved. + +Abstract + + This document specifies algorithm identifiers and ASN.1 encoding + formats for digital signatures and subject public keys used in the + Internet X.509 Public Key Infrastructure (PKI). Digital signatures + are used to sign certificates and certificate revocation list (CRLs). + Certificates include the public key of the named subject. + +Table of Contents + + 1 Introduction . . . . . . . . . . . . . . . . . . . . . . 2 + 2 Algorithm Support . . . . . . . . . . . . . . . . . . . . 3 + 2.1 One-Way Hash Functions . . . . . . . . . . . . . . . . 3 + 2.1.1 MD2 One-Way Hash Functions . . . . . . . . . . . . . 3 + 2.1.2 MD5 One-Way Hash Functions . . . . . . . . . . . . . 4 + 2.1.3 SHA-1 One-Way Hash Functions . . . . . . . . . . . . 4 + 2.2 Signature Algorithms . . . . . . . . . . . . . . . . . 4 + 2.2.1 RSA Signature Algorithm . . . . . . . . . . . . . . . 5 + 2.2.2 DSA Signature Algorithm . . . . . . . . . . . . . . . 6 + 2.2.3 Elliptic Curve Digital Signature Algorithm . . . . . 7 + 2.3 Subject Public Key Algorithms . . . . . . . . . . . . . 7 + 2.3.1 RSA Keys . . . . . . . . . . . . . . . . . . . . . . 8 + 2.3.2 DSA Signature Keys . . . . . . . . . . . . . . . . . 9 + 2.3.3 Diffie-Hellman Key Exchange Keys . . . . . . . . . . 10 + + + +Polk, et al. Standards Track [Page 1] + +RFC 3279 Algorithms and Identifiers April 2002 + + + 2.3.4 KEA Public Keys . . . . . . . . . . . . . . . . . . . 11 + 2.3.5 ECDSA and ECDH Public Keys . . . . . . . . . . . . . 13 + 3 ASN.1 Module . . . . . . . . . . . . . . . . . . . . . . 18 + 4 References . . . . . . . . . . . . . . . . . . . . . . . 24 + 5 Security Considerations . . . . . . . . . . . . . . . . . 25 + 6 Intellectual Property Rights . . . . . . . . . . . . . . 26 + 7 Author Addresses . . . . . . . . . . . . . . . . . . . . 26 + 8 Full Copyright Statement . . . . . . . . . . . . . . . . 27 + +1 Introduction + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC 2119]. + + This document specifies algorithm identifiers and ASN.1 [X.660] + encoding formats for digital signatures and subject public keys used + in the Internet X.509 Public Key Infrastructure (PKI). This + specification supplements [RFC 3280], "Internet X.509 Public Key + Infrastructure: Certificate and Certificate Revocation List (CRL) + Profile." Implementations of this specification MUST also conform to + RFC 3280. + + This specification defines the contents of the signatureAlgorithm, + signatureValue, signature, and subjectPublicKeyInfo fields within + Internet X.509 certificates and CRLs. + + This document identifies one-way hash functions for use in the + generation of digital signatures. These algorithms are used in + conjunction with digital signature algorithms. + + This specification describes the encoding of digital signatures + generated with the following cryptographic algorithms: + + * Rivest-Shamir-Adelman (RSA); + * Digital Signature Algorithm (DSA); and + * Elliptic Curve Digital Signature Algorithm (ECDSA). + + This document specifies the contents of the subjectPublicKeyInfo + field in Internet X.509 certificates. For each algorithm, the + appropriate alternatives for the the keyUsage extension are provided. + This specification describes encoding formats for public keys used + with the following cryptographic algorithms: + + * Rivest-Shamir-Adelman (RSA); + * Digital Signature Algorithm (DSA); + * Diffie-Hellman (DH); + * Key Encryption Algorithm (KEA); + + + +Polk, et al. Standards Track [Page 2] + +RFC 3279 Algorithms and Identifiers April 2002 + + + * Elliptic Curve Digital Signature Algorithm (ECDSA); and + * Elliptic Curve Diffie-Hellman (ECDH). + +2 Algorithm Support + + This section describes cryptographic algorithms which may be used + with the Internet X.509 certificate and CRL profile [RFC 3280]. This + section describes one-way hash functions and digital signature + algorithms which may be used to sign certificates and CRLs, and + identifies object identifiers (OIDs) for public keys contained in a + certificate. + + Conforming CAs and applications MUST, at a minimum, support digital + signatures and public keys for one of the specified algorithms. When + using any of the algorithms identified in this specification, + conforming CAs and applications MUST support them as described. + +2.1 One-way Hash Functions + + This section identifies one-way hash functions for use in the + Internet X.509 PKI. One-way hash functions are also called message + digest algorithms. SHA-1 is the preferred one-way hash function for + the Internet X.509 PKI. However, PEM uses MD2 for certificates [RFC + 1422] [RFC 1423] and MD5 is used in other legacy applications. For + these reasons, MD2 and MD5 are included in this profile. The data + that is hashed for certificate and CRL signing is fully described in + [RFC 3280]. + +2.1.1 MD2 One-way Hash Function + + MD2 was developed by Ron Rivest for RSA Security. RSA Security has + recently placed the MD2 algorithm in the public domain. Previously, + RSA Data Security had granted license for use of MD2 for non- + commercial Internet Privacy-Enhanced Mail (PEM). MD2 may continue to + be used with PEM certificates, but SHA-1 is preferred. MD2 produces + a 128-bit "hash" of the input. MD2 is fully described in [RFC 1319]. + + At the Selected Areas in Cryptography '95 conference in May 1995, + Rogier and Chauvaud presented an attack on MD2 that can nearly find + collisions [RC95]. Collisions occur when one can find two different + messages that generate the same message digest. A checksum operation + in MD2 is the only remaining obstacle to the success of the attack. + For this reason, the use of MD2 for new applications is discouraged. + It is still reasonable to use MD2 to verify existing signatures, as + the ability to find collisions in MD2 does not enable an attacker to + find new messages having a previously computed hash value. + + + + + +Polk, et al. Standards Track [Page 3] + +RFC 3279 Algorithms and Identifiers April 2002 + + +2.1.2 MD5 One-way Hash Function + + MD5 was developed by Ron Rivest for RSA Security. RSA Security has + placed the MD5 algorithm in the public domain. MD5 produces a 128- + bit "hash" of the input. MD5 is fully described in [RFC 1321]. + + Den Boer and Bosselaers [DB94] have found pseudo-collisions for MD5, + but there are no other known cryptanalytic results. The use of MD5 + for new applications is discouraged. It is still reasonable to use + MD5 to verify existing signatures. + +2.1.3 SHA-1 One-way Hash Function + + SHA-1 was developed by the U.S. Government. SHA-1 produces a 160-bit + "hash" of the input. SHA-1 is fully described in [FIPS 180-1]. RFC + 3174 [RFC 3174] also describes SHA-1, and it provides an + implementation of the algorithm. + +2.2 Signature Algorithms + + Certificates and CRLs conforming to [RFC 3280] may be signed with any + public key signature algorithm. The certificate or CRL indicates the + algorithm through an algorithm identifier which appears in the + signatureAlgorithm field within the Certificate or CertificateList. + This algorithm identifier is an OID and has optionally associated + parameters. This section identifies algorithm identifiers and + parameters that MUST be used in the signatureAlgorithm field in a + Certificate or CertificateList. + + Signature algorithms are always used in conjunction with a one-way + hash function. + + This section identifies OIDS for RSA, DSA, and ECDSA. The contents + of the parameters component for each algorithm vary; details are + provided for each algorithm. + + The data to be signed (e.g., the one-way hash function output value) + is formatted for the signature algorithm to be used. Then, a private + key operation (e.g., RSA encryption) is performed to generate the + signature value. This signature value is then ASN.1 encoded as a BIT + STRING and included in the Certificate or CertificateList in the + signature field. + + + + + + + + + +Polk, et al. Standards Track [Page 4] + +RFC 3279 Algorithms and Identifiers April 2002 + + +2.2.1 RSA Signature Algorithm + + The RSA algorithm is named for its inventors: Rivest, Shamir, and + Adleman. This profile includes three signature algorithms based on + the RSA asymmetric encryption algorithm. The signature algorithms + combine RSA with either the MD2, MD5, or the SHA-1 one-way hash + functions. + + The signature algorithm with SHA-1 and the RSA encryption algorithm + is implemented using the padding and encoding conventions described + in PKCS #1 [RFC 2313]. The message digest is computed using the + SHA-1 hash algorithm. + + The RSA signature algorithm, as specified in PKCS #1 [RFC 2313] + includes a data encoding step. In this step, the message digest and + the OID for the one-way hash function used to compute the digest are + combined. When performing the data encoding step, the md2, md5, and + id-sha1 OIDs MUST be used to specify the MD2, MD5, and SHA-1 one-way + hash functions, respectively: + + md2 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) US(840) rsadsi(113549) + digestAlgorithm(2) 2 } + + md5 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) US(840) rsadsi(113549) + digestAlgorithm(2) 5 } + + id-sha1 OBJECT IDENTIFIER ::= { + iso(1) identified-organization(3) oiw(14) secsig(3) + algorithms(2) 26 } + + The signature algorithm with MD2 and the RSA encryption algorithm is + defined in PKCS #1 [RFC 2313]. As defined in PKCS #1 [RFC 2313], the + ASN.1 OID used to identify this signature algorithm is: + + md2WithRSAEncryption OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) + pkcs-1(1) 2 } + + The signature algorithm with MD5 and the RSA encryption algorithm is + defined in PKCS #1 [RFC 2313]. As defined in PKCS #1 [RFC 2313], the + ASN.1 OID used to identify this signature algorithm is: + + md5WithRSAEncryption OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) + pkcs-1(1) 4 } + + + + +Polk, et al. Standards Track [Page 5] + +RFC 3279 Algorithms and Identifiers April 2002 + + + The ASN.1 object identifier used to identify this signature algorithm + is: + + sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) + pkcs-1(1) 5 } + + When any of these three OIDs appears within the ASN.1 type + AlgorithmIdentifier, the parameters component of that type SHALL be + the ASN.1 type NULL. + + The RSA signature generation process and the encoding of the result + is described in detail in PKCS #1 [RFC 2313]. + +2.2.2 DSA Signature Algorithm + + The Digital Signature Algorithm (DSA) is defined in the Digital + Signature Standard (DSS). DSA was developed by the U.S. Government, + and DSA is used in conjunction with the SHA-1 one-way hash function. + DSA is fully described in [FIPS 186]. The ASN.1 OID used to identify + this signature algorithm is: + + id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57 (10040) + x9cm(4) 3 } + + When the id-dsa-with-sha1 algorithm identifier appears as the + algorithm field in an AlgorithmIdentifier, the encoding SHALL omit + the parameters field. That is, the AlgorithmIdentifier SHALL be a + SEQUENCE of one component: the OBJECT IDENTIFIER id-dsa-with-sha1. + + The DSA parameters in the subjectPublicKeyInfo field of the + certificate of the issuer SHALL apply to the verification of the + signature. + + When signing, the DSA algorithm generates two values. These values + are commonly referred to as r and s. To easily transfer these two + values as one signature, they SHALL be ASN.1 encoded using the + following ASN.1 structure: + + Dss-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER } + + + + + + + + +Polk, et al. Standards Track [Page 6] + +RFC 3279 Algorithms and Identifiers April 2002 + + +2.2.3 ECDSA Signature Algorithm + + The Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in + [X9.62]. The ASN.1 object identifiers used to identify ECDSA are + defined in the following arc: + + ansi-X9-62 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) 10045 } + + id-ecSigType OBJECT IDENTIFIER ::= { + ansi-X9-62 signatures(4) } + + ECDSA is used in conjunction with the SHA-1 one-way hash function. + The ASN.1 object identifier used to identify ECDSA with SHA-1 is: + + ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { + id-ecSigType 1 } + + When the ecdsa-with-SHA1 algorithm identifier appears as the + algorithm field in an AlgorithmIdentifier, the encoding MUST omit the + parameters field. That is, the AlgorithmIdentifier SHALL be a + SEQUENCE of one component: the OBJECT IDENTIFIER ecdsa-with-SHA1. + + The elliptic curve parameters in the subjectPublicKeyInfo field of + the certificate of the issuer SHALL apply to the verification of the + signature. + + When signing, the ECDSA algorithm generates two values. These values + are commonly referred to as r and s. To easily transfer these two + values as one signature, they MUST be ASN.1 encoded using the + following ASN.1 structure: + + Ecdsa-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER } + +2.3 Subject Public Key Algorithms + + Certificates conforming to [RFC 3280] may convey a public key for any + public key algorithm. The certificate indicates the algorithm + through an algorithm identifier. This algorithm identifier is an OID + and optionally associated parameters. + + This section identifies preferred OIDs and parameters for the RSA, + DSA, Diffie-Hellman, KEA, ECDSA, and ECDH algorithms. Conforming CAs + MUST use the identified OIDs when issuing certificates containing + + + + + +Polk, et al. Standards Track [Page 7] + +RFC 3279 Algorithms and Identifiers April 2002 + + + public keys for these algorithms. Conforming applications supporting + any of these algorithms MUST, at a minimum, recognize the OID + identified in this section. + +2.3.1 RSA Keys + + The OID rsaEncryption identifies RSA public keys. + + pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) + rsadsi(113549) pkcs(1) 1 } + + rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1} + + The rsaEncryption OID is intended to be used in the algorithm field + of a value of type AlgorithmIdentifier. The parameters field MUST + have ASN.1 type NULL for this algorithm identifier. + + The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey: + + RSAPublicKey ::= SEQUENCE { + modulus INTEGER, -- n + publicExponent INTEGER } -- e + + where modulus is the modulus n, and publicExponent is the public + exponent e. The DER encoded RSAPublicKey is the value of the BIT + STRING subjectPublicKey. + + This OID is used in public key certificates for both RSA signature + keys and RSA encryption keys. The intended application for the key + MAY be indicated in the key usage field (see [RFC 3280]). The use of + a single key for both signature and encryption purposes is not + recommended, but is not forbidden. + + If the keyUsage extension is present in an end entity certificate + which conveys an RSA public key, any combination of the following + values MAY be present: + + digitalSignature; + nonRepudiation; + keyEncipherment; and + dataEncipherment. + + If the keyUsage extension is present in a CA or CRL issuer + certificate which conveys an RSA public key, any combination of the + following values MAY be present: + + digitalSignature; + nonRepudiation; + + + +Polk, et al. Standards Track [Page 8] + +RFC 3279 Algorithms and Identifiers April 2002 + + + keyEncipherment; + dataEncipherment; + keyCertSign; and + cRLSign. + + However, this specification RECOMMENDS that if keyCertSign or cRLSign + is present, both keyEncipherment and dataEncipherment SHOULD NOT be + present. + +2.3.2 DSA Signature Keys + + The Digital Signature Algorithm (DSA) is defined in the Digital + Signature Standard (DSS) [FIPS 186]. The DSA OID supported by this + profile is: + + id-dsa OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } + + The id-dsa algorithm syntax includes optional domain parameters. + These parameters are commonly referred to as p, q, and g. When + omitted, the parameters component MUST be omitted entirely. That is, + the AlgorithmIdentifier MUST be a SEQUENCE of one component: the + OBJECT IDENTIFIER id-dsa. + + If the DSA domain parameters are present in the subjectPublicKeyInfo + AlgorithmIdentifier, the parameters are included using the following + ASN.1 structure: + + Dss-Parms ::= SEQUENCE { + p INTEGER, + q INTEGER, + g INTEGER } + + The AlgorithmIdentifier within subjectPublicKeyInfo is the only place + within a certificate where the parameters may be used. If the DSA + algorithm parameters are omitted from the subjectPublicKeyInfo + AlgorithmIdentifier and the CA signed the subject certificate using + DSA, then the certificate issuer's DSA parameters apply to the + subject's DSA key. If the DSA domain parameters are omitted from the + SubjectPublicKeyInfo AlgorithmIdentifier and the CA signed the + subject certificate using a signature algorithm other than DSA, then + the subject's DSA domain parameters are distributed by other means. + If the subjectPublicKeyInfo AlgorithmIdentifier field omits the + parameters component, the CA signed the subject with a signature + algorithm other than DSA, and the subject's DSA parameters are not + available through other means, then clients MUST reject the + certificate. + + + + +Polk, et al. Standards Track [Page 9] + +RFC 3279 Algorithms and Identifiers April 2002 + + + The DSA public key MUST be ASN.1 DER encoded as an INTEGER; this + encoding shall be used as the contents (i.e., the value) of the + subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo + data element. + + DSAPublicKey ::= INTEGER -- public key, Y + + If the keyUsage extension is present in an end entity certificate + which conveys a DSA public key, any combination of the following + values MAY be present: + + digitalSignature; + nonRepudiation; + + If the keyUsage extension is present in a CA or CRL issuer + certificate which conveys a DSA public key, any combination of the + following values MAY be present: + + digitalSignature; + nonRepudiation; + keyCertSign; and + cRLSign. + +2.3.3 Diffie-Hellman Key Exchange Keys + + The Diffie-Hellman OID supported by this profile is defined in + [X9.42]. + + dhpublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) ansi-x942(10046) number-type(2) 1 } + + The dhpublicnumber OID is intended to be used in the algorithm field + of a value of type AlgorithmIdentifier. The parameters field of that + type, which has the algorithm-specific syntax ANY DEFINED BY + algorithm, have the ASN.1 type DomainParameters for this algorithm. + + DomainParameters ::= SEQUENCE { + p INTEGER, -- odd prime, p=jq +1 + g INTEGER, -- generator, g + q INTEGER, -- factor of p-1 + j INTEGER OPTIONAL, -- subgroup factor + validationParms ValidationParms OPTIONAL } + + ValidationParms ::= SEQUENCE { + seed BIT STRING, + pgenCounter INTEGER } + + + + + +Polk, et al. Standards Track [Page 10] + +RFC 3279 Algorithms and Identifiers April 2002 + + + The fields of type DomainParameters have the following meanings: + + p identifies the prime p defining the Galois field; + + g specifies the generator of the multiplicative subgroup of order + g; + + q specifies the prime factor of p-1; + + j optionally specifies the value that satisfies the equation + p=jq+1 to support the optional verification of group parameters; + + seed optionally specifies the bit string parameter used as the + seed for the domain parameter generation process; and + + pgenCounter optionally specifies the integer value output as part + of the of the domain parameter prime generation process. + + If either of the domain parameter generation components (pgenCounter + or seed) is provided, the other MUST be present as well. + + The Diffie-Hellman public key MUST be ASN.1 encoded as an INTEGER; + this encoding shall be used as the contents (i.e., the value) of the + subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo + data element. + + DHPublicKey ::= INTEGER -- public key, y = g^x mod p + + If the keyUsage extension is present in a certificate which conveys a + DH public key, the following values may be present: + + keyAgreement; + encipherOnly; and + decipherOnly. + + If present, the keyUsage extension MUST assert keyAgreement and MAY + assert either encipherOnly and decipherOnly. The keyUsage extension + MUST NOT assert both encipherOnly and decipherOnly. + +2.3.4 KEA Public Keys + + This section identifies the preferred OID and parameters for the + inclusion of a KEA public key in a certificate. The Key Exchange + Algorithm (KEA) is a key agreement algorithm. Two parties may + generate a "pairwise key" if and only if they share the same KEA + parameters. The KEA parameters are not included in a certificate; + instead a domain identifier is supplied in the parameters field. + + + + +Polk, et al. Standards Track [Page 11] + +RFC 3279 Algorithms and Identifiers April 2002 + + + When the SubjectPublicKeyInfo field contains a KEA key, the algorithm + identifier and parameters SHALL be as defined in [SDN.701r]: + + id-keyExchangeAlgorithm OBJECT IDENTIFIER ::= + { 2 16 840 1 101 2 1 1 22 } + + KEA-Parms-Id ::= OCTET STRING + + CAs MUST populate the parameters field of the AlgorithmIdentifier + within the SubjectPublicKeyInfo field of each certificate containing + a KEA public key with an 80-bit parameter identifier (OCTET STRING), + also known as the domain identifier. The domain identifier is + computed in three steps: + + (1) the KEA domain parameters (p, q, and g) are DER encoded using + the Dss-Parms structure; + + (2) a 160-bit SHA-1 hash is generated from the parameters; and + + (3) the 160-bit hash is reduced to 80-bits by performing an + "exclusive or" of the 80 high order bits with the 80 low order + bits. + + The resulting value is encoded such that the most significant byte of + the 80-bit value is the first octet in the octet string. The Dss- + Parms is provided above in Section 2.3.2. + + A KEA public key, y, is conveyed in the subjectPublicKey BIT STRING + such that the most significant bit (MSB) of y becomes the MSB of the + BIT STRING value field and the least significant bit (LSB) of y + becomes the LSB of the BIT STRING value field. This results in the + following encoding: + + BIT STRING tag; + BIT STRING length; + 0 (indicating that there are zero unused bits in the final octet + of y); and + BIT STRING value field including y. + + The key usage extension may optionally appear in a KEA certificate. + If a KEA certificate includes the keyUsage extension, only the + following values may be asserted: + + keyAgreement; + encipherOnly; and + decipherOnly. + + + + + +Polk, et al. Standards Track [Page 12] + +RFC 3279 Algorithms and Identifiers April 2002 + + + If present, the keyUsage extension MUST assert keyAgreement and MAY + assert either encipherOnly and decipherOnly. The keyUsage extension + MUST NOT assert both encipherOnly and decipherOnly. + +2.3.5 ECDSA and ECDH Keys + + This section identifies the preferred OID and parameter encoding for + the inclusion of an ECDSA or ECDH public key in a certificate. The + Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in + [X9.62]. ECDSA is the elliptic curve mathematical analog of the + Digital Signature Algorithm [FIPS 186]. The Elliptic Curve Diffie + Hellman (ECDH) algorithm is a key agreement algorithm defined in + [X9.63]. + + ECDH is the elliptic curve mathematical analog of the Diffie-Hellman + key agreement algorithm as specified in [X9.42]. The ECDSA and ECDH + specifications use the same OIDs and parameter encodings. The ASN.1 + object identifiers used to identify these public keys are defined in + the following arc: + + ansi-X9-62 OBJECT IDENTIFIER ::= + { iso(1) member-body(2) us(840) 10045 } + + When certificates contain an ECDSA or ECDH public key, the + id-ecPublicKey algorithm identifier MUST be used. The id-ecPublicKey + algorithm identifier is defined as follows: + + id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } + + id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } + + This OID is used in public key certificates for both ECDSA signature + keys and ECDH encryption keys. The intended application for the key + may be indicated in the key usage field (see [RFC 3280]). The use of + a single key for both signature and encryption purposes is not + recommended, but is not forbidden. + + ECDSA and ECDH require use of certain parameters with the public key. + The parameters may be inherited from the issuer, implicitly included + through reference to a "named curve," or explicitly included in the + certificate. + + EcpkParameters ::= CHOICE { + ecParameters ECParameters, + namedCurve OBJECT IDENTIFIER, + implicitlyCA NULL } + + + + + +Polk, et al. Standards Track [Page 13] + +RFC 3279 Algorithms and Identifiers April 2002 + + + When the parameters are inherited, the parameters field SHALL contain + implictlyCA, which is the ASN.1 value NULL. When parameters are + specified by reference, the parameters field SHALL contain the + named-Curve choice, which is an object identifier. When the + parameters are explicitly included, they SHALL be encoded in the + ASN.1 structure ECParameters: + + ECParameters ::= SEQUENCE { + version ECPVer, -- version is always 1 + fieldID FieldID, -- identifies the finite field over + -- which the curve is defined + curve Curve, -- coefficients a and b of the + -- elliptic curve + base ECPoint, -- specifies the base point P + -- on the elliptic curve + order INTEGER, -- the order n of the base point + cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n + } + + ECPVer ::= INTEGER {ecpVer1(1)} + + Curve ::= SEQUENCE { + a FieldElement, + b FieldElement, + seed BIT STRING OPTIONAL } + + FieldElement ::= OCTET STRING + + ECPoint ::= OCTET STRING + + The value of FieldElement SHALL be the octet string representation of + a field element following the conversion routine in [X9.62], Section + 4.3.3. The value of ECPoint SHALL be the octet string representation + of an elliptic curve point following the conversion routine in + [X9.62], Section 4.3.6. Note that this octet string may represent an + elliptic curve point in compressed or uncompressed form. + + Implementations that support elliptic curve according to this + specification MUST support the uncompressed form and MAY support the + compressed form. + + The components of type ECParameters have the following meanings: + + version specifies the version number of the elliptic curve + parameters. It MUST have the value 1 (ecpVer1). + + + + + + +Polk, et al. Standards Track [Page 14] + +RFC 3279 Algorithms and Identifiers April 2002 + + + fieldID identifies the finite field over which the elliptic curve + is defined. Finite fields are represented by values of the + parameterized type FieldID, constrained to the values of the + objects defined in the information object set FieldTypes. + Additional detail regarding fieldID is provided below. + + curve specifies the coefficients a and b of the elliptic curve E. + Each coefficient is represented as a value of type FieldElement, + an OCTET STRING. seed is an optional parameter used to derive the + coefficients of a randomly generated elliptic curve. + + base specifies the base point P on the elliptic curve. The base + point is represented as a value of type ECPoint, an OCTET STRING. + + order specifies the order n of the base point. + + cofactor is the integer h = #E(Fq)/n. This parameter is specified + as OPTIONAL. However, the cofactor MUST be included in ECDH + public key parameters. The cofactor is not required to support + ECDSA, except in parameter validation. The cofactor MAY be + included to support parameter validation for ECDSA keys. + Parameter validation is not required by this specification. + + The AlgorithmIdentifier within SubjectPublicKeyInfo is the only place + within a certificate where the parameters may be used. If the + elliptic curve parameters are specified as implicitlyCA in the + SubjectPublicKeyInfo AlgorithmIdentifier and the CA signed the + subject certificate using ECDSA, then the certificate issuer's ECDSA + parameters apply to the subject's ECDSA key. If the elliptic curve + parameters are specified as implicitlyCA in the SubjectPublicKeyInfo + AlgorithmIdentifier and the CA signed the certificate using a + signature algorithm other than ECDSA, then clients MUST not make use + of the elliptic curve public key. + + FieldID ::= SEQUENCE { + fieldType OBJECT IDENTIFIER, + parameters ANY DEFINED BY fieldType } + + FieldID is a SEQUENCE of two components, fieldType and parameters. + The fieldType contains an object identifier value that uniquely + identifies the type contained in the parameters. + + The object identifier id-fieldType specifies an arc containing the + object identifiers of each field type. It has the following value: + + id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1) } + + + + + +Polk, et al. Standards Track [Page 15] + +RFC 3279 Algorithms and Identifiers April 2002 + + + The object identifiers prime-field and characteristic-two-field name + the two kinds of fields defined in this Standard. They have the + following values: + + prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + + Prime-p ::= INTEGER -- Field size p (p in bits) + + characteristic-two-field OBJECT IDENTIFIER ::= { id-fieldType 2 } + + Characteristic-two ::= SEQUENCE { + m INTEGER, -- Field size 2^m + basis OBJECT IDENTIFIER, + parameters ANY DEFINED BY basis } + + The object identifier id-characteristic-two-basis specifies an arc + containing the object identifiers for each type of basis for the + characteristic-two finite fields. It has the following value: + + id-characteristic-two-basis OBJECT IDENTIFIER ::= { + characteristic-two-field basisType(1) } + + The object identifiers gnBasis, tpBasis and ppBasis name the three + kinds of basis for characteristic-two finite fields defined by + [X9.62]. They have the following values: + + gnBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 1 } + + -- for gnBasis, the value of the parameters field is NULL + + tpBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 2 } + + -- type of parameters field for tpBasis is Trinomial + + Trinomial ::= INTEGER + + ppBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 3 } + + -- type of parameters field for ppBasis is Pentanomial + + Pentanomial ::= SEQUENCE { + k1 INTEGER, + k2 INTEGER, + k3 INTEGER } + + + + + + + +Polk, et al. Standards Track [Page 16] + +RFC 3279 Algorithms and Identifiers April 2002 + + + The elliptic curve public key (an ECPoint which is an OCTET STRING) + is mapped to a subjectPublicKey (a BIT STRING) as follows: the most + significant bit of the OCTET STRING becomes the most significant bit + of the BIT STRING, and the least significant bit of the OCTET STRING + becomes the least significant bit of the BIT STRING. Note that this + octet string may represent an elliptic curve point in compressed or + uncompressed form. Implementations that support elliptic curve + according to this specification MUST support the uncompressed form + and MAY support the compressed form. + + If the keyUsage extension is present in a CA or CRL issuer + certificate which conveys an elliptic curve public key, any + combination of the following values MAY be present: + + digitalSignature; + nonRepudiation; and + keyAgreement. + + If the keyAgreement value is present, either of the following values + MAY be present: + + encipherOnly; and + decipherOnly. + + The keyUsage extension MUST NOT assert both encipherOnly and + decipherOnly. + + If the keyUsage extension is present in a CA certificate which + conveys an elliptic curve public key, any combination of the + following values MAY be present: + + digitalSignature; + nonRepudiation; + keyAgreement; + keyCertSign; and + cRLSign. + + As above, if the keyUsage extension asserts keyAgreement then it MAY + assert either encipherOnly and decipherOnly. However, this + specification RECOMMENDS that if keyCertSign or cRLSign is present, + keyAgreement, encipherOnly, and decipherOnly SHOULD NOT be present. + + + + + + + + + + +Polk, et al. Standards Track [Page 17] + +RFC 3279 Algorithms and Identifiers April 2002 + + +3 ASN.1 Module + + PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6) + internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) + id-mod-pkix1-algorithms(17) } + + DEFINITIONS EXPLICIT TAGS ::= BEGIN + + -- EXPORTS All; + + -- IMPORTS NONE; + + -- + -- One-way Hash Functions + -- + + md2 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) + digestAlgorithm(2) 2 } + + md5 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) + digestAlgorithm(2) 5 } + + id-sha1 OBJECT IDENTIFIER ::= { + iso(1) identified-organization(3) oiw(14) secsig(3) + algorithms(2) 26 } + + -- + -- DSA Keys and Signatures + -- + + -- OID for DSA public key + + id-dsa OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 } + + -- encoding for DSA public key + + DSAPublicKey ::= INTEGER -- public key, y + + Dss-Parms ::= SEQUENCE { + p INTEGER, + q INTEGER, + g INTEGER } + + + + + + +Polk, et al. Standards Track [Page 18] + +RFC 3279 Algorithms and Identifiers April 2002 + + + -- OID for DSA signature generated with SHA-1 hash + + id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 } + + -- encoding for DSA signature generated with SHA-1 hash + + Dss-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER } + + -- + -- RSA Keys and Signatures + -- + + -- arc for RSA public key and RSA signature OIDs + + pkcs-1 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + + -- OID for RSA public keys + + rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } + + -- OID for RSA signature generated with MD2 hash + + md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } + + -- OID for RSA signature generated with MD5 hash + + md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } + + -- OID for RSA signature generated with SHA-1 hash + + sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } + + -- encoding for RSA public key + + RSAPublicKey ::= SEQUENCE { + modulus INTEGER, -- n + publicExponent INTEGER } -- e + + + + + + + + + + +Polk, et al. Standards Track [Page 19] + +RFC 3279 Algorithms and Identifiers April 2002 + + + -- + -- Diffie-Hellman Keys + -- + + dhpublicnumber OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) ansi-x942(10046) + number-type(2) 1 } + + -- encoding for DSA public key + + DHPublicKey ::= INTEGER -- public key, y = g^x mod p + + DomainParameters ::= SEQUENCE { + p INTEGER, -- odd prime, p=jq +1 + g INTEGER, -- generator, g + q INTEGER, -- factor of p-1 + j INTEGER OPTIONAL, -- subgroup factor, j>= 2 + validationParms ValidationParms OPTIONAL } + + ValidationParms ::= SEQUENCE { + seed BIT STRING, + pgenCounter INTEGER } + + -- + -- KEA Keys + -- + + id-keyExchangeAlgorithm OBJECT IDENTIFIER ::= + { 2 16 840 1 101 2 1 1 22 } + + KEA-Parms-Id ::= OCTET STRING + + -- + -- Elliptic Curve Keys, Signatures, and Curves + -- + + ansi-X9-62 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) 10045 } + + FieldID ::= SEQUENCE { -- Finite field + fieldType OBJECT IDENTIFIER, + parameters ANY DEFINED BY fieldType } + + -- Arc for ECDSA signature OIDS + + id-ecSigType OBJECT IDENTIFIER ::= { ansi-X9-62 signatures(4) } + + + + + +Polk, et al. Standards Track [Page 20] + +RFC 3279 Algorithms and Identifiers April 2002 + + + -- OID for ECDSA signatures with SHA-1 + + ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { id-ecSigType 1 } + + -- OID for an elliptic curve signature + -- format for the value of an ECDSA signature value + + ECDSA-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER } + + -- recognized field type OIDs are defined in the following arc + + id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1) } + + -- where fieldType is prime-field, the parameters are of type Prime-p + + prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + + Prime-p ::= INTEGER -- Finite field F(p), where p is an odd prime + + -- where fieldType is characteristic-two-field, the parameters are + -- of type Characteristic-two + + characteristic-two-field OBJECT IDENTIFIER ::= { id-fieldType 2 } + + Characteristic-two ::= SEQUENCE { + m INTEGER, -- Field size 2^m + basis OBJECT IDENTIFIER, + parameters ANY DEFINED BY basis } + + -- recognized basis type OIDs are defined in the following arc + + id-characteristic-two-basis OBJECT IDENTIFIER ::= { + characteristic-two-field basisType(3) } + + -- gnbasis is identified by OID gnBasis and indicates + -- parameters are NULL + + gnBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 1 } + + -- parameters for this basis are NULL + + -- trinomial basis is identified by OID tpBasis and indicates + -- parameters of type Pentanomial + + tpBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 2 } + + + + +Polk, et al. Standards Track [Page 21] + +RFC 3279 Algorithms and Identifiers April 2002 + + + -- Trinomial basis representation of F2^m + -- Integer k for reduction polynomial xm + xk + 1 + + Trinomial ::= INTEGER + + -- for pentanomial basis is identified by OID ppBasis and indicates + -- parameters of type Pentanomial + + ppBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 3 } + + -- Pentanomial basis representation of F2^m + -- reduction polynomial integers k1, k2, k3 + -- f(x) = x**m + x**k3 + x**k2 + x**k1 + 1 + + Pentanomial ::= SEQUENCE { + k1 INTEGER, + k2 INTEGER, + k3 INTEGER } + + -- The object identifiers gnBasis, tpBasis and ppBasis name + -- three kinds of basis for characteristic-two finite fields + + FieldElement ::= OCTET STRING -- Finite field element + + ECPoint ::= OCTET STRING -- Elliptic curve point + + -- Elliptic Curve parameters may be specified explicitly, + -- specified implicitly through a "named curve", or + -- inherited from the CA + + EcpkParameters ::= CHOICE { + ecParameters ECParameters, + namedCurve OBJECT IDENTIFIER, + implicitlyCA NULL } + + ECParameters ::= SEQUENCE { -- Elliptic curve parameters + version ECPVer, + fieldID FieldID, + curve Curve, + base ECPoint, -- Base point G + order INTEGER, -- Order n of the base point + cofactor INTEGER OPTIONAL } -- The integer h = #E(Fq)/n + + ECPVer ::= INTEGER {ecpVer1(1)} + + + + + + + +Polk, et al. Standards Track [Page 22] + +RFC 3279 Algorithms and Identifiers April 2002 + + + Curve ::= SEQUENCE { + a FieldElement, -- Elliptic curve coefficient a + b FieldElement, -- Elliptic curve coefficient b + seed BIT STRING OPTIONAL } + + id-publicKeyType OBJECT IDENTIFIER ::= { ansi-X9-62 keyType(2) } + + id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } + + -- Named Elliptic Curves in ANSI X9.62. + + ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) } + + c-TwoCurve OBJECT IDENTIFIER ::= { + ellipticCurve characteristicTwo(0) } + + c2pnb163v1 OBJECT IDENTIFIER ::= { c-TwoCurve 1 } + c2pnb163v2 OBJECT IDENTIFIER ::= { c-TwoCurve 2 } + c2pnb163v3 OBJECT IDENTIFIER ::= { c-TwoCurve 3 } + c2pnb176w1 OBJECT IDENTIFIER ::= { c-TwoCurve 4 } + c2tnb191v1 OBJECT IDENTIFIER ::= { c-TwoCurve 5 } + c2tnb191v2 OBJECT IDENTIFIER ::= { c-TwoCurve 6 } + c2tnb191v3 OBJECT IDENTIFIER ::= { c-TwoCurve 7 } + c2onb191v4 OBJECT IDENTIFIER ::= { c-TwoCurve 8 } + c2onb191v5 OBJECT IDENTIFIER ::= { c-TwoCurve 9 } + c2pnb208w1 OBJECT IDENTIFIER ::= { c-TwoCurve 10 } + c2tnb239v1 OBJECT IDENTIFIER ::= { c-TwoCurve 11 } + c2tnb239v2 OBJECT IDENTIFIER ::= { c-TwoCurve 12 } + c2tnb239v3 OBJECT IDENTIFIER ::= { c-TwoCurve 13 } + c2onb239v4 OBJECT IDENTIFIER ::= { c-TwoCurve 14 } + c2onb239v5 OBJECT IDENTIFIER ::= { c-TwoCurve 15 } + c2pnb272w1 OBJECT IDENTIFIER ::= { c-TwoCurve 16 } + c2pnb304w1 OBJECT IDENTIFIER ::= { c-TwoCurve 17 } + c2tnb359v1 OBJECT IDENTIFIER ::= { c-TwoCurve 18 } + c2pnb368w1 OBJECT IDENTIFIER ::= { c-TwoCurve 19 } + c2tnb431r1 OBJECT IDENTIFIER ::= { c-TwoCurve 20 } + + primeCurve OBJECT IDENTIFIER ::= { ellipticCurve prime(1) } + + prime192v1 OBJECT IDENTIFIER ::= { primeCurve 1 } + prime192v2 OBJECT IDENTIFIER ::= { primeCurve 2 } + prime192v3 OBJECT IDENTIFIER ::= { primeCurve 3 } + prime239v1 OBJECT IDENTIFIER ::= { primeCurve 4 } + prime239v2 OBJECT IDENTIFIER ::= { primeCurve 5 } + prime239v3 OBJECT IDENTIFIER ::= { primeCurve 6 } + prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 } + + END + + + +Polk, et al. Standards Track [Page 23] + +RFC 3279 Algorithms and Identifiers April 2002 + + +4 References + + [FIPS 180-1] Federal Information Processing Standards Publication + (FIPS PUB) 180-1, Secure Hash Standard, 17 April 1995. + [Supersedes FIPS PUB 180 dated 11 May 1993.] + + [FIPS 186-2] Federal Information Processing Standards Publication + (FIPS PUB) 186, Digital Signature Standard, 27 January + 2000. [Supersedes FIPS PUB 186-1 dated 15 December + 1998.] + + [P1363] IEEE P1363, "Standard Specifications for Public-Key + Cryptography", 2001. + + [RC95] Rogier, N. and Chauvaud, P., "The compression function + of MD2 is not collision free," Presented at Selected + Areas in Cryptography '95, May 1995. + + [RFC 1034] Mockapetris, P., "Domain Names - Concepts and + Facilities", STD 13, RFC 1034, November 1987. + + [RFC 1319] Kaliski, B., "The MD2 Message-Digest Algorithm", RFC + 1319, April 1992. + + [RFC 1321] Rivest, R., "The MD5 Message-Digest Algorithm", RFC + 1321, April 1992. + + [RFC 1422] Kent, S., "Privacy Enhancement for Internet Electronic + Mail: Part II: Certificate-Based Key Management", RFC + 1422, February 1993. + + [RFC 1423] Balenson, D., "Privacy Enhancement for Internet + Electronic Mail: Part III: Algorithms, Modes, and + Identifiers", RFC 1423, February 1993. + + [RFC 2119] Bradner, S., "Key Words for Use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC 2313] Kaliski, B., "PKCS #1: RSA Encryption Version 1.5", + RFC 2313, March 1998. + + [RFC 2459] Housley, R., Ford, W., Polk, W. and D. Solo "Internet + X.509 Public Key Infrastructure: Certificate and CRL + Profile", RFC 2459, January, 1999. + + [RFC 3174] Eastlake, D. and P. Jones, "US Secure Hash Algorithm 1 + (SHA1)", RFC 3174, September 2001. + + + + +Polk, et al. Standards Track [Page 24] + +RFC 3279 Algorithms and Identifiers April 2002 + + + [RFC 3280] Housley, R., Polk, W., Ford, W. and D. Solo, "Internet + X.509 Public Key Infrastructure Certificate and + Certificate Revocation List (CRL) Profile", RFC 3280, + April 2002. + + [SDN.701r] SDN.701, "Message Security Protocol 4.0", Revision A + 1997-02-06. + + [X.208] CCITT Recommendation X.208: Specification of Abstract + Syntax Notation One (ASN.1), 1988. + + [X.660] ITU-T Recommendation X.660 Information Technology - + ASN.1 encoding rules: Specification of Basic Encoding + Rules (BER), Canonical Encoding Rules (CER) and + Distinguished Encoding Rules (DER), 1997. + + [X9.42] ANSI X9.42-2000, "Public Key Cryptography for The + Financial Services Industry: Agreement of Symmetric + Keys Using Discrete Logarithm Cryptography", December, + 1999. + + [X9.62] X9.62-1998, "Public Key Cryptography For The Financial + Services Industry: The Elliptic Curve Digital + Signature Algorithm (ECDSA)", January 7, 1999. + + [X9.63] ANSI X9.63-2001, "Public Key Cryptography For The + Financial Services Industry: Key Agreement and Key + Transport Using Elliptic Curve Cryptography", Work in + Progress. + +5 Security Considerations + + This specification does not constrain the size of public keys or + their parameters for use in the Internet PKI. However, the key size + selected impacts the strength achieved when implementing + cryptographic services. Selection of appropriate key sizes is + critical to implementing appropriate security. + + This specification does not identify particular elliptic curves for + use in the Internet PKI. However, the particular curve selected + impact the strength of the digital signatures. Some curves are + cryptographically stronger than others! + + In general, use of "well-known" curves, such as the "named curves" + from ANSI X9.62, is a sound strategy. For additional information, + refer to X9.62 Appendix H.1.3, "Key Length Considerations" and + Appendix A.1, "Avoiding Cryptographically Weak Keys". + + + + +Polk, et al. Standards Track [Page 25] + +RFC 3279 Algorithms and Identifiers April 2002 + + + This specification supplements RFC 3280. The security considerations + section of that document applies to this specification as well. + +6 Intellectual Property Rights + + The IETF has been notified of intellectual property rights claimed in + regard to some or all of the specification contained in this + document. For more information consult the online list of claimed + rights. + + The IETF takes no position regarding the validity or scope of any + intellectual property or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; neither does it represent that it + has made any effort to identify any such rights. Information on the + IETF's procedures with respect to rights in standards-track and + standards- related documentation can be found in BCP-11. Copies of + claims of rights made available for publication and any assurances of + licenses to be made available, or the result of an attempt made to + obtain a general license or permission for the use of such + proprietary rights by implementors or users of this specification can + be obtained from the IETF Secretariat. + +7 Author Addresses: + + Tim Polk + NIST + 100 Bureau Drive, Stop 8930 + Gaithersburg, MD 20899-8930 + USA + EMail: tim.polk@nist.gov + + Russell Housley + RSA Laboratories + 918 Spring Knoll Drive + Herndon, VA 20170 + USA + EMail: rhousley@rsasecurity.com + + Larry Bassham + NIST + 100 Bureau Drive, Stop 8930 + Gaithersburg, MD 20899-8930 + USA + EMail: lbassham@nist.gov + + + + + +Polk, et al. Standards Track [Page 26] + +RFC 3279 Algorithms and Identifiers April 2002 + + +8. Full Copyright Statement + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Polk, et al. Standards Track [Page 27] + diff --git a/utils/open-isns/doc/rfc3720.txt b/utils/open-isns/doc/rfc3720.txt new file mode 100644 index 0000000..2041f93 --- /dev/null +++ b/utils/open-isns/doc/rfc3720.txt @@ -0,0 +1,14395 @@ + + + + + + +Network Working Group J. Satran +Request for Comments: 3720 K. Meth +Category: Standards Track IBM + C. Sapuntzakis + Cisco Systems + M. Chadalapaka + Hewlett-Packard Co. + E. Zeidner + IBM + April 2004 + + + Internet Small Computer Systems Interface (iSCSI) + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2003). All Rights Reserved. + +Abstract + + This document describes a transport protocol for Internet Small + Computer Systems Interface (iSCSI) that works on top of TCP. The + iSCSI protocol aims to be fully compliant with the standardized SCSI + architecture model. + + SCSI is a popular family of protocols that enable systems to + communicate with I/O devices, especially storage devices. SCSI + protocols are request/response application protocols with a common + standardized architecture model and basic command set, as well as + standardized command sets for different device classes (disks, tapes, + media-changers etc.). + + As system interconnects move from the classical bus structure to a + network structure, SCSI has to be mapped to network transport + protocols. IP networks now meet the performance requirements of fast + system interconnects and as such are good candidates to "carry" SCSI. + + + + + + + +Satran, et al. Standards Track [Page 1] + +RFC 3720 iSCSI April 2004 + + +Table of Contents + + 1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . 9 + 2. Definitions and Acronyms. . . . . . . . . . . . . . . . . . . 10 + 2.1. Definitions. . . . . . . . . . . . . . . . . . . . . . 10 + 2.2. Acronyms . . . . . . . . . . . . . . . . . . . . . . . 14 + 2.3. Conventions. . . . . . . . . . . . . . . . . . . . . . 16 + 2.3.1. Word Rule. . . . . . . . . . . . . . . . . . 16 + 2.3.2. Half-Word Rule . . . . . . . . . . . . . . . 17 + 2.3.3. Byte Rule. . . . . . . . . . . . . . . . . . 17 + 3. Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . 17 + 3.1. SCSI Concepts. . . . . . . . . . . . . . . . . . . . . 17 + 3.2. iSCSI Concepts and Functional Overview . . . . . . . . 18 + 3.2.1. Layers and Sessions. . . . . . . . . . . . . 19 + 3.2.2. Ordering and iSCSI Numbering . . . . . . . . 19 + 3.2.2.1. Command Numbering and + Acknowledging . . . . . . . . . . 20 + 3.2.2.2. Response/Status Numbering and + Acknowledging . . . . . . . . . . 23 + 3.2.2.3. Data Sequencing . . . . . . . . 24 + 3.2.3. iSCSI Login. . . . . . . . . . . . . . . . . 24 + 3.2.4. iSCSI Full Feature Phase . . . . . . . . . . 25 + 3.2.4.1. Command Connection Allegiance . . 26 + 3.2.4.2. Data Transfer Overview. . . . . . 27 + 3.2.4.3. Tags and Integrity Checks . . . . 28 + 3.2.4.4. Task Management . . . . . . . . . 28 + 3.2.5. iSCSI Connection Termination . . . . . . . . 29 + 3.2.6. iSCSI Names. . . . . . . . . . . . . . . . . 29 + 3.2.6.1. iSCSI Name Properties . . . . . . 30 + 3.2.6.2. iSCSI Name Encoding . . . . . . . 31 + 3.2.6.3. iSCSI Name Structure. . . . . . . 32 + 3.2.6.3.1. Type "iqn." (iSCSI + Qualified Name) . . . 32 + 3.2.6.3.2. Type "eui." (IEEE + EUI-64 format). . . . 34 + 3.2.7. Persistent State . . . . . . . . . . . . . . 34 + 3.2.8. Message Synchronization and Steering . . . . 35 + 3.2.8.1. Sync/Steering and iSCSI PDU + Length . . . . . . . . . . . . . 36 + 3.3. iSCSI Session Types. . . . . . . . . . . . . . . . . . 36 + 3.4. SCSI to iSCSI Concepts Mapping Model . . . . . . . . . 37 + 3.4.1. iSCSI Architecture Model . . . . . . . . . . 37 + 3.4.2. SCSI Architecture Model. . . . . . . . . . . 39 + 3.4.3. Consequences of the Model. . . . . . . . . . 41 + 3.4.3.1. I_T Nexus State . . . . . . . . . 42 + 3.5. Request/Response Summary . . . . . . . . . . . . . . . 42 + 3.5.1. Request/Response Types Carrying SCSI Payload 43 + 3.5.1.1. SCSI-Command . . . . . . . . . . 43 + + + +Satran, et al. Standards Track [Page 2] + +RFC 3720 iSCSI April 2004 + + + 3.5.1.2. SCSI-Response . . . . . . . . . 43 + 3.5.1.3. Task Management Function Request. 44 + 3.5.1.4. Task Management Function Response 44 + 3.5.1.5. SCSI Data-Out and SCSI Data-In. . 44 + 3.5.1.6. Ready To Transfer (R2T) . . . . . 45 + 3.5.2. Requests/Responses carrying SCSI and iSCSI + Payload. . . . . . . . . . . . . . . . . . . 46 + 3.5.2.1. Asynchronous Message. . . . . . . 46 + 3.5.3. Requests/Responses Carrying iSCSI Only + Payload. . . . . . . . . . . . . . . . . . . 46 + 3.5.3.1. Text Request and Text Response. . 46 + 3.5.3.2. Login Request and Login Response. 47 + 3.5.3.3. Logout Request and Response . . . 47 + 3.5.3.4. SNACK Request . . . . . . . . . . 48 + 3.5.3.5. Reject. . . . . . . . . . . . . . 48 + 3.5.3.6. NOP-Out Request and NOP-In + Response . . . . . . . . . . . . 48 + 4. SCSI Mode Parameters for iSCSI. . . . . . . . . . . . . . . . 48 + 5. Login and Full Feature Phase Negotiation. . . . . . . . . . . 48 + 5.1. Text Format. . . . . . . . . . . . . . . . . . . . . . 50 + 5.2. Text Mode Negotiation. . . . . . . . . . . . . . . . . 53 + 5.2.1. List negotiations. . . . . . . . . . . . . . 56 + 5.2.2. Simple-value Negotiations. . . . . . . . . . 56 + 5.3. Login Phase. . . . . . . . . . . . . . . . . . . . . . 57 + 5.3.1. Login Phase Start. . . . . . . . . . . . . . 60 + 5.3.2. iSCSI Security Negotiation . . . . . . . . . 62 + 5.3.3. Operational Parameter Negotiation During + the Login Phase. . . . . . . . . . . . . . . 63 + 5.3.4. Connection Reinstatement . . . . . . . . . . 64 + 5.3.5. Session Reinstatement, Closure, and Timeout. 64 + 5 5.3.5.1. Loss of Nexus + Notification. . . . . 65 + 5.3.6. Session Continuation and Failure . . . . . . 65 + 5.4. Operational Parameter Negotiation Outside the Login + Phase. . . . . . . . . . . . . . . . . . . . . . . . . 66 + 6. iSCSI Error Handling and Recovery . . . . . . . . . . . . . . 67 + 6.1. Overview . . . . . . . . . . . . . . . . . . . . . . . 67 + 6.1.1. Background . . . . . . . . . . . . . . . . . 67 + 6.1.2. Goals. . . . . . . . . . . . . . . . . . . . 67 + 6.1.3. Protocol Features and State Expectations . . 68 + 6.1.4. Recovery Classes . . . . . . . . . . . . . . 69 + 6.1.4.1. Recovery Within-command . . . . . 69 + 6.1.4.2. Recovery Within-connection. . . . 70 + 6.1.4.3. Connection Recovery . . . . . . . 71 + 6.1.4.4. Session Recovery. . . . . . . . . 72 + 6.1.5. Error Recovery Hierarchy . . . . . . . . . . . 72 + 6.2. Retry and Reassign in Recovery . . . . . . . . . . . . 74 + 6.2.1. Usage of Retry . . . . . . . . . . . . . . . 74 + + + +Satran, et al. Standards Track [Page 3] + +RFC 3720 iSCSI April 2004 + + + 6.2.2. Allegiance Reassignment. . . . . . . . . . . 75 + 6.3. Usage Of Reject PDU in Recovery. . . . . . . . . . . . 76 + 6.4. Connection Timeout Management. . . . . . . . . . . . . 76 + 6.4.1. Timeouts on Transport Exception Events . . . 77 + 6.4.2. Timeouts on Planned Decommissioning. . . . . 77 + 6.5. Implicit Termination of Tasks. . . . . . . . . . . . . 77 + 6.6. Format Errors. . . . . . . . . . . . . . . . . . . . . 78 + 6.7. Digest Errors. . . . . . . . . . . . . . . . . . . . . 78 + 6.8. Sequence Errors. . . . . . . . . . . . . . . . . . . . 80 + 6.9. SCSI Timeouts. . . . . . . . . . . . . . . . . . . . . 81 + 6.10. Negotiation Failures . . . . . . . . . . . . . . . . . 81 + 6.11. Protocol Errors. . . . . . . . . . . . . . . . . . . . 82 + 6.12. Connection Failures. . . . . . . . . . . . . . . . . . 82 + 6.13. Session Errors . . . . . . . . . . . . . . . . . . . . 83 + 7. State Transitions . . . . . . . . . . . . . . . . . . . . . . 84 + 7.1. Standard Connection State Diagrams . . . . . . . . . . 84 + 7.1.1. State Descriptions for Initiators and + Targets. . . . . . . . . . . . . . . . . . . 84 + 7.1.2. State Transition Descriptions for Initiators + and Targets. . . . . . . . . . . . . . . . . 85 + 7.1.3. Standard Connection State Diagram for an + Initiator. . . . . . . . . . . . . . . . . . 88 + 7.1.4. Standard Connection State Diagram for a + Target . . . . . . . . . . . . . . . . . . . 90 + 7.2. Connection Cleanup State Diagram for Initiators and + Targets. . . . . . . . . . . . . . . . . . . . . . . . 92 + 7.2.1. State Descriptions for Initiators and + Targets. . . . . . . . . . . . . . . . . . . 94 + 7.2.2. State Transition Descriptions for Initiators + and Targets. . . . . . . . . . . . . . . . . 94 + 7.3. Session State Diagrams . . . . . . . . . . . . . . . . 95 + 7.3.1. Session State Diagram for an Initiator . . . 95 + 7.3.2. Session State Diagram for a Target . . . . . 96 + 7.3.3. State Descriptions for Initiators and + Targets. . . . . . . . . . . . . . . . . . . 97 + 7.3.4. State Transition Descriptions for Initiators + and Targets. . . . . . . . . . . . . . . . . 98 + 8. Security Considerations . . . . . . . . . . . . . . . . . . . 99 + 8.1. iSCSI Security Mechanisms. . . . . . . . . . . . . . . 100 + 8.2. In-band Initiator-Target Authentication. . . . . . . . 100 + 8.2.1. CHAP Considerations. . . . . . . . . . . . . 101 + 8.2.2. SRP Considerations . . . . . . . . . . . . . 103 + 8.3. IPsec. . . . . . . . . . . . . . . . . . . . . . . . . 104 + 8.3.1. Data Integrity and Authentication. . . . . . 104 + 8.3.2. Confidentiality. . . . . . . . . . . . . . . 105 + 8.3.3. Policy, Security Associations, and + Cryptographic Key Management . . . . . . . . 105 + 9. Notes to Implementers . . . . . . . . . . . . . . . . . . . . 106 + + + +Satran, et al. Standards Track [Page 4] + +RFC 3720 iSCSI April 2004 + + + 9.1. Multiple Network Adapters. . . . . . . . . . . . . . . 106 + 9.1.1. Conservative Reuse of ISIDs. . . . . . . . . 107 + 9.1.2. iSCSI Name, ISID, and TPGT Use . . . . . . . 107 + 9.2. Autosense and Auto Contingent Allegiance (ACA) . . . . 109 + 9.3. iSCSI Timeouts . . . . . . . . . . . . . . . . . . . . 109 + 9.4. Command Retry and Cleaning Old Command Instances . . . 110 + 9.5. Synch and Steering Layer and Performance . . . . . . . 110 + 9.6. Considerations for State-dependent Devices and + Long-lasting SCSI Operations . . . . . . . . . . . . . 111 + 9.6.1. Determining the Proper ErrorRecoveryLevel. . 112 + 10. iSCSI PDU Formats . . . . . . . . . . . . . . . . . . . . . . 112 + 10.1. iSCSI PDU Length and Padding . . . . . . . . . . . . . 113 + 10.2. PDU Template, Header, and Opcodes. . . . . . . . . . . 113 + 10.2.1. Basic Header Segment (BHS) . . . . . . . . . 114 + 10.2.1.1. I . . . . . . . . . . . . . . . . 115 + 10.2.1.2. Opcode. . . . . . . . . . . . . . 115 + 10.2.1.3. Final (F) bit . . . . . . . . . . 116 + 10.2.1.4. Opcode-specific Fields. . . . . . 116 + 10.2.1.5. TotalAHSLength. . . . . . . . . . 116 + 10.2.1.6. DataSegmentLength . . . . . . . . 116 + 10.2.1.7. LUN . . . . . . . . . . . . . . . 116 + 10.2.1.8. Initiator Task Tag. . . . . . . . 117 + 10.2.2. Additional Header Segment (AHS) . . . . . . . 117 + 10.2.2.1. AHSType . . . . . . . . . . . . . 117 + 10.2.2.2. AHSLength . . . . . . . . . . . . 117 + 10.2.2.3. Extended CDB AHS. . . . . . . . . 118 + 10.2.2.4. Bidirectional Expected Read-Data + Length AHS. . . . . . . . . . . . 118 + 10.2.3. Header Digest and Data Digest. . . . . . . . 118 + 10.2.4. Data Segment . . . . . . . . . . . . . . . . 119 + 10.3. SCSI Command . . . . . . . . . . . . . . . . . . . . . 119 + 10.3.1. Flags and Task Attributes (byte 1) . . . . . 120 + 10.3.2. CmdSN - Command Sequence Number. . . . . . . 120 + 10.3.3. ExpStatSN. . . . . . . . . . . . . . . . . . 120 + 10.3.4. Expected Data Transfer Length. . . . . . . . 121 + 10.3.5. CDB - SCSI Command Descriptor Block. . . . . 121 + 10.3.6. Data Segment - Command Data. . . . . . . . . 121 + 10.4. SCSI Response. . . . . . . . . . . . . . . . . . . . . 122 + 10.4.1. Flags (byte 1) . . . . . . . . . . . . . . . 123 + 10.4.2. Status . . . . . . . . . . . . . . . . . . . 123 + 10.4.3. Response . . . . . . . . . . . . . . . . . . 124 + 10.4.4. SNACK Tag. . . . . . . . . . . . . . . . . . 125 + 10.4.5. Residual Count . . . . . . . . . . . . . . . 125 + 10.4.6. Bidirectional Read Residual Count. . . . . . 125 + 10.4.7. Data Segment - Sense and Response Data + Segment. . . . . . . . . . . . . . . . . . . 125 + 10.4.7.1. SenseLength . . . . . . . . . . . 126 + 10.4.7.2. Sense Data. . . . . . . . . . . . 126 + + + +Satran, et al. Standards Track [Page 5] + +RFC 3720 iSCSI April 2004 + + + 10.4.8. ExpDataSN. . . . . . . . . . . . . . . . . . 127 + 10.4.9. StatSN - Status Sequence Number. . . . . . . 127 + 10.4.10. ExpCmdSN - Next Expected CmdSN from this + Initiator. . . . . . . . . . . . . . . . . . 128 + 10.4.11. MaxCmdSN - Maximum CmdSN from this Initiator 128 + 10.5. Task Management Function Request . . . . . . . . . . . 129 + 10.5.1. Function . . . . . . . . . . . . . . . . . . 129 + 10.5.2. TotalAHSLength and DataSegmentLength . . . . 132 + 10.5.3. LUN. . . . . . . . . . . . . . . . . . . . . 132 + 10.5.4. Referenced Task Tag. . . . . . . . . . . . . 132 + 10.5.5. RefCmdSN . . . . . . . . . . . . . . . . . . 132 + 10.5.6. ExpDataSN. . . . . . . . . . . . . . . . . . 133 + 10.6. Task Management Function Response. . . . . . . . . . . 134 + 10.6.1. Response . . . . . . . . . . . . . . . . . . 134 + 10.6.2. Task Management Actions on Task Sets . . . . 136 + 10.6.3. TotalAHSLength and DataSegmentLength . . . . 137 + 10.7. SCSI Data-Out & SCSI Data-In . . . . . . . . . . . . . 137 + 10.7.1. F (Final) Bit. . . . . . . . . . . . . . . . 139 + 10.7.2. A (Acknowledge) Bit. . . . . . . . . . . . . 139 + 10.7.3. Flags (byte 1) . . . . . . . . . . . . . . . 140 + 10.7.4. Target Transfer Tag and LUN. . . . . . . . . 140 + 10.7.5. DataSN . . . . . . . . . . . . . . . . . . . 141 + 10.7.6. Buffer Offset. . . . . . . . . . . . . . . . 141 + 10.7.7. DataSegmentLength. . . . . . . . . . . . . . 141 + 10.8. Ready To Transfer (R2T). . . . . . . . . . . . . . . . 142 + 10.8.1. TotalAHSLength and DataSegmentLength . . . . 143 + 10.8.2. R2TSN. . . . . . . . . . . . . . . . . . . . 143 + 10.8.3. StatSN . . . . . . . . . . . . . . . . . . . 144 + 10.8.4. Desired Data Transfer Length and Buffer + Offset . . . . . . . . . . . . . . . . . . . 144 + 10.8.5. Target Transfer Tag. . . . . . . . . . . . . 144 + 10.9. Asynchronous Message . . . . . . . . . . . . . . . . . 145 + 10.9.1. AsyncEvent . . . . . . . . . . . . . . . . . 146 + 10.9.2. AsyncVCode . . . . . . . . . . . . . . . . . 147 + 10.9.3. LUN. . . . . . . . . . . . . . . . . . . . . 147 + 10.9.4. Sense Data and iSCSI Event Data. . . . . . . 148 + 10.9.4.1. SenseLength . . . . . . . . . . . 148 + 10.10. Text Request . . . . . . . . . . . . . . . . . . . . . 149 + 10.10.1. F (Final) Bit. . . . . . . . . . . . . . . . 150 + 10.10.2. C (Continue) Bit . . . . . . . . . . . . . . 150 + 10.10.3. Initiator Task Tag . . . . . . . . . . . . . 150 + 10.10.4. Target Transfer Tag. . . . . . . . . . . . . 150 + 10.10.5. Text . . . . . . . . . . . . . . . . . . . . 151 + 10.11. Text Response. . . . . . . . . . . . . . . . . . . . . 152 + 10.11.1. F (Final) Bit. . . . . . . . . . . . . . . . 152 + 10.11.2. C (Continue) Bit . . . . . . . . . . . . . . 153 + 10.11.3. Initiator Task Tag . . . . . . . . . . . . . 153 + 10.11.4. Target Transfer Tag. . . . . . . . . . . . . 153 + + + +Satran, et al. Standards Track [Page 6] + +RFC 3720 iSCSI April 2004 + + + 10.11.5. StatSN . . . . . . . . . . . . . . . . . . . 154 + 10.11.6. Text Response Data . . . . . . . . . . . . . 154 + 10.12. Login Request. . . . . . . . . . . . . . . . . . . . . 154 + 10.12.1. T (Transit) Bit. . . . . . . . . . . . . . . 155 + 10.12.2. C (Continue) Bit . . . . . . . . . . . . . . 155 + 10.12.3. CSG and NSG. . . . . . . . . . . . . . . . . 156 + 10.12.4. Version. . . . . . . . . . . . . . . . . . . 156 + 10.12.4.1. Version-max. . . . . . . . . . . 156 + 10.12.4.2. Version-min. . . . . . . . . . . 156 + 10.12.5. ISID . . . . . . . . . . . . . . . . . . . . 157 + 10.12.6. TSIH . . . . . . . . . . . . . . . . . . . . 158 + 10.12.7. Connection ID - CID. . . . . . . . . . . . . 158 + 10.12.8. CmdSN. . . . . . . . . . . . . . . . . . . . 159 + 10.12.9. ExpStatSN. . . . . . . . . . . . . . . . . . 159 + 10.12.10. Login Parameters . . . . . . . . . . . . . . 159 + 10.13. Login Response . . . . . . . . . . . . . . . . . . . . 160 + 10.13.1. Version-max. . . . . . . . . . . . . . . . . 160 + 10.13.2. Version-active . . . . . . . . . . . . . . . 161 + 10.13.3. TSIH . . . . . . . . . . . . . . . . . . . . 161 + 10.13.4. StatSN . . . . . . . . . . . . . . . . . . . 161 + 10.13.5. Status-Class and Status-Detail . . . . . . . 161 + 10.13.6. T (Transit) Bit. . . . . . . . . . . . . . . 164 + 10.13.7. C (Continue) Bit . . . . . . . . . . . . . . 164 + 10.13.8. Login Parameters . . . . . . . . . . . . . . 164 + 10.14. Logout Request . . . . . . . . . . . . . . . . . . . . 165 + 10.14.1. Reason Code. . . . . . . . . . . . . . . . . 167 + 10.14.2. TotalAHSLength and DataSegmentLength . . . . 168 + 10.14.3. CID. . . . . . . . . . . . . . . . . . . . . 168 + 10.14.4. ExpStatSN. . . . . . . . . . . . . . . . . . 168 + 10.14.5. Implicit termination of tasks. . . . . . . . 168 + 10.15. Logout Response. . . . . . . . . . . . . . . . . . . . 169 + 10.15.1. Response . . . . . . . . . . . . . . . . . . 170 + 10.15.2. TotalAHSLength and DataSegmentLength . . . . 170 + 10.15.3. Time2Wait. . . . . . . . . . . . . . . . . . 170 + 10.15.4. Time2Retain. . . . . . . . . . . . . . . . . 170 + 10.16. SNACK Request. . . . . . . . . . . . . . . . . . . . . 171 + 10.16.1. Type . . . . . . . . . . . . . . . . . . . . 172 + 10.16.2. Data Acknowledgement . . . . . . . . . . . . 173 + 10.16.3. Resegmentation . . . . . . . . . . . . . . . 173 + 10.16.4. Initiator Task Tag . . . . . . . . . . . . . 174 + 10.16.5. Target Transfer Tag or SNACK Tag . . . . . . 174 + 10.16.6. BegRun . . . . . . . . . . . . . . . . . . . 174 + 10.16.7. RunLength. . . . . . . . . . . . . . . . . . 174 + 10.17. Reject . . . . . . . . . . . . . . . . . . . . . . . . 175 + 10.17.1. Reason . . . . . . . . . . . . . . . . . . . 176 + 10.17.2. DataSN/R2TSN . . . . . . . . . . . . . . . . 177 + 10.17.3. StatSN, ExpCmdSN and MaxCmdSN. . . . . . . . 177 + 10.17.4. Complete Header of Bad PDU . . . . . . . . . 177 + + + +Satran, et al. Standards Track [Page 7] + +RFC 3720 iSCSI April 2004 + + + 10.18. NOP-Out. . . . . . . . . . . . . . . . . . . . . . . . 178 + 10.18.1. Initiator Task Tag . . . . . . . . . . . . . 179 + 10.18.2. Target Transfer Tag. . . . . . . . . . . . . 179 + 10.18.3. Ping Data. . . . . . . . . . . . . . . . . . 179 + 10.19. NOP-In . . . . . . . . . . . . . . . . . . . . . . . . 180 + 10.19.1. Target Transfer Tag. . . . . . . . . . . . . 181 + 10.19.2. StatSN . . . . . . . . . . . . . . . . . . . 181 + 10.19.3. LUN. . . . . . . . . . . . . . . . . . . . . 181 + 11. iSCSI Security Text Keys and Authentication Methods . . . . . 181 + 11.1. AuthMethod . . . . . . . . . . . . . . . . . . . . . . 182 + 11.1.1. Kerberos . . . . . . . . . . . . . . . . . . 184 + 11.1.2. Simple Public-Key Mechanism (SPKM) . . . . . 184 + 11.1.3. Secure Remote Password (SRP) . . . . . . . . 185 + 11.1.4. Challenge Handshake Authentication Protocol + (CHAP) . . . . . . . . . . . . . . . . . . . 186 + 12. Login/Text Operational Text Keys. . . . . . . . . . . . . . . 187 + 12.1. HeaderDigest and DataDigest. . . . . . . . . . . . . . 188 + 12.2. MaxConnections . . . . . . . . . . . . . . . . . . . . 190 + 12.3. SendTargets. . . . . . . . . . . . . . . . . . . . . . 191 + 12.4. TargetName . . . . . . . . . . . . . . . . . . . . . . 191 + 12.5. InitiatorName. . . . . . . . . . . . . . . . . . . . . 192 + 12.6. TargetAlias. . . . . . . . . . . . . . . . . . . . . . 192 + 12.7. InitiatorAlias . . . . . . . . . . . . . . . . . . . . 193 + 12.8. TargetAddress. . . . . . . . . . . . . . . . . . . . . 193 + 12.9. TargetPortalGroupTag . . . . . . . . . . . . . . . . . 194 + 12.10. InitialR2T . . . . . . . . . . . . . . . . . . . . . . 194 + 12.11. ImmediateData. . . . . . . . . . . . . . . . . . . . . 195 + 12.12. MaxRecvDataSegmentLength . . . . . . . . . . . . . . . 196 + 12.13. MaxBurstLength . . . . . . . . . . . . . . . . . . . . 196 + 12.14. FirstBurstLength . . . . . . . . . . . . . . . . . . . 197 + 12.15. DefaultTime2Wait . . . . . . . . . . . . . . . . . . . 197 + 12.16. DefaultTime2Retain . . . . . . . . . . . . . . . . . . 198 + 12.17. MaxOutstandingR2T. . . . . . . . . . . . . . . . . . . 198 + 12.18. DataPDUInOrder . . . . . . . . . . . . . . . . . . . . 198 + 12.19. DataSequenceInOrder. . . . . . . . . . . . . . . . . . 199 + 12.20. ErrorRecoveryLevel . . . . . . . . . . . . . . . . . . 199 + 12.21. SessionType. . . . . . . . . . . . . . . . . . . . . . 200 + 12.22. The Private or Public Extension Key Format . . . . . . 200 + 13. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 201 + 13.1. Naming Requirements. . . . . . . . . . . . . . . . . . 203 + 13.2. Mechanism Specification Requirements . . . . . . . . . 203 + 13.3. Publication Requirements . . . . . . . . . . . . . . . 203 + 13.4. Security Requirements. . . . . . . . . . . . . . . . . 203 + 13.5. Registration Procedure . . . . . . . . . . . . . . . . 204 + 13.5.1. Present the iSCSI extension item to the + Community. . . . . . . . . . . . . . . . . . 204 + 13.5.2. iSCSI extension item review and IESG + approval . . . . . . . . . . . . . . . . . . 204 + + + +Satran, et al. Standards Track [Page 8] + +RFC 3720 iSCSI April 2004 + + + 13.5.3. IANA Registration. . . . . . . . . . . . . . 204 + 13.5.4. Standard iSCSI extension item-label format . 204 + 13.6. IANA Procedures for Registering iSCSI extension items. 205 + References. . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 + Appendix A. Sync and Steering with Fixed Interval Markers . . . . 209 + A.1. Markers At Fixed Intervals . . . . . . . . . . . . . . 209 + A.2. Initial Marker-less Interval . . . . . . . . . . . . . 210 + A.3. Negotiation. . . . . . . . . . . . . . . . . . . . . . 210 + A.3.1. OFMarker, IFMarker . . . . . . . . . . . . . 210 + A.3.2. OFMarkInt, IFMarkInt . . . . . . . . . . . . 211 + Appendix B. Examples . . . . . . . . . . . . . . . . . . . . . . 212 + B.1. Read Operation Example . . . . . . . . . . . . . . . . 212 + B.2. Write Operation Example. . . . . . . . . . . . . . . . 213 + B.3. R2TSN/DataSN Use Examples. . . . . . . . . . . . . . . 214 + B.4. CRC Examples . . . . . . . . . . . . . . . . . . . . . 217 + Appendix C. Login Phase Examples . . . . . . . . . . . . . . . . 219 + Appendix D. SendTargets Operation. . . . . . . . . . . . . . . . 229 + Appendix E. Algorithmic Presentation of Error Recovery Classes . 233 + E.1. General Data Structure and Procedure Description . . . 233 + E.2. Within-command Error Recovery Algorithms . . . . . . . 234 + E.2.1. Procedure Descriptions . . . . . . . . . . . 234 + E.2.2. Initiator Algorithms . . . . . . . . . . . . 235 + E.2.3. Target Algorithms. . . . . . . . . . . . . . 237 + E.3. Within-connection Recovery Algorithms. . . . . . . . . 240 + E.3.1. Procedure Descriptions . . . . . . . . . . . 240 + E.3.2. Initiator Algorithms . . . . . . . . . . . . 241 + E.3.3. Target Algorithms. . . . . . . . . . . . . . 243 + E.4. Connection Recovery Algorithms . . . . . . . . . . . . 243 + E.4.1. Procedure Descriptions . . . . . . . . . . . 243 + E.4.2. Initiator Algorithms . . . . . . . . . . . . 244 + E.4.3. Target Algorithms. . . . . . . . . . . . . . 246 + Appendix F. Clearing Effects of Various Events on Targets. . . . 249 + F.1. Clearing Effects on iSCSI Objects. . . . . . . . . . . 249 + F.2. Clearing Effects on SCSI Objects . . . . . . . . . . . 253 + Acknowledgements. . . . . . . . . . . . . . . . . . . . . . . . . 254 + Authors' Addresses. . . . . . . . . . . . . . . . . . . . . . . . 256 + Full Copyright Statement. . . . . . . . . . . . . . . . . . . . . 257 + +1. Introduction + + The Small Computer Systems Interface (SCSI) is a popular family of + protocols for communicating with I/O devices, especially storage + devices. SCSI is a client-server architecture. Clients of a SCSI + interface are called "initiators". Initiators issue SCSI "commands" + to request services from components, logical units of a server known + as a "target". A "SCSI transport" maps the client-server SCSI + protocol to a specific interconnect. An Initiator is one endpoint of + a SCSI transport and a target is the other endpoint. + + + +Satran, et al. Standards Track [Page 9] + +RFC 3720 iSCSI April 2004 + + + The SCSI protocol has been mapped over various transports, including + Parallel SCSI, IPI, IEEE-1394 (firewire) and Fibre Channel. These + transports are I/O specific and have limited distance capabilities. + + The iSCSI protocol defined in this document describes a means of + transporting SCSI packets over TCP/IP (see [RFC791], [RFC793], + [RFC1035], [RFC1122]), providing for an interoperable solution which + can take advantage of existing Internet infrastructure, Internet + management facilities, and address distance limitations. + +2. Definitions and Acronyms + +2.1. Definitions + + - Alias: An alias string can also be associated with an iSCSI Node. + The alias allows an organization to associate a user-friendly + string with the iSCSI Name. However, the alias string is not a + substitute for the iSCSI Name. + + - CID (Connection ID): Connections within a session are identified by + a connection ID. It is a unique ID for this connection within the + session for the initiator. It is generated by the initiator and + presented to the target during login requests and during logouts + that close connections. + + - Connection: A connection is a TCP connection. Communication + between the initiator and target occurs over one or more TCP + connections. The TCP connections carry control messages, SCSI + commands, parameters, and data within iSCSI Protocol Data Units + (iSCSI PDUs). + + - iSCSI Device: A SCSI Device using an iSCSI service delivery + subsystem. Service Delivery Subsystem is defined by [SAM2] as a + transport mechanism for SCSI commands and responses. + + - iSCSI Initiator Name: The iSCSI Initiator Name specifies the + worldwide unique name of the initiator. + + - iSCSI Initiator Node: The "initiator". The word "initiator" has + been appropriately qualified as either a port or a device in the + rest of the document when the context is ambiguous. All + unqualified usages of "initiator" refer to an initiator port (or + device) depending on the context. + + - iSCSI Layer: This layer builds/receives iSCSI PDUs and + relays/receives them to/from one or more TCP connections that form + an initiator-target "session". + + + + +Satran, et al. Standards Track [Page 10] + +RFC 3720 iSCSI April 2004 + + + - iSCSI Name: The name of an iSCSI initiator or iSCSI target. + + - iSCSI Node: The iSCSI Node represents a single iSCSI initiator or + iSCSI target. There are one or more iSCSI Nodes within a Network + Entity. The iSCSI Node is accessible via one or more Network + Portals. An iSCSI Node is identified by its iSCSI Name. The + separation of the iSCSI Name from the addresses used by and for the + iSCSI Node allows multiple iSCSI Nodes to use the same address, and + the same iSCSI Node to use multiple addresses. + + - iSCSI Target Name: The iSCSI Target Name specifies the worldwide + unique name of the target. + + - iSCSI Target Node: The "target". + + - iSCSI Task: An iSCSI task is an iSCSI request for which a response + is expected. + + - iSCSI Transfer Direction: The iSCSI transfer direction is defined + with regard to the initiator. Outbound or outgoing transfers are + transfers from the initiator to the target, while inbound or + incoming transfers are from the target to the initiator. + + - ISID: The initiator part of the Session Identifier. It is + explicitly specified by the initiator during Login. + + - I_T nexus: According to [SAM2], the I_T nexus is a relationship + between a SCSI Initiator Port and a SCSI Target Port. For iSCSI, + this relationship is a session, defined as a relationship between + an iSCSI Initiator's end of the session (SCSI Initiator Port) and + the iSCSI Target's Portal Group. The I_T nexus can be identified + by the conjunction of the SCSI port names; that is, the I_T nexus + identifier is the tuple (iSCSI Initiator Name + ',i,'+ ISID, iSCSI + Target Name + ',t,'+ Portal Group Tag). + + - Network Entity: The Network Entity represents a device or gateway + that is accessible from the IP network. A Network Entity must have + one or more Network Portals, each of which can be used to gain + access to the IP network by some iSCSI Nodes contained in that + Network Entity. + + - Network Portal: The Network Portal is a component of a Network + Entity that has a TCP/IP network address and that may be used by an + iSCSI Node within that Network Entity for the connection(s) within + one of its iSCSI sessions. A Network Portal in an initiator is + identified by its IP address. A Network Portal in a target is + identified by its IP address and its listening TCP port. + + + + +Satran, et al. Standards Track [Page 11] + +RFC 3720 iSCSI April 2004 + + + - Originator: In a negotiation or exchange, the party that initiates + the negotiation or exchange. + + - PDU (Protocol Data Unit): The initiator and target divide their + communications into messages. The term "iSCSI protocol data unit" + (iSCSI PDU) is used for these messages. + + - Portal Groups: iSCSI supports multiple connections within the same + session; some implementations will have the ability to combine + connections in a session across multiple Network Portals. A Portal + Group defines a set of Network Portals within an iSCSI Network + Entity that collectively supports the capability of coordinating a + session with connections spanning these portals. Not all Network + Portals within a Portal Group need participate in every session + connected through that Portal Group. One or more Portal Groups may + provide access to an iSCSI Node. Each Network Portal, as utilized + by a given iSCSI Node, belongs to exactly one portal group within + that node. + + - Portal Group Tag: This 16-bit quantity identifies a Portal Group + within an iSCSI Node. All Network Portals with the same portal + group tag in the context of a given iSCSI Node are in the same + Portal Group. + + - Recovery R2T: An R2T generated by a target upon detecting the loss + of one or more Data-Out PDUs through one of the following means: a + digest error, a sequence error, or a sequence reception timeout. A + recovery R2T carries the next unused R2TSN, but requests all or + part of the data burst that an earlier R2T (with a lower R2TSN) had + already requested. + + - Responder: In a negotiation or exchange, the party that responds to + the originator of the negotiation or exchange. + + - SCSI Device: This is the SAM2 term for an entity that contains one + or more SCSI ports that are connected to a service delivery + subsystem and supports a SCSI application protocol. For example, a + SCSI Initiator Device contains one or more SCSI Initiator Ports and + zero or more application clients. A Target Device contains one or + more SCSI Target Ports and one or more device servers and + associated logical units. For iSCSI, the SCSI Device is the + component within an iSCSI Node that provides the SCSI + functionality. As such, there can be at most, one SCSI Device + within a given iSCSI Node. Access to the SCSI Device can only be + achieved in an iSCSI normal operational session. The SCSI Device + Name is defined to be the iSCSI Name of the node. + + + + + +Satran, et al. Standards Track [Page 12] + +RFC 3720 iSCSI April 2004 + + + - SCSI Layer: This builds/receives SCSI CDBs (Command Descriptor + Blocks) and relays/receives them with the remaining command execute + [SAM2] parameters to/from the iSCSI Layer. + + - Session: The group of TCP connections that link an initiator with a + target form a session (loosely equivalent to a SCSI I-T nexus). + TCP connections can be added and removed from a session. Across + all connections within a session, an initiator sees one and the + same target. + + - SCSI Initiator Port: This maps to the endpoint of an iSCSI normal + operational session. An iSCSI normal operational session is + negotiated through the login process between an iSCSI initiator + node and an iSCSI target node. At successful completion of this + process, a SCSI Initiator Port is created within the SCSI Initiator + Device. The SCSI Initiator Port Name and SCSI Initiator Port + Identifier are both defined to be the iSCSI Initiator Name together + with (a) a label that identifies it as an initiator port + name/identifier and (b) the ISID portion of the session identifier. + + - SCSI Port: This is the SAM2 term for an entity in a SCSI Device + that provides the SCSI functionality to interface with a service + delivery subsystem. For iSCSI, the definition of the SCSI + Initiator Port and the SCSI Target Port are different. + + - SCSI Port Name: A name made up as UTF-8 [RFC2279] characters and + includes the iSCSI Name + 'i' or 't' + ISID or Portal Group Tag. + + + - SCSI Target Port: This maps to an iSCSI Target Portal Group. + + - SCSI Target Port Name and SCSI Target Port Identifier: These are + both defined to be the iSCSI Target Name together with (a) a label + that identifies it as a target port name/identifier and (b) the + portal group tag. + + - SSID (Session ID): A session between an iSCSI initiator and an + iSCSI target is defined by a session ID that is a tuple composed of + an initiator part (ISID) and a target part (Target Portal Group + Tag). The ISID is explicitly specified by the initiator at session + establishment. The Target Portal Group Tag is implied by the + initiator through the selection of the TCP endpoint at connection + establishment. The TargetPortalGroupTag key must also be returned + by the target as a confirmation during connection establishment + when TargetName is given. + + - Target Portal Group Tag: A numerical identifier (16-bit) for an + iSCSI Target Portal Group. + + + +Satran, et al. Standards Track [Page 13] + +RFC 3720 iSCSI April 2004 + + + - TSIH (Target Session Identifying Handle): A target assigned tag for + a session with a specific named initiator. The target generates it + during session establishment. Its internal format and content are + not defined by this protocol, except for the value 0 that is + reserved and used by the initiator to indicate a new session. It + is given to the target during additional connection establishment + for the same session. + +2.2. Acronyms + + Acronym Definition + ------------------------------------------------------------ + 3DES Triple Data Encryption Standard + ACA Auto Contingent Allegiance + AEN Asynchronous Event Notification + AES Advanced Encryption Standard + AH Additional Header (not the IPsec AH!) + AHS Additional Header Segment + API Application Programming Interface + ASC Additional Sense Code + ASCII American Standard Code for Information Interchange + ASCQ Additional Sense Code Qualifier + BHS Basic Header Segment + CBC Cipher Block Chaining + CD Compact Disk + CDB Command Descriptor Block + CHAP Challenge Handshake Authentication Protocol + CID Connection ID + CO Connection Only + CRC Cyclic Redundancy Check + CRL Certificate Revocation List + CSG Current Stage + CSM Connection State Machine + DES Data Encryption Standard + DNS Domain Name Server + DOI Domain of Interpretation + DVD Digital Versatile Disk + ESP Encapsulating Security Payload + EUI Extended Unique Identifier + FFP Full Feature Phase + FFPO Full Feature Phase Only + FIM Fixed Interval Marker + Gbps Gigabits per Second + HBA Host Bus Adapter + HMAC Hashed Message Authentication Code + I_T Initiator_Target + I_T_L Initiator_Target_LUN + IANA Internet Assigned Numbers Authority + + + +Satran, et al. Standards Track [Page 14] + +RFC 3720 iSCSI April 2004 + + + ID Identifier + IDN Internationalized Domain Name + IEEE Institute of Electrical & Electronics Engineers + IETF Internet Engineering Task Force + IKE Internet Key Exchange + I/O Input - Output + IO Initialize Only + IP Internet Protocol + IPsec Internet Protocol Security + IPv4 Internet Protocol Version 4 + IPv6 Internet Protocol Version 6 + IQN iSCSI Qualified Name + ISID Initiator Session ID + ITN iSCSI Target Name + ITT Initiator Task Tag + KRB5 Kerberos V5 + LFL Lower Functional Layer + LTDS Logical-Text-Data-Segment + LO Leading Only + LU Logical Unit + LUN Logical Unit Number + MAC Message Authentication Codes + NA Not Applicable + NIC Network Interface Card + NOP No Operation + NSG Next Stage + OS Operating System + PDU Protocol Data Unit + PKI Public Key Infrastructure + R2T Ready To Transfer + R2TSN Ready To Transfer Sequence Number + RDMA Remote Direct Memory Access + RFC Request For Comments + SAM SCSI Architecture Model + SAM2 SCSI Architecture Model - 2 + SAN Storage Area Network + SCSI Small Computer Systems Interface + SN Sequence Number + SNACK Selective Negative Acknowledgment - also + Sequence Number Acknowledgement for data + SPKM Simple Public-Key Mechanism + SRP Secure Remote Password + SSID Session ID + SW Session Wide + TCB Task Control Block + TCP Transmission Control Protocol + TPGT Target Portal Group Tag + TSIH Target Session Identifying Handle + + + +Satran, et al. Standards Track [Page 15] + +RFC 3720 iSCSI April 2004 + + + TTT Target Transfer Tag + UFL Upper Functional Layer + ULP Upper Level Protocol + URN Uniform Resource Names [RFC2396] + UTF Universal Transformation Format + WG Working Group + +2.3. Conventions + + In examples, "I->" and "T->" show iSCSI PDUs sent by the initiator + and target respectively. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in BCP 14 [RFC2119]. + + iSCSI messages - PDUs - are represented by diagrams as in the + following example: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0| Basic Header Segment (BHS) | + +---------------+---------------+---------------+---------------+ + ---------- + +| | + +---------------+---------------+---------------+---------------+ + + The diagrams include byte and bit numbering. + + The following representation and ordering rules are observed in this + document: + + - Word Rule + - Half-word Rule + - Byte Rule + +2.3.1. Word Rule + + A word holds four consecutive bytes. Whenever a word has numeric + content, it is considered an unsigned number in base 2 positional + representation with the lowest numbered byte (e.g., byte 0) bit 0 + representing 2**31 and bit 1 representing 2**30 through lowest + numbered byte + 3 (e.g., byte 3) bit 7 representing 2**0. + + Decimal and hexadecimal representation of word values map this + representation to decimal or hexadecimal positional notation. + + + +Satran, et al. Standards Track [Page 16] + +RFC 3720 iSCSI April 2004 + + +2.3.2. Half-Word Rule + + A half-word holds two consecutive bytes. Whenever a half-word has + numeric content it is considered an unsigned number in base 2 + positional representation with the lowest numbered byte (e.g., byte + 0), bit 0 representing 2**15 and bit 1 representing 2**14 through + lowest numbered byte + 1 (e.g., byte 1), bit 7 representing 2**0. + + Decimal and hexadecimal representation of half-word values map this + representation to decimal or hexadecimal positional notation. + +2.3.3. Byte Rule + + For every PDU, bytes are sent and received in increasing numbered + order (network order). + + Whenever a byte has numerical content, it is considered an unsigned + number in base 2 positional representation with bit 0 representing + 2**7 and bit 1 representing 2**6 through bit 7 representing 2**0. + +3. Overview + +3.1. SCSI Concepts + + The SCSI Architecture Model-2 [SAM2] describes in detail the + architecture of the SCSI family of I/O protocols. This section + provides a brief background of the SCSI architecture and is intended + to familiarize readers with its terminology. + + At the highest level, SCSI is a family of interfaces for requesting + services from I/O devices, including hard drives, tape drives, CD and + DVD drives, printers, and scanners. In SCSI terminology, an + individual I/O device is called a "logical unit" (LU). + + SCSI is a client-server architecture. Clients of a SCSI interface + are called "initiators". Initiators issue SCSI "commands" to request + services from components, logical units, of a server known as a + "target". The "device server" on the logical unit accepts SCSI + commands and processes them. + + A "SCSI transport" maps the client-server SCSI protocol to a specific + interconnect. Initiators are one endpoint of a SCSI transport. The + "target" is the other endpoint. A target can contain multiple + Logical Units (LUs). Each Logical Unit has an address within a + target called a Logical Unit Number (LUN). + + A SCSI task is a SCSI command or possibly a linked set of SCSI + commands. Some LUs support multiple pending (queued) tasks, but the + + + +Satran, et al. Standards Track [Page 17] + +RFC 3720 iSCSI April 2004 + + + queue of tasks is managed by the logical unit. The target uses an + initiator provided "task tag" to distinguish between tasks. Only one + command in a task can be outstanding at any given time. + + Each SCSI command results in an optional data phase and a required + response phase. In the data phase, information can travel from the + initiator to target (e.g., WRITE), target to initiator (e.g., READ), + or in both directions. In the response phase, the target returns the + final status of the operation, including any errors. + + Command Descriptor Blocks (CDB) are the data structures used to + contain the command parameters that an initiator sends to a target. + The CDB content and structure is defined by [SAM2] and device-type + specific SCSI standards. + +3.2. iSCSI Concepts and Functional Overview + + The iSCSI protocol is a mapping of the SCSI remote procedure + invocation model (see [SAM2]) over the TCP protocol. SCSI commands + are carried by iSCSI requests and SCSI responses and status are + carried by iSCSI responses. iSCSI also uses the request response + mechanism for iSCSI protocol mechanisms. + + For the remainder of this document, the terms "initiator" and + "target" refer to "iSCSI initiator node" and "iSCSI target node", + respectively (see Section 3.4.1 iSCSI Architecture Model) unless + otherwise qualified. + + In keeping with similar protocols, the initiator and target divide + their communications into messages. This document uses the term + "iSCSI protocol data unit" (iSCSI PDU) for these messages. + + For performance reasons, iSCSI allows a "phase-collapse". A command + and its associated data may be shipped together from initiator to + target, and data and responses may be shipped together from targets. + + The iSCSI transfer direction is defined with respect to the + initiator. Outbound or outgoing transfers are transfers from an + initiator to a target, while inbound or incoming transfers are from a + target to an initiator. + + An iSCSI task is an iSCSI request for which a response is expected. + + In this document "iSCSI request", "iSCSI command", request, or + (unqualified) command have the same meaning. Also, unless otherwise + specified, status, response, or numbered response have the same + meaning. + + + + +Satran, et al. Standards Track [Page 18] + +RFC 3720 iSCSI April 2004 + + +3.2.1. Layers and Sessions + + The following conceptual layering model is used to specify initiator + and target actions and the way in which they relate to transmitted + and received Protocol Data Units: + + a) the SCSI layer builds/receives SCSI CDBs (Command Descriptor + Blocks) and passes/receives them with the remaining command + execute parameters ([SAM2]) to/from + + b) the iSCSI layer that builds/receives iSCSI PDUs and + relays/receives them to/from one or more TCP connections; the + group of connections form an initiator-target "session". + + Communication between the initiator and target occurs over one or + more TCP connections. The TCP connections carry control messages, + SCSI commands, parameters, and data within iSCSI Protocol Data Units + (iSCSI PDUs). The group of TCP connections that link an initiator + with a target form a session (loosely equivalent to a SCSI I_T nexus, + see Section 3.4.2 SCSI Architecture Model). A session is defined by + a session ID that is composed of an initiator part and a target part. + TCP connections can be added and removed from a session. Each + connection within a session is identified by a connection ID (CID). + + Across all connections within a session, an initiator sees one + "target image". All target identifying elements, such as LUN, are + the same. A target also sees one "initiator image" across all + connections within a session. Initiator identifying elements, such + as the Initiator Task Tag, are global across the session regardless + of the connection on which they are sent or received. + + iSCSI targets and initiators MUST support at least one TCP connection + and MAY support several connections in a session. For error recovery + purposes, targets and initiators that support a single active + connection in a session SHOULD support two connections during + recovery. + +3.2.2. Ordering and iSCSI Numbering + + iSCSI uses Command and Status numbering schemes and a Data sequencing + scheme. + + Command numbering is session-wide and is used for ordered command + delivery over multiple connections. It can also be used as a + mechanism for command flow control over a session. + + + + + + +Satran, et al. Standards Track [Page 19] + +RFC 3720 iSCSI April 2004 + + + Status numbering is per connection and is used to enable missing + status detection and recovery in the presence of transient or + permanent communication errors. + + Data sequencing is per command or part of a command (R2T triggered + sequence) and is used to detect missing data and/or R2T PDUs due to + header digest errors. + + Typically, fields in the iSCSI PDUs communicate the Sequence Numbers + between the initiator and target. During periods when traffic on a + connection is unidirectional, iSCSI NOP-Out/In PDUs may be utilized + to synchronize the command and status ordering counters of the target + and initiator. + + The iSCSI session abstraction is equivalent to the SCSI I_T nexus, + and the iSCSI session provides an ordered command delivery from the + SCSI initiator to the SCSI target. For detailed design + considerations that led to the iSCSI session model as it is defined + here and how it relates the SCSI command ordering features defined in + SCSI specifications to the iSCSI concepts see [CORD]. + +3.2.2.1. Command Numbering and Acknowledging + + iSCSI performs ordered command delivery within a session. All + commands (initiator-to-target PDUs) in transit from the initiator to + the target are numbered. + + iSCSI considers a task to be instantiated on the target in response + to every request issued by the initiator. A set of task management + operations including abort and reassign (see Section 10.5 Task + Management Function Request) may be performed on any iSCSI task. + + Some iSCSI tasks are SCSI tasks, and many SCSI activities are related + to a SCSI task ([SAM2]). In all cases, the task is identified by the + Initiator Task Tag for the life of the task. + + The command number is carried by the iSCSI PDU as CmdSN + (Command Sequence Number). The numbering is session-wide. Outgoing + iSCSI PDUs carry this number. The iSCSI initiator allocates CmdSNs + with a 32-bit unsigned counter (modulo 2**32). Comparisons and + arithmetic on CmdSN use Serial Number Arithmetic as defined in + [RFC1982] where SERIAL_BITS = 32. + + Commands meant for immediate delivery are marked with an immediate + delivery flag; they MUST also carry the current CmdSN. CmdSN does + not advance after a command marked for immediate delivery is sent. + + + + + +Satran, et al. Standards Track [Page 20] + +RFC 3720 iSCSI April 2004 + + + Command numbering starts with the first login request on the first + connection of a session (the leading login on the leading connection) + and command numbers are incremented by 1 for every non-immediate + command issued afterwards. + + If immediate delivery is used with task management commands, these + commands may reach the target before the tasks on which they are + supposed to act. However their CmdSN serves as a marker of their + position in the stream of commands. The initiator and target must + ensure that the task management commands act as specified by [SAM2]. + For example, both commands and responses appear as if delivered in + order. Whenever CmdSN for an outgoing PDU is not specified by an + explicit rule, CmdSN will carry the current value of the local CmdSN + variable (see later in this section). + + The means by which an implementation decides to mark a PDU for + immediate delivery or by which iSCSI decides by itself to mark a PDU + for immediate delivery are beyond the scope of this document. + + The number of commands used for immediate delivery is not limited and + their delivery for execution is not acknowledged through the + numbering scheme. Immediate commands MAY be rejected by the iSCSI + target layer due to a lack of resources. An iSCSI target MUST be + able to handle at least one immediate task management command and one + immediate non-task-management iSCSI command per connection at any + time. + + In this document, delivery for execution means delivery to the SCSI + execution engine or an iSCSI protocol specific execution engine + (e.g., for text requests with public or private extension keys + involving an execution component). With the exception of the + commands marked for immediate delivery, the iSCSI target layer MUST + deliver the commands for execution in the order specified by CmdSN. + Commands marked for immediate delivery may be delivered by the iSCSI + target layer for execution as soon as detected. iSCSI may avoid + delivering some commands to the SCSI target layer if required by a + prior SCSI or iSCSI action (e.g., CLEAR TASK SET Task Management + request received before all the commands on which it was supposed to + act). + + On any connection, the iSCSI initiator MUST send the commands in + increasing order of CmdSN, except for commands that are retransmitted + due to digest error recovery and connection recovery. + + For the numbering mechanism, the initiator and target maintain the + following three variables for each session: + + + + + +Satran, et al. Standards Track [Page 21] + +RFC 3720 iSCSI April 2004 + + + - CmdSN - the current command Sequence Number, advanced by 1 on + each command shipped except for commands marked for immediate + delivery. CmdSN always contains the number to be assigned to + the next Command PDU. + - ExpCmdSN - the next expected command by the target. The target + acknowledges all commands up to, but not including, this + number. The initiator treats all commands with CmdSN less than + ExpCmdSN as acknowledged. The target iSCSI layer sets the + ExpCmdSN to the largest non-immediate CmdSN that it can deliver + for execution plus 1 (no holes in the CmdSN sequence). + - MaxCmdSN - the maximum number to be shipped. The queuing + capacity of the receiving iSCSI layer is MaxCmdSN - ExpCmdSN + + 1. + + The initiator's ExpCmdSN and MaxCmdSN are derived from + target-to-initiator PDU fields. Comparisons and arithmetic on + ExpCmdSN and MaxCmdSN MUST use Serial Number Arithmetic as defined in + [RFC1982] where SERIAL_BITS = 32. + + The target MUST NOT transmit a MaxCmdSN that is less than + ExpCmdSN-1. For non-immediate commands, the CmdSN field can take any + value from ExpCmdSN to MaxCmdSN inclusive. The target MUST silently + ignore any non-immediate command outside of this range or non- + immediate duplicates within the range. The CmdSN carried by + immediate commands may lie outside the ExpCmdSN to MaxCmdSN range. + For example, if the initiator has previously sent a non-immediate + command carrying the CmdSN equal to MaxCmdSN, the target window is + closed. For group task management commands issued as immediate + commands, CmdSN indicates the scope of the group action (e.g., on + ABORT TASK SET indicates which commands are aborted). + + MaxCmdSN and ExpCmdSN fields are processed by the initiator as + follows: + + - If the PDU MaxCmdSN is less than the PDU ExpCmdSN-1 (in Serial + Arithmetic Sense), they are both ignored. + - If the PDU MaxCmdSN is greater than the local MaxCmdSN (in + Serial Arithmetic Sense), it updates the local MaxCmdSN; + otherwise, it is ignored. + - If the PDU ExpCmdSN is greater than the local ExpCmdSN (in + Serial Arithmetic Sense), it updates the local ExpCmdSN; + otherwise, it is ignored. + + This sequence is required because updates may arrive out of order + (e.g., the updates are sent on different TCP connections). + + iSCSI initiators and targets MUST support the command numbering + scheme. + + + +Satran, et al. Standards Track [Page 22] + +RFC 3720 iSCSI April 2004 + + + A numbered iSCSI request will not change its allocated CmdSN, + regardless of the number of times and circumstances in which it is + reissued (see Section 6.2.1 Usage of Retry). At the target, CmdSN is + only relevant when the command has not created any state related to + its execution (execution state); afterwards, CmdSN becomes + irrelevant. Testing for the execution state (represented by + identifying the Initiator Task Tag) MUST precede any other action at + the target. If no execution state is found, it is followed by + ordering and delivery. If an execution state is found, it is + followed by delivery. + + If an initiator issues a command retry for a command with CmdSN R on + a connection when the session CmdSN value is Q, it MUST NOT advance + the CmdSN past R + 2**31 -1 unless the connection is no longer + operational (i.e., it has returned to the FREE state, see Section + 7.1.3 Standard Connection State Diagram for an Initiator), the + connection has been reinstated (see Section 5.3.4 Connection + Reinstatement), or a non-immediate command with CmdSN equal or + greater than Q was issued subsequent to the command retry on the same + connection and the reception of that command is acknowledged by the + target (see Section 9.4 Command Retry and Cleaning Old Command + Instances). + + A target MUST NOT issue a command response or Data-In PDU with status + before acknowledging the command. However, the acknowledgement can + be included in the response or Data-In PDU. + +3.2.2.2. Response/Status Numbering and Acknowledging + + Responses in transit from the target to the initiator are numbered. + The StatSN (Status Sequence Number) is used for this purpose. StatSN + is a counter maintained per connection. ExpStatSN is used by the + initiator to acknowledge status. The status sequence number space is + 32-bit unsigned-integers and the arithmetic operations are the + regular mod(2**32) arithmetic. + + Status numbering starts with the Login response to the first Login + request of the connection. The Login response includes an initial + value for status numbering (any initial value is valid). + + To enable command recovery, the target MAY maintain enough state + information for data and status recovery after a connection failure. + A target doing so can safely discard all of the state information + maintained for recovery of a command after the delivery of the status + for the command (numbered StatSN) is acknowledged through ExpStatSN. + + A large absolute difference between StatSN and ExpStatSN may indicate + a failed connection. Initiators MUST undertake recovery actions if + + + +Satran, et al. Standards Track [Page 23] + +RFC 3720 iSCSI April 2004 + + + the difference is greater than an implementation defined constant + that MUST NOT exceed 2**31-1. + + Initiators and Targets MUST support the response-numbering scheme. + +3.2.2.3. Data Sequencing + + Data and R2T PDUs transferred as part of some command execution MUST + be sequenced. The DataSN field is used for data sequencing. For + input (read) data PDUs, DataSN starts with 0 for the first data PDU + of an input command and advances by 1 for each subsequent data PDU. + For output data PDUs, DataSN starts with 0 for the first data PDU of + a sequence (the initial unsolicited sequence or any data PDU sequence + issued to satisfy an R2T) and advances by 1 for each subsequent data + PDU. R2Ts are also sequenced per command. For example, the first + R2T has an R2TSN of 0 and advances by 1 for each subsequent R2T. For + bidirectional commands, the target uses the DataSN/R2TSN to sequence + Data-In and R2T PDUs in one continuous sequence (undifferentiated). + Unlike command and status, data PDUs and R2Ts are not acknowledged by + a field in regular outgoing PDUs. Data-In PDUs can be acknowledged + on demand by a special form of the SNACK PDU. Data and R2T PDUs are + implicitly acknowledged by status for the command. The DataSN/R2TSN + field enables the initiator to detect missing data or R2T PDUs. + + For any read or bidirectional command, a target MUST issue less than + 2**32 combined R2T and Data-In PDUs. Any output data sequence MUST + contain less than 2**32 Data-Out PDUs. + +3.2.3. iSCSI Login + + The purpose of the iSCSI login is to enable a TCP connection for + iSCSI use, authentication of the parties, negotiation of the + session's parameters and marking of the connection as belonging to an + iSCSI session. + + A session is used to identify to a target all the connections with a + given initiator that belong to the same I_T nexus. (For more details + on how a session relates to an I_T nexus, see Section 3.4.2 SCSI + Architecture Model). + + The targets listen on a well-known TCP port or other TCP port for + incoming connections. The initiator begins the login process by + connecting to one of these TCP ports. + + As part of the login process, the initiator and target SHOULD + authenticate each other and MAY set a security association protocol + for the session. This can occur in many different ways and is + subject to negotiation. + + + +Satran, et al. Standards Track [Page 24] + +RFC 3720 iSCSI April 2004 + + + To protect the TCP connection, an IPsec security association MAY be + established before the Login request. For information on using IPsec + security for iSCSI see Chapter 8 and [RFC3723]. + + The iSCSI Login Phase is carried through Login requests and + responses. Once suitable authentication has occurred and operational + parameters have been set, the session transitions to the Full Feature + Phase and the initiator may start to send SCSI commands. The + security policy for whether, and by what means, a target chooses to + authorize an initiator is beyond the scope of this document. For a + more detailed description of the Login Phase, see Chapter 5. + + The login PDU includes the ISID part of the session ID (SSID). The + target portal group that services the login is implied by the + selection of the connection endpoint. For a new session, the TSIH is + zero. As part of the response, the target generates a TSIH. + + During session establishment, the target identifies the SCSI + initiator port (the "I" in the "I_T nexus") through the value pair + (InitiatorName, ISID). We describe InitiatorName later in this + section. Any persistent state (e.g., persistent reservations) on the + target that is associated with a SCSI initiator port is identified + based on this value pair. Any state associated with the SCSI target + port (the "T" in the "I_T nexus") is identified externally by the + TargetName and portal group tag (see Section 3.4.1 iSCSI Architecture + Model). ISID is subject to reuse restrictions because it is used to + identify a persistent state (see Section 3.4.3 Consequences of the + Model). + + Before the Full Feature Phase is established, only Login Request and + Login Response PDUs are allowed. Login requests and responses MUST + be used exclusively during Login. On any connection, the login phase + MUST immediately follow TCP connection establishment and a subsequent + Login Phase MUST NOT occur before tearing down a connection. + + A target receiving any PDU except a Login request before the Login + phase is started MUST immediately terminate the connection on which + the PDU was received. Once the Login phase has started, if the + target receives any PDU except a Login request, it MUST send a Login + reject (with Status "invalid during login") and then disconnect. If + the initiator receives any PDU except a Login response, it MUST + immediately terminate the connection. + +3.2.4. iSCSI Full Feature Phase + + Once the initiator is authorized to do so, the iSCSI session is in + the iSCSI Full Feature Phase. A session is in Full Feature Phase + after successfully finishing the Login Phase on the first (leading) + + + +Satran, et al. Standards Track [Page 25] + +RFC 3720 iSCSI April 2004 + + + connection of a session. A connection is in Full Feature Phase if + the session is in Full Feature Phase and the connection login has + completed successfully. An iSCSI connection is not in Full Feature + Phase + + a) when it does not have an established transport connection, + + OR + + b) when it has a valid transport connection, but a successful + login was not performed or the connection is currently logged + out. + + In a normal Full Feature Phase, the initiator may send SCSI commands + and data to the various LUs on the target by encapsulating them in + iSCSI PDUs that go over the established iSCSI session. + +3.2.4.1. Command Connection Allegiance + + For any iSCSI request issued over a TCP connection, the corresponding + response and/or other related PDU(s) MUST be sent over the same + connection. We call this "connection allegiance". If the original + connection fails before the command is completed, the connection + allegiance of the command may be explicitly reassigned to a different + transport connection as described in detail in Section 6.2 Retry and + Reassign in Recovery. + + Thus, if an initiator issues a READ command, the target MUST send the + requested data, if any, followed by the status to the initiator over + the same TCP connection that was used to deliver the SCSI command. + If an initiator issues a WRITE command, the initiator MUST send the + data, if any, for that command over the same TCP connection that was + used to deliver the SCSI command. The target MUST return Ready To + Transfer (R2T), if any, and the status over the same TCP connection + that was used to deliver the SCSI command. Retransmission requests + (SNACK PDUs) and the data and status that they generate MUST also use + the same connection. + + However, consecutive commands that are part of a SCSI linked + command-chain task (see [SAM2]) MAY use different connections. + Connection allegiance is strictly per-command and not per-task. + During the iSCSI Full Feature Phase, the initiator and target MAY + interleave unrelated SCSI commands, their SCSI Data, and responses + over the session. + + + + + + + +Satran, et al. Standards Track [Page 26] + +RFC 3720 iSCSI April 2004 + + +3.2.4.2. Data Transfer Overview + + Outgoing SCSI data (initiator to target user data or command + parameters) is sent as either solicited data or unsolicited data. + Solicited data are sent in response to R2T PDUs. Unsolicited data + can be sent as part of an iSCSI command PDU ("immediate data") or in + separate iSCSI data PDUs. + + Immediate data are assumed to originate at offset 0 in the initiator + SCSI write-buffer (outgoing data buffer). All other Data PDUs have + the buffer offset set explicitly in the PDU header. + + An initiator may send unsolicited data up to FirstBurstLength as + immediate (up to the negotiated maximum PDU length), in a separate + PDU sequence or both. All subsequent data MUST be solicited. The + maximum length of an individual data PDU or the immediate-part of the + first unsolicited burst MAY be negotiated at login. + + The maximum amount of unsolicited data that can be sent with a + command is negotiated at login through the FirstBurstLength key. A + target MAY separately enable immediate data (through the + ImmediateData key) without enabling the more general (separate data + PDUs) form of unsolicited data (through the InitialR2T key). + + Unsolicited data on write are meant to reduce the effect of latency + on throughput (no R2T is needed to start sending data). In addition, + immediate data is meant to reduce the protocol overhead (both + bandwidth and execution time). + + An iSCSI initiator MAY choose not to send unsolicited data, only + immediate data or FirstBurstLength bytes of unsolicited data with a + command. If any non-immediate unsolicited data is sent, the total + unsolicited data MUST be either FirstBurstLength, or all of the data + if the total amount is less than the FirstBurstLength. + + It is considered an error for an initiator to send unsolicited data + PDUs to a target that operates in R2T mode (only solicited data are + allowed). It is also an error for an initiator to send more + unsolicited data, whether immediate or as separate PDUs, than + FirstBurstLength. + + An initiator MUST honor an R2T data request for a valid outstanding + command (i.e., carrying a valid Initiator Task Tag) and deliver all + the requested data provided the command is supposed to deliver + outgoing data and the R2T specifies data within the command bounds. + The initiator action is unspecified for receiving an R2T request that + specifies data, all or part, outside of the bounds of the command. + + + + +Satran, et al. Standards Track [Page 27] + +RFC 3720 iSCSI April 2004 + + + A target SHOULD NOT silently discard data and then request + retransmission through R2T. Initiators SHOULD NOT keep track of the + data transferred to or from the target (scoreboarding). SCSI targets + perform residual count calculation to check how much data was + actually transferred to or from the device by a command. This may + differ from the amount the initiator sent and/or received for reasons + such as retransmissions and errors. Read or bidirectional commands + implicitly solicit the transmission of the entire amount of data + covered by the command. SCSI data packets are matched to their + corresponding SCSI commands by using tags specified in the protocol. + + In addition, iSCSI initiators and targets MUST enforce some ordering + rules. When unsolicited data is used, the order of the unsolicited + data on each connection MUST match the order in which the commands on + that connection are sent. Command and unsolicited data PDUs may be + interleaved on a single connection as long as the ordering + requirements of each are maintained (e.g., command N+1 MAY be sent + before the unsolicited Data-Out PDUs for command N, but the + unsolicited Data-Out PDUs for command N MUST precede the unsolicited + Data-Out PDUs of command N+1). A target that receives data out of + order MAY terminate the session. + +3.2.4.3. Tags and Integrity Checks + + Initiator tags for pending commands are unique initiator-wide for a + session. Target tags are not strictly specified by the protocol. It + is assumed that target tags are used by the target to tag (alone or + in combination with the LUN) the solicited data. Target tags are + generated by the target and "echoed" by the initiator. These + mechanisms are designed to accomplish efficient data delivery along + with a large degree of control over the data flow. + + As the Initiator Task Tag is used to identify a task during its + execution, the iSCSI initiator and target MUST verify that all other + fields used in task-related PDUs have values that are consistent with + the values used at the task instantiation based on the Initiator Task + Tag (e.g., the LUN used in an R2T PDU MUST be the same as the one + used in the SCSI command PDU used to instantiate the task). Using + inconsistent field values is considered a protocol error. + +3.2.4.4. Task Management + + SCSI task management assumes that individual tasks and task groups + can be aborted solely based on the task tags (for individual tasks) + or the timing of the task management command (for task groups), and + that the task management action is executed synchronously - i.e., no + message involving an aborted task will be seen by the SCSI initiator + after receiving the task management response. In iSCSI initiators + + + +Satran, et al. Standards Track [Page 28] + +RFC 3720 iSCSI April 2004 + + + and targets interact asynchronously over several connections. iSCSI + specifies the protocol mechanism and implementation requirements + needed to present a synchronous view while using an asynchronous + infrastructure. + +3.2.5. iSCSI Connection Termination + + An iSCSI connection may be terminated by use of a transport + connection shutdown or a transport reset. Transport reset is assumed + to be an exceptional event. + + Graceful TCP connection shutdowns are done by sending TCP FINs. A + graceful transport connection shutdown SHOULD only be initiated by + either party when the connection is not in iSCSI Full Feature Phase. + A target MAY terminate a Full Feature Phase connection on internal + exception events, but it SHOULD announce the fact through an + Asynchronous Message PDU. Connection termination with outstanding + commands may require recovery actions. + + If a connection is terminated while in Full Feature Phase, connection + cleanup (see section 7) is required prior to recovery. By doing + connection cleanup before starting recovery, the initiator and target + will avoid receiving stale PDUs after recovery. + +3.2.6. iSCSI Names + + Both targets and initiators require names for the purpose of + identification. In addition, names enable iSCSI storage resources to + be managed regardless of location (address). An iSCSI node name is + also the SCSI device name of an iSCSI device. The iSCSI name of a + SCSI device is the principal object used in authentication of targets + to initiators and initiators to targets. This name is also used to + identify and manage iSCSI storage resources. + + iSCSI names must be unique within the operational domain of the end + user. However, because the operational domain of an IP network is + potentially worldwide, the iSCSI name formats are architected to be + worldwide unique. To assist naming authorities in the construction + of worldwide unique names, iSCSI provides two name formats for + different types of naming authorities. + + iSCSI names are associated with iSCSI nodes, and not iSCSI network + adapter cards, to ensure that the replacement of network adapter + cards does not require reconfiguration of all SCSI and iSCSI resource + allocation information. + + + + + + +Satran, et al. Standards Track [Page 29] + +RFC 3720 iSCSI April 2004 + + + Some SCSI commands require that protocol-specific identifiers be + communicated within SCSI CDBs. See Section 3.4.2 SCSI Architecture + Model for the definition of the SCSI port name/identifier for iSCSI + ports. + + An initiator may discover the iSCSI Target Names to which it has + access, along with their addresses, using the SendTargets text + request, or other techniques discussed in [RFC3721]. + +3.2.6.1. iSCSI Name Properties + + Each iSCSI node, whether an initiator or target, MUST have an iSCSI + name. + + Initiators and targets MUST support the receipt of iSCSI names of up + to the maximum length of 223 bytes. + + The initiator MUST present both its iSCSI Initiator Name and the + iSCSI Target Name to which it wishes to connect in the first login + request of a new session or connection. The only exception is if a + discovery session (see Section 2.3 iSCSI Session Types) is to be + established. In this case, the iSCSI Initiator Name is still + required, but the iSCSI Target Name MAY be omitted. + + iSCSI names have the following properties: + + a) iSCSI names are globally unique. No two initiators or targets + can have the same name. + b) iSCSI names are permanent. An iSCSI initiator node or target + node has the same name for its lifetime. + c) iSCSI names do not imply a location or address. An iSCSI + initiator or target can move, or have multiple addresses. A + change of address does not imply a change of name. + d) iSCSI names do not rely on a central name broker; the naming + authority is distributed. + e) iSCSI names support integration with existing unique naming + schemes. + f) iSCSI names rely on existing naming authorities. iSCSI does + not create any new naming authority. + + The encoding of an iSCSI name has the following properties: + + a) iSCSI names have the same encoding method regardless of the + underlying protocols. + b) iSCSI names are relatively simple to compare. The algorithm + for comparing two iSCSI names for equivalence does not rely on + an external server. + + + + +Satran, et al. Standards Track [Page 30] + +RFC 3720 iSCSI April 2004 + + + c) iSCSI names are composed only of displayable characters. iSCSI + names allow the use of international character sets but are not + case sensitive. No whitespace characters are used in iSCSI + names. + d) iSCSI names may be transported using both binary and + ASCII-based protocols. + + An iSCSI name really names a logical software entity, and is not tied + to a port or other hardware that can be changed. For instance, an + initiator name should name the iSCSI initiator node, not a particular + NIC or HBA. When multiple NICs are used, they should generally all + present the same iSCSI initiator name to the targets, because they + are simply paths to the same SCSI layer. In most operating systems, + the named entity is the operating system image. + + Similarly, a target name should not be tied to hardware interfaces + that can be changed. A target name should identify the logical + target and must be the same for the target regardless of the physical + portion being addressed. This assists iSCSI initiators in + determining that the two targets it has discovered are really two + paths to the same target. + + The iSCSI name is designed to fulfill the functional requirements for + Uniform Resource Names (URN) [RFC1737]. For example, it is required + that the name have a global scope, be independent of address or + location, and be persistent and globally unique. Names must be + extensible and scalable with the use of naming authorities. The name + encoding should be both human and machine readable. See [RFC1737] + for further requirements. + +3.2.6.2. iSCSI Name Encoding + + An iSCSI name MUST be a UTF-8 encoding of a string of Unicode + characters with the following properties: + + - It is in Normalization Form C (see "Unicode Normalization + Forms" [UNICODE]). + - It only contains characters allowed by the output of the iSCSI + stringprep template (described in [RFC3722]). + - The following characters are used for formatting iSCSI names: + + - dash ('-'=U+002d) + - dot ('.'=U+002e) + - colon (':'=U+003a) + + - The UTF-8 encoding of the name is not larger than 223 bytes. + + + + + +Satran, et al. Standards Track [Page 31] + +RFC 3720 iSCSI April 2004 + + + The stringprep process is described in [RFC3454]; iSCSI's use of the + stringprep process is described in [RFC3722]. Stringprep is a method + designed by the Internationalized Domain Name (IDN) working group to + translate human-typed strings into a format that can be compared as + opaque strings. Strings MUST NOT include punctuation, spacing, + diacritical marks, or other characters that could get in the way of + readability. The stringprep process also converts strings into + equivalent strings of lower-case characters. + + The stringprep process does not need to be implemented if the names + are only generated using numeric and lower-case (any character set) + alphabetic characters. + + Once iSCSI names encoded in UTF-8 are "normalized" they may be safely + compared byte-for-byte. + +3.2.6.3. iSCSI Name Structure + + An iSCSI name consists of two parts--a type designator followed by a + unique name string. + + The iSCSI name does not define any new naming authorities. Instead, + it supports two existing ways of designating naming authorities: an + iSCSI-Qualified Name, using domain names to identify a naming + authority, and the EUI format, where the IEEE Registration Authority + assists in the formation of worldwide unique names (EUI-64 format). + + The type designator strings currently defined are: + + iqn. - iSCSI Qualified name + eui. - Remainder of the string is an IEEE EUI-64 + identifier, in ASCII-encoded hexadecimal. + + These two naming authority designators were considered sufficient at + the time of writing this document. The creation of additional naming + type designators for iSCSI may be considered by the IETF and detailed + in separate RFCs. + +3.2.6.3.1. Type "iqn." (iSCSI Qualified Name) + + This iSCSI name type can be used by any organization that owns a + domain name. This naming format is useful when an end user or + service provider wishes to assign iSCSI names for targets and/or + initiators. + + To generate names of this type, the person or organization generating + the name must own a registered domain name. This domain name does + not have to be active, and does not have to resolve to an address; it + + + +Satran, et al. Standards Track [Page 32] + +RFC 3720 iSCSI April 2004 + + + just needs to be reserved to prevent others from generating iSCSI + names using the same domain name. + + Since a domain name can expire, be acquired by another entity, or may + be used to generate iSCSI names by both owners, the domain name must + be additionally qualified by a date during which the naming authority + owned the domain name. For this reason, a date code is provided as + part of the "iqn." format. + + The iSCSI qualified name string consists of: + + - The string "iqn.", used to distinguish these names from "eui." + formatted names. + - A date code, in yyyy-mm format. This date MUST be a date + during which the naming authority owned the domain name used in + this format, and SHOULD be the first month in which the domain + name was owned by this naming authority at 00:01 GMT of the + first day of the month. This date code uses the Gregorian + calendar. All four digits in the year must be present. Both + digits of the month must be present, with January == "01" and + December == "12". The dash must be included. + - A dot "." + - The reversed domain name of the naming authority (person or + organization) creating this iSCSI name. + - An optional, colon (:) prefixed, string within the character + set and length boundaries that the owner of the domain name + deems appropriate. This may contain product types, serial + numbers, host identifiers, or software keys (e.g., it may + include colons to separate organization boundaries). With the + exception of the colon prefix, the owner of the domain name can + assign everything after the reversed domain name as desired. + It is the responsibility of the entity that is the naming + authority to ensure that the iSCSI names it assigns are + worldwide unique. For example, "Example Storage Arrays, Inc.", + might own the domain name "example.com". + + The following are examples of iSCSI qualified names that might be + generated by "EXAMPLE Storage Arrays, Inc." + + Naming String defined by + Type Date Auth "example.com" naming authority + +--++-----+ +---------+ +--------------------------------+ + | || | | | | | + + iqn.2001-04.com.example:storage:diskarrays-sn-a8675309 + iqn.2001-04.com.example + iqn.2001-04.com.example:storage.tape1.sys1.xyz + iqn.2001-04.com.example:storage.disk2.sys1.xyz + + + +Satran, et al. Standards Track [Page 33] + +RFC 3720 iSCSI April 2004 + + + +3.2.6.3.2. Type "eui." (IEEE EUI-64 format) + + The IEEE Registration Authority provides a service for assigning + globally unique identifiers [EUI]. The EUI-64 format is used to + build a global identifier in other network protocols. For example, + Fibre Channel defines a method of encoding it into a WorldWideName. + For more information on registering for EUI identifiers, see [OUI]. + + The format is "eui." followed by an EUI-64 identifier (16 + ASCII-encoded hexadecimal digits). + + Example iSCSI name: + + Type EUI-64 identifier (ASCII-encoded hexadecimal) + +--++--------------+ + | || | + eui.02004567A425678D + + The IEEE EUI-64 iSCSI name format might be used when a manufacturer + is already registered with the IEEE Registration Authority and uses + EUI-64 formatted worldwide unique names for its products. + + More examples of name construction are discussed in [RFC3721]. + +3.2.7. Persistent State + + iSCSI does not require any persistent state maintenance across + sessions. However, in some cases, SCSI requires persistent + identification of the SCSI initiator port name (See Section 3.4.2 + SCSI Architecture Model and Section 3.4.3 Consequences of the Model). + + iSCSI sessions do not persist through power cycles and boot + operations. + + All iSCSI session and connection parameters are re-initialized upon + session and connection creation. + + Commands persist beyond connection termination if the session + persists and command recovery within the session is supported. + However, when a connection is dropped, command execution, as + perceived by iSCSI (i.e., involving iSCSI protocol exchanges for the + affected task), is suspended until a new allegiance is established by + the 'task reassign' task management function. (See Section 10.5 Task + Management Function Request.) + + + + + + +Satran, et al. Standards Track [Page 34] + +RFC 3720 iSCSI April 2004 + + +3.2.8. Message Synchronization and Steering + + iSCSI presents a mapping of the SCSI protocol onto TCP. This + encapsulation is accomplished by sending iSCSI PDUs of varying + lengths. Unfortunately, TCP does not have a built-in mechanism for + signaling message boundaries at the TCP layer. iSCSI overcomes this + obstacle by placing the message length in the iSCSI message header. + This serves to delineate the end of the current message as well as + the beginning of the next message. + + In situations where IP packets are delivered in order from the + network, iSCSI message framing is not an issue and messages are + processed one after the other. In the presence of IP packet + reordering (i.e., frames being dropped), legacy TCP implementations + store the "out of order" TCP segments in temporary buffers until the + missing TCP segments arrive, upon which the data must be copied to + the application buffers. In iSCSI, it is desirable to steer the SCSI + data within these out of order TCP segments into the pre-allocated + SCSI buffers rather than store them in temporary buffers. This + decreases the need for dedicated reassembly buffers as well as the + latency and bandwidth related to extra copies. + + Relying solely on the "message length" information from the iSCSI + message header may make it impossible to find iSCSI message + boundaries in subsequent TCP segments due to the loss of a TCP + segment that contains the iSCSI message length. The missing TCP + segment(s) must be received before any of the following segments can + be steered to the correct SCSI buffers (due to the inability to + determine the iSCSI message boundaries). Since these segments cannot + be steered to the correct location, they must be saved in temporary + buffers that must then be copied to the SCSI buffers. + + Different schemes can be used to recover synchronization. To make + these schemes work, iSCSI implementations have to make sure that the + appropriate protocol layers are provided with enough information to + implement a synchronization and/or data steering mechanism. One of + these schemes is detailed in Appendix A. - Sync and Steering with + Fixed Interval Markers -. + + The Fixed Interval Markers (FIM) scheme works by inserting markers in + the payload stream at fixed intervals that contain the offset for the + start of the next iSCSI PDU. + + Under normal circumstances (no PDU loss or data reception out of + order), iSCSI data steering can be accomplished by using the + identifying tag and the data offset fields in the iSCSI header in + addition to the TCP sequence number from the TCP header. The + + + + +Satran, et al. Standards Track [Page 35] + +RFC 3720 iSCSI April 2004 + + + identifying tag helps associate the PDU with a SCSI buffer address + while the data offset and TCP sequence number are used to determine + the offset within the buffer. + + When the part of the TCP data stream containing an iSCSI PDU header + is delayed or lost, markers may be used to minimize the damage as + follows: + + - Markers indicate where the next iSCSI PDU starts and enable + continued processing when iSCSI headers have to be dropped due to + data errors discovered at the iSCSI level (e.g., iSCSI header CRC + errors). + + - Markers help minimize the amount of data that has to be kept by + the TCP/iSCSI layer while waiting for a late TCP packet arrival + or recovery, because later they might help find iSCSI PDU headers + and use the information contained in those to steer data to SCSI + buffers. + +3.2.8.1. Sync/Steering and iSCSI PDU Length + + When a large iSCSI message is sent, the TCP segment(s) that contain + the iSCSI header may be lost. The remaining TCP segment(s), up to + the next iSCSI message, must be buffered (in temporary buffers) + because the iSCSI header that indicates to which SCSI buffers the + data are to be steered was lost. To minimize the amount of + buffering, it is recommended that the iSCSI PDU length be restricted + to a small value (perhaps a few TCP segments in length). During + login, each end of the iSCSI session specifies the maximum iSCSI PDU + length it will accept. + +3.3. iSCSI Session Types + + iSCSI defines two types of sessions: + + a) Normal operational session - an unrestricted session. + b) Discovery-session - a session only opened for target + discovery. The target MUST ONLY accept text requests with the + SendTargets key and a logout request with the reason "close + the session". All other requests MUST be rejected. + + The session type is defined during login with the key=value parameter + in the login command. + + + + + + + + +Satran, et al. Standards Track [Page 36] + +RFC 3720 iSCSI April 2004 + + +3.4. SCSI to iSCSI Concepts Mapping Model + + The following diagram shows an example of how multiple iSCSI Nodes + (targets in this case) can coexist within the same Network Entity and + can share Network Portals (IP addresses and TCP ports). Other more + complex configurations are also possible. For detailed descriptions + of the components of these diagrams, see Section 3.4.1 iSCSI + Architecture Model. + + +-----------------------------------+ + | Network Entity (iSCSI Client) | + | | + | +-------------+ | + | | iSCSI Node | | + | | (Initiator) | | + | +-------------+ | + | | | | + | +--------------+ +--------------+ | + | |Network Portal| |Network Portal| | + | | 10.1.30.4 | | 10.1.40.6 | | + +-+--------------+-+--------------+-+ + | | + | IP Networks | + | | + +-+--------------+-+--------------+-+ + | |Network Portal| |Network Portal| | + | | 10.1.30.21 | | 10.1.40.3 | | + | | TCP Port 3260| | TCP Port 3260| | + | +--------------+ +--------------+ | + | | | | + | ----------------- | + | | | | + | +-------------+ +--------------+ | + | | iSCSI Node | | iSCSI Node | | + | | (Target) | | (Target) | | + | +-------------+ +--------------+ | + | | + | Network Entity (iSCSI Server) | + +-----------------------------------+ + +3.4.1. iSCSI Architecture Model + + This section describes the part of the iSCSI architecture model that + has the most bearing on the relationship between iSCSI and the SCSI + Architecture Model. + + + + + + +Satran, et al. Standards Track [Page 37] + +RFC 3720 iSCSI April 2004 + + + a) Network Entity - represents a device or gateway that is + accessible from the IP network. A Network Entity must have + one or more Network Portals (see item d), each of which can be + used by some iSCSI Nodes (see item (b)) contained in that + Network Entity to gain access to the IP network. + + b) iSCSI Node - represents a single iSCSI initiator or iSCSI + target. There are one or more iSCSI Nodes within a Network + Entity. The iSCSI Node is accessible via one or more Network + Portals (see item d). An iSCSI Node is identified by its + iSCSI Name (see Section 3.2.6 iSCSI Names and Chapter 12). + The separation of the iSCSI Name from the addresses used by + and for the iSCSI node allows multiple iSCSI nodes to use the + same addresses, and the same iSCSI node to use multiple + addresses. + + c) An alias string may also be associated with an iSCSI Node. + The alias allows an organization to associate a user friendly + string with the iSCSI Name. However, the alias string is not + a substitute for the iSCSI Name. + + d) Network Portal - a component of a Network Entity that has a + TCP/IP network address and that may be used by an iSCSI Node + within that Network Entity for the connection(s) within one of + its iSCSI sessions. In an initiator, it is identified by its + IP address. In a target, it is identified by its IP address + and its listening TCP port. + + e) Portal Groups - iSCSI supports multiple connections within the + same session; some implementations will have the ability to + combine connections in a session across multiple Network + Portals. A Portal Group defines a set of Network Portals + within an iSCSI Node that collectively supports the capability + of coordinating a session with connections that span these + portals. Not all Network Portals within a Portal Group need + to participate in every session connected through that Portal + Group. One or more Portal Groups may provide access to an + iSCSI Node. Each Network Portal, as utilized by a given iSCSI + Node, belongs to exactly one portal group within that node. + Portal Groups are identified within an iSCSI Node by a portal + group tag, a simple unsigned-integer between 0 and 65535 (see + Section 12.3 SendTargets). All Network Portals with the same + portal group tag in the context of a given iSCSI Node are in + the same Portal Group. + + + + + + + +Satran, et al. Standards Track [Page 38] + +RFC 3720 iSCSI April 2004 + + + Both iSCSI Initiators and iSCSI Targets have portal groups, + though only the iSCSI Target Portal Groups are used directly + in the iSCSI protocol (e.g., in SendTargets). For references + to the initiator Portal Groups, see Section 9.1.1 Conservative + Reuse of ISIDs. + + f) Portals within a Portal Group should support similar session + parameters, because they may participate in a common session. + + The following diagram shows an example of one such configuration on a + target and how a session that shares Network Portals within a Portal + Group may be established. + + ----------------------------IP Network--------------------- + | | | + +----|---------------|-----+ +----|---------+ + | +---------+ +---------+ | | +---------+ | + | | Network | | Network | | | | Network | | + | | Portal | | Portal | | | | Portal | | + | +--|------+ +---------+ | | +---------+ | + | | | | | | | + | | Portal | | | | Portal | + | | Group 1 | | | | Group 2 | + +--------------------------+ +--------------+ + | | | + +--------|---------------|--------------------|--------------------+ + | | | | | + | +----------------------------+ +-----------------------------+ | + | | iSCSI Session (Target side)| | iSCSI Session (Target side) | | + | | | | | | + | | (TSIH = 56) | | (TSIH = 48) | | + | +----------------------------+ +-----------------------------+ | + | | + | iSCSI Target Node | + | (within Network Entity, not shown) | + +------------------------------------------------------------------+ + +3.4.2. SCSI Architecture Model + + This section describes the relationship between the SCSI Architecture + Model [SAM2] and the constructs of the SCSI device, SCSI port and I_T + nexus, and the iSCSI constructs described in Section 3.4.1 iSCSI + Architecture Model. + + This relationship implies implementation requirements in order to + conform to the SAM2 model and other SCSI operational functions. + These requirements are detailed in Section 3.4.3 Consequences of the + Model. + + + +Satran, et al. Standards Track [Page 39] + +RFC 3720 iSCSI April 2004 + + + The following list outlines mappings of SCSI architectural elements + to iSCSI. + + a) SCSI Device - the SAM2 term for an entity that contains one or + more SCSI ports that are connected to a service delivery + subsystem and supports a SCSI application protocol. For + example, a SCSI Initiator Device contains one or more SCSI + Initiator Ports and zero or more application clients. A SCSI + Target Device contains one or more SCSI Target Ports and one + or more logical units. For iSCSI, the SCSI Device is the + component within an iSCSI Node that provides the SCSI + functionality. As such, there can be one SCSI Device, at + most, within an iSCSI Node. Access to the SCSI Device can + only be achieved in an iSCSI normal operational session (see + Section 3.3 iSCSI Session Types). The SCSI Device Name is + defined to be the iSCSI Name of the node and MUST be used in + the iSCSI protocol. + + b) SCSI Port - the SAM2 term for an entity in a SCSI Device that + provides the SCSI functionality to interface with a service + delivery subsystem or transport. For iSCSI, the definition of + SCSI Initiator Port and SCSI Target Port are different. + + SCSI Initiator Port: This maps to one endpoint of an iSCSI + normal operational session (see Section 3.3 iSCSI Session + Types). An iSCSI normal operational session is negotiated + through the login process between an iSCSI initiator node and + an iSCSI target node. At successful completion of this + process, a SCSI Initiator Port is created within the SCSI + Initiator Device. The SCSI Initiator Port Name and SCSI + Initiator Port Identifier are both defined to be the iSCSI + Initiator Name together with (a) a label that identifies it as + an initiator port name/identifier and (b) the ISID portion of + the session identifier. + + SCSI Target Port: This maps to an iSCSI Target Portal Group. + The SCSI Target Port Name and the SCSI Target Port Identifier + are both defined to be the iSCSI Target Name together with (a) + a label that identifies it as a target port name/identifier + and (b) the portal group tag. + + The SCSI Port Name MUST be used in iSCSI. When used in SCSI + parameter data, the SCSI port name MUST be encoded as: + - The iSCSI Name in UTF-8 format, followed by + - a comma separator (1 byte), followed by + - the ASCII character 'i' (for SCSI Initiator Port) or the + ASCII character 't' (for SCSI Target Port) (1 byte), + followed by + + + +Satran, et al. Standards Track [Page 40] + +RFC 3720 iSCSI April 2004 + + + - a comma separator (1 byte), followed by + - a text encoding as a hex-constant (see Section 5.1 Text + Format) of the ISID (for SCSI initiator port) or the portal + group tag (for SCSI target port) including the initial 0X + or 0x and the terminating null (15 bytes). + + The ASCII character 'i' or 't' is the label that identifies + this port as either a SCSI Initiator Port or a SCSI Target + Port. + + c) I_T nexus - a relationship between a SCSI Initiator Port and a + SCSI Target Port, according to [SAM2]. For iSCSI, this + relationship is a session, defined as a relationship between + an iSCSI Initiator's end of the session (SCSI Initiator Port) + and the iSCSI Target's Portal Group. The I_T nexus can be + identified by the conjunction of the SCSI port names or by the + iSCSI session identifier SSID. iSCSI defines the I_T nexus + identifier to be the tuple (iSCSI Initiator Name + 'i' + ISID, + iSCSI Target Name + 't' + Portal Group Tag). + + NOTE: The I_T nexus identifier is not equal to the session + identifier (SSID). + +3.4.3. Consequences of the Model + + This section describes implementation and behavioral requirements + that result from the mapping of SCSI constructs to the iSCSI + constructs defined above. Between a given SCSI initiator port and a + given SCSI target port, only one I_T nexus (session) can exist. No + more than one nexus relationship (parallel nexus) is allowed by + [SAM2]. Therefore, at any given time, only one session can exist + between a given iSCSI initiator node and an iSCSI target node, with + the same session identifier (SSID). + + These assumptions lead to the following conclusions and requirements: + + ISID RULE: Between a given iSCSI Initiator and iSCSI Target Portal + Group (SCSI target port), there can only be one session with a given + value for ISID that identifies the SCSI initiator port. See Section + 10.12.5 ISID. + + The structure of the ISID that contains a naming authority component + (see Section 10.12.5 ISID and [RFC3721]) provides a mechanism to + facilitate compliance with the ISID rule. (See Section 9.1.1 + Conservative Reuse of ISIDs.) + + + + + + +Satran, et al. Standards Track [Page 41] + +RFC 3720 iSCSI April 2004 + + + The iSCSI Initiator Node should manage the assignment of ISIDs prior + to session initiation. The "ISID RULE" does not preclude the use of + the same ISID from the same iSCSI Initiator with different Target + Portal Groups on the same iSCSI target or on other iSCSI targets (see + Section 9.1.1 Conservative Reuse of ISIDs). Allowing this would be + analogous to a single SCSI Initiator Port having relationships + (nexus) with multiple SCSI target ports on the same SCSI target + device or SCSI target ports on other SCSI target devices. It is also + possible to have multiple sessions with different ISIDs to the same + Target Portal Group. Each such session would be considered to be + with a different initiator even when the sessions originate from the + same initiator device. The same ISID may be used by a different + iSCSI initiator because it is the iSCSI Name together with the ISID + that identifies the SCSI Initiator Port. + + NOTE: A consequence of the ISID RULE and the specification for the + I_T nexus identifier is that two nexus with the same identifier + should never exist at the same time. + + TSIH RULE: The iSCSI Target selects a non-zero value for the TSIH at + session creation (when an initiator presents a 0 value at Login). + After being selected, the same TSIH value MUST be used whenever the + initiator or target refers to the session and a TSIH is required. + +3.4.3.1. I_T Nexus State + + Certain nexus relationships contain an explicit state (e.g., + initiator-specific mode pages) that may need to be preserved by the + device server [SAM2] in a logical unit through changes or failures in + the iSCSI layer (e.g., session failures). In order for that state to + be restored, the iSCSI initiator should reestablish its session + (re-login) to the same Target Portal Group using the previous ISID. + That is, it should perform session recovery as described in Chapter + 6. This is because the SCSI initiator port identifier and the SCSI + target port identifier (or relative target port) form the datum that + the SCSI logical unit device server uses to identify the I_T nexus. + +3.5. Request/Response Summary + + This section lists and briefly describes all the iSCSI PDU types + (request and responses). + + All iSCSI PDUs are built as a set of one or more header segments + (basic and auxiliary) and zero or one data segments. The header + group and the data segment may each be followed by a CRC (digest). + + The basic header segment has a fixed length of 48 bytes. + + + + +Satran, et al. Standards Track [Page 42] + +RFC 3720 iSCSI April 2004 + + +3.5.1. Request/Response Types Carrying SCSI Payload + +3.5.1.1. SCSI-Command + + This request carries the SCSI CDB and all the other SCSI execute + command procedure call (see [SAM2]) IN arguments such as task + attributes, Expected Data Transfer Length for one or both transfer + directions (the latter for bidirectional commands), and Task Tag (as + part of the I_T_L_x nexus). The I_T_L nexus is derived by the + initiator and target from the LUN field in the request and the I_T + nexus is implicit in the session identification. + + In addition, the SCSI-command PDU carries information required for + the proper operation of the iSCSI protocol - the command sequence + number (CmdSN) for the session and the expected status number + (ExpStatSN) for the connection. + + All or part of the SCSI output (write) data associated with the SCSI + command may be sent as part of the SCSI-Command PDU as a data + segment. + +3.5.1.2. SCSI-Response + + The SCSI-Response carries all the SCSI execute-command procedure call + (see [SAM2]) OUT arguments and the SCSI execute-command procedure + call return value. + + The SCSI-Response contains the residual counts from the operation, if + any, an indication of whether the counts represent an overflow or an + underflow, and the SCSI status if the status is valid or a response + code (a non-zero return value for the execute-command procedure call) + if the status is not valid. + + For a valid status that indicates that the command has been + processed, but resulted in an exception (e.g., a SCSI CHECK + CONDITION), the PDU data segment contains the associated sense data. + The use of Autosense ([SAM2]) is REQUIRED by iSCSI. + + Some data segment content may also be associated (in the data + segment) with a non-zero response code. + + In addition, the SCSI-Response PDU carries information required for + the proper operation of the iSCSI protocol: + + - The number of Data-In PDUs that a target has sent (to enable + the initiator to check that all have arrived). + - StatSN - the Status Sequence Number on this connection. + + + + +Satran, et al. Standards Track [Page 43] + +RFC 3720 iSCSI April 2004 + + + - ExpCmdSN - the next Expected Command Sequence Number at the + target. + - MaxCmdSN - the maximum CmdSN acceptable at the target from + this initiator. + +3.5.1.3 Task Management Function Request + + The Task Management function request provides an initiator with a way + to explicitly control the execution of one or more SCSI Tasks or + iSCSI functions. The PDU carries a function identifier (which task + management function to perform) and enough information to + unequivocally identify the task or task-set on which to perform the + action, even if the task(s) to act upon has not yet arrived or has + been discarded due to an error. + + The referenced tag identifies an individual task if the function + refers to an individual task. + + The I_T_L nexus identifies task sets. In iSCSI the I_T_L nexus is + identified by the LUN and the session identification (the session + identifies an I_T nexus). + + For task sets, the CmdSN of the Task Management function request + helps identify the tasks upon which to act, namely all tasks + associated with a LUN and having a CmdSN preceding the Task + Management function request CmdSN. + + For a Task Management function, the coordination between responses to + the tasks affected and the Task Management function response is done + by the target. + +3.5.1.4. Task Management Function Response + + The Task Management function response carries an indication of + function completion for a Task Management function request including + how it was completed (response and qualifier) and additional + information for failure responses. + + After the Task Management response indicates Task Management function + completion, the initiator will not receive any additional responses + from the affected tasks. + +3.5.1.5. SCSI Data-Out and SCSI Data-In + + SCSI Data-Out and SCSI Data-In are the main vehicles by which SCSI + data payload is carried between initiator and target. Data payload + is associated with a specific SCSI command through the Initiator Task + Tag. For target convenience, outgoing solicited data also carries a + + + +Satran, et al. Standards Track [Page 44] + +RFC 3720 iSCSI April 2004 + + + Target Transfer Tag (copied from R2T) and the LUN. Each PDU contains + the payload length and the data offset relative to the buffer address + contained in the SCSI execute command procedure call. + + In each direction, the data transfer is split into "sequences". An + end-of-sequence is indicated by the F bit. + + An outgoing sequence is either unsolicited (only the first sequence + can be unsolicited) or consists of all the Data-Out PDUs sent in + response to an R2T. + + Input sequences are built to enable the direction switching for + bidirectional commands. + + For input, the target may request positive acknowledgement of input + data. This is limited to sessions that support error recovery and is + implemented through the A bit in the SCSI Data-In PDU header. + + Data-In and Data-Out PDUs also carry the DataSN to enable the + initiator and target to detect missing PDUs (discarded due to an + error). + + In addition, StatSN is carried by the Data-In PDUs. + + To enable a SCSI command to be processed while involving a minimum + number of messages, the last SCSI Data-In PDU passed for a command + may also contain the status if the status indicates termination with + no exceptions (no sense or response involved). + +3.5.1.6. Ready To Transfer (R2T) + + R2T is the mechanism by which the SCSI target "requests" the + initiator for output data. R2T specifies to the initiator the offset + of the requested data relative to the buffer address from the execute + command procedure call and the length of the solicited data. + + To help the SCSI target associate the resulting Data-Out with an R2T, + the R2T carries a Target Transfer Tag that will be copied by the + initiator in the solicited SCSI Data-Out PDUs. There are no protocol + specific requirements with regard to the value of these tags, but it + is assumed that together with the LUN, they will enable the target to + associate data with an R2T. + + + + + + + + + +Satran, et al. Standards Track [Page 45] + +RFC 3720 iSCSI April 2004 + + + R2T also carries information required for proper operation of the + iSCSI protocol, such as: + + - R2TSN (to enable an initiator to detect a missing R2T) + - StatSN + - ExpCmdSN + - MaxCmdSN + +3.5.2. Requests/Responses carrying SCSI and iSCSI Payload + +3.5.2.1. Asynchronous Message + + Asynchronous Messages are used to carry SCSI asynchronous events + (AEN) and iSCSI asynchronous messages. + + When carrying an AEN, the event details are reported as sense data in + the data segment. + +3.5.3. Requests/Responses Carrying iSCSI Only Payload + +3.5.3.1. Text Request and Text Response + + Text requests and responses are designed as a parameter negotiation + vehicle and as a vehicle for future extension. + + In the data segment, Text Requests/Responses carry text information + using a simple "key=value" syntax. + + Text Request/Responses may form extended sequences using the same + Initiator Task Tag. The initiator uses the F (Final) flag bit in the + text request header to indicate its readiness to terminate a + sequence. The target uses the F (Final) flag bit in the text + response header to indicate its consent to sequence termination. + + Text Request and Responses also use the Target Transfer Tag to + indicate continuation of an operation or a new beginning. A target + that wishes to continue an operation will set the Target Transfer Tag + in a Text Response to a value different from the default 0xffffffff. + An initiator willing to continue will copy this value into the Target + Transfer Tag of the next Text Request. If the initiator wants to + restart the current target negotiation (start fresh) will set the + Target Transfer Tag to 0xffffffff. + + Although a complete exchange is always started by the initiator, + specific parameter negotiations may be initiated by the initiator or + target. + + + + + +Satran, et al. Standards Track [Page 46] + +RFC 3720 iSCSI April 2004 + + +3.5.3.2. Login Request and Login Response + + Login Requests and Responses are used exclusively during the Login + Phase of each connection to set up the session and connection + parameters. (The Login Phase consists of a sequence of login + requests and responses carrying the same Initiator Task Tag.) + + A connection is identified by an arbitrarily selected connection-ID + (CID) that is unique within a session. + + Similar to the Text Requests and Responses, Login Requests/Responses + carry key=value text information with a simple syntax in the data + segment. + + The Login Phase proceeds through several stages (security + negotiation, operational parameter negotiation) that are selected + with two binary coded fields in the header -- the "current stage" + (CSG) and the "next stage" (NSG) with the appearance of the latter + being signaled by the "transit" flag (T). + + The first Login Phase of a session plays a special role, called the + leading login, which determines some header fields (e.g., the version + number, the maximum number of connections, and the session + identification). + + The CmdSN initial value is also set by the leading login. + + StatSN for each connection is initiated by the connection login. + + A login request may indicate an implied logout (cleanup) of the + connection to be logged in (a connection restart) by using the same + Connection ID (CID) as an existing connection, as well as the same + session identifying elements of the session to which the old + connection was associated. + +3.5.3.3. Logout Request and Response + + Logout Requests and Responses are used for the orderly closing of + connections for recovery or maintenance. The logout request may be + issued following a target prompt (through an asynchronous message) or + at an initiators initiative. When issued on the connection to be + logged out, no other request may follow it. + + The Logout Response indicates that the connection or session cleanup + is completed and no other responses will arrive on the connection (if + received on the logging out connection). In addition, the Logout + Response indicates how long the target will continue to hold + resources for recovery (e.g., command execution that continues on a + + + +Satran, et al. Standards Track [Page 47] + +RFC 3720 iSCSI April 2004 + + + new connection) in the text key Time2Retain and how long the + initiator must wait before proceeding with recovery in the text key + Time2Wait. + +3.5.3.4. SNACK Request + + With the SNACK Request, the initiator requests retransmission of + numbered-responses or data from the target. A single SNACK request + covers a contiguous set of missing items, called a run, of a given + type of items. The type is indicated in a type field in the PDU + header. The run is composed of an initial item (StatSN, DataSN, + R2TSN) and the number of missed Status, Data, or R2T PDUs. For long + Data-In sequences, the target may request (at predefined minimum + intervals) a positive acknowledgement for the data sent. A SNACK + request with a type field that indicates ACK and the number of + Data-In PDUs acknowledged conveys this positive acknowledgement. + +3.5.3.5. Reject + + Reject enables the target to report an iSCSI error condition (e.g., + protocol, unsupported option) that uses a Reason field in the PDU + header and includes the complete header of the bad PDU in the Reject + PDU data segment. + +3.5.3.6. NOP-Out Request and NOP-In Response + + This request/response pair may be used by an initiator and target as + a "ping" mechanism to verify that a connection/session is still + active and all of its components are operational. Such a ping may be + triggered by the initiator or target. The triggering party indicates + that it wants a reply by setting a value different from the default + 0xffffffff in the corresponding Initiator/Target Transfer Tag. + + NOP-In/NOP-Out may also be used "unidirectional" to convey to the + initiator/target command, status or data counter values when there is + no other "carrier" and there is a need to update the initiator/ + target. + +4. SCSI Mode Parameters for iSCSI + + There are no iSCSI specific mode pages. + +5. Login and Full Feature Phase Negotiation + + iSCSI parameters are negotiated at session or connection + establishment by using Login Requests and Responses (see Section + 3.2.3 iSCSI Login) and during the Full Feature Phase (Section 3.2.4 + iSCSI Full Feature Phase) by using Text Requests and Responses. In + + + +Satran, et al. Standards Track [Page 48] + +RFC 3720 iSCSI April 2004 + + + both cases the mechanism used is an exchange of iSCSI-text-key=value + pairs. For brevity iSCSI-text-keys are called just keys in the rest + of this document. + + Keys are either declarative or require negotiation and the key + description indicates if the key is declarative or requires + negotiation. + + For the declarative keys, the declaring party sets a value for the + key. The key specification indicates if the key can be declared by + the initiator, target or both. + + For the keys that require negotiation one of the parties (the + proposing party) proposes a value or set of values by including the + key=value in the data part of a Login or Text Request or Response + PDUs. The other party (the accepting party) makes a selection based + on the value or list of values proposed and includes the selected + value in a key=value in the data part of one of the following Login + or Text Response or Request PDUs. For most of the keys both the + initiator and target can be proposing parties. + + The login process proceeds in two stages - the security negotiation + stage and the operational parameter negotiation stage. Both stages + are optional but at least one of them has to be present to enable the + setting of some mandatory parameters. + + If present, the security negotiation stage precedes the operational + parameter negotiation stage. + + Progression from stage to stage is controlled by the T (Transition) + bit in the Login Request/Response PDU header. Through the T bit set + to 1, the initiator indicates that it would like to transition. The + target agrees to the transition (and selects the next stage) when + ready. A field in the Login PDU header indicates the current stage + (CSG) and during transition, another field indicates the next stage + (NSG) proposed (initiator) and selected (target). + + The text negotiation process is used to negotiate or declare + operational parameters. The negotiation process is controlled by the + F (final) bit in the PDU header. During text negotiations, the F bit + is used by the initiator to indicate that it is ready to finish the + negotiation and by the Target to acquiesce the end of negotiation. + + Since some key=value pairs may not fit entirely in a single PDU, the + C (continuation) bit is used (both in Login and Text) to indicate + that "more follows". + + + + + +Satran, et al. Standards Track [Page 49] + +RFC 3720 iSCSI April 2004 + + + The text negotiation uses an additional mechanism by which a target + may deliver larger amounts of data to an enquiring initiator. The + target sets a Target Task Tag to be used as a bookmark that when + returned by the initiator, means "go on". If reset to a "neutral + value", it means "forget about the rest". + + This chapter details types of keys and values used, the syntax rules + for parameter formation, and the negotiation schemes to be used with + different types of parameters. + +5.1. Text Format + + The initiator and target send a set of key=value pairs encoded in + UTF-8 Unicode. All the text keys and text values specified in this + document are to be presented and interpreted in the case in which + they appear in this document. They are case sensitive. + + The following character symbols are used in this document for text + items (the hexadecimal values represent Unicode code points): + + (a-z, A-Z) - letters + (0-9) - digits + " " (0x20) - space + "." (0x2e) - dot + "-" (0x2d) - minus + "+" (0x2b) - plus + "@" (0x40) - commercial at + "_" (0x5f) - underscore + "=" (0x3d) - equal + ":" (0x3a) - colon + "/" (0x2f) - solidus or slash + "[" (0x5b) - left bracket + "]" (0x5d) - right bracket + null (0x00) - null separator + "," (0x2c) - comma + "~" (0x7e) - tilde + + Key=value pairs may span PDU boundaries. An initiator or target that + sends partial key=value text within a PDU indicates that more text + follows by setting the C bit in the Text or Login Request or Text or + Login Response to 1. Data segments in a series of PDUs that have the + C bit set to 1 and end with a PDU that have the C bit set to 0, or + include a single PDU that has the C bit set to 0, have to be + considered as forming a single logical-text-data-segment (LTDS). + + Every key=value pair, including the last or only pair in a LTDS, MUST + be followed by one null (0x00) delimiter. + + + + +Satran, et al. Standards Track [Page 50] + +RFC 3720 iSCSI April 2004 + + + A key-name is whatever precedes the first "=" in the key=value pair. + The term key is used frequently in this document in place of + key-name. + + A value is whatever follows the first "=" in the key=value pair up to + the end of the key=value pair, but not including the null delimiter. + + The following definitions will be used in the rest of this document: + + standard-label: A string of one or more characters that consist of + letters, digits, dot, minus, plus, commercial at, or underscore. + A standard-label MUST begin with a capital letter and must not + exceed 63 characters. + + key-name: A standard-label. + + text-value: A string of zero or more characters that consist of + letters, digits, dot, minus, plus, commercial at, underscore, + slash, left bracket, right bracket, or colon. + + iSCSI-name-value: A string of one or more characters that consist + of minus, dot, colon, or any character allowed by the output of + the iSCSI string-prep template as specified in [RFC3722] (see + also Section 3.2.6.2 iSCSI Name Encoding). + + iSCSI-local-name-value: A UTF-8 string; no null characters are + allowed in the string. This encoding is to be used for localized + (internationalized) aliases. + + boolean-value: The string "Yes" or "No". + + hex-constant: A hexadecimal constant encoded as a string that + starts with "0x" or "0X" followed by one or more digits or the + letters a, b, c, d, e, f, A, B, C, D, E, or F. Hex-constants are + used to encode numerical values or binary strings. When used to + encode numerical values, the excessive use of leading 0 digits is + discouraged. The string following 0X (or 0x) represents a base16 + number that starts with the most significant base16 digit, + followed by all other digits in decreasing order of significance + and ending with the least-significant base16 digit. When used to + encode binary strings, hexadecimal constants have an implicit + byte-length that includes four bits for every hexadecimal digit + of the constant, including leading zeroes. For example, a + hex-constant of n hexadecimal digits has a byte-length of (the + integer part of) (n+1)/2. + + + + + + +Satran, et al. Standards Track [Page 51] + +RFC 3720 iSCSI April 2004 + + + decimal-constant: An unsigned decimal number with the digit 0 or a + string of one or more digits that start with a non-zero digit. + Decimal-constants are used to encode numerical values or binary + strings. Decimal constants can only be used to encode binary + strings if the string length is explicitly specified. There is + no implicit length for decimal strings. Decimal-constant MUST + NOT be used for parameter values if the values can be equal or + greater than 2**64 (numerical) or for binary strings that can be + longer than 64 bits. + + base64-constant: base64 constant encoded as a string that starts + with "0b" or "0B" followed by 1 or more digits or letters or plus + or slash or equal. The encoding is done according to [RFC2045] + and each character, except equal, represents a base64 digit or a + 6-bit binary string. Base64-constants are used to encode + numerical-values or binary strings. When used to encode + numerical values, the excessive use of leading 0 digits (encoded + as A) is discouraged. The string following 0B (or 0b) represents + a base64 number that starts with the most significant base64 + digit, followed by all other digits in decreasing order of + significance and ending with the least-significant base64 digit; + the least significant base64 digit may be optionally followed by + pad digits (encoded as equal) that are not considered as part of + the number. When used to encode binary strings, base64-constants + have an implicit + byte-length that includes six bits for every character of the + constant, excluding trailing equals (i.e., a base64-constant of n + base64 characters excluding the trailing equals has a byte-length + of ((the integer part of) (n*3/4)). Correctly encoded base64 + strings cannot have n values of 1, 5 ... k*4+1. + + numerical-value: An unsigned integer always less than 2**64 encoded + as a decimal-constant or a hex-constant. Unsigned integer + arithmetic applies to numerical-values. + + large-numerical-value: An unsigned integer that can be larger than + or equal to 2**64 encoded as a hex constant, or + base64-constant. Unsigned integer arithmetic applies to + large-numeric-values. + + numeric-range: Two numerical-values separated by a tilde where the + value to the right of tilde must not be lower than the value to + the left. + + regular-binary-value: A binary string not longer than 64 bits + encoded as a decimal constant, hex constant, or base64-constant. + The length of the string is either specified by the key + definition or is the implicit byte-length of the encoded string. + + + +Satran, et al. Standards Track [Page 52] + +RFC 3720 iSCSI April 2004 + + + large-binary-value: A binary string longer than 64 bits encoded as + a hex-constant or base64-constant. The length of the string is + either specified by the key definition or is the implicit + byte-length of the encoded string. + + binary-value: A regular-binary-value or a large-binary-value. + Operations on binary values are key specific. + + simple-value: Text-value, iSCSI-name-value, boolean-value, + numeric-value, a numeric-range, or a binary-value. + + list-of-values: A sequence of text-values separated by a comma. + + If not otherwise specified, the maximum length of a simple-value (not + its encoded representation) is 255 bytes, not including the delimiter + (comma or zero byte). + + Any iSCSI target or initiator MUST support receiving at least 8192 + bytes of key=value data in a negotiation sequence. When proposing or + accepting authentication methods that explicitly require support for + very long authentication items, the initiator and target MUST support + receiving of at least 64 kilobytes of key=value data (see Appendix + 11.1.2 - Simple Public-Key Mechanism (SPKM) - that require support + for public key certificates). + +5.2. Text Mode Negotiation + + During login, and thereafter, some session or connection parameters + are either declared or negotiated through an exchange of textual + information. + + The initiator starts the negotiation and/or declaration through a + Text or Login Request and indicates when it is ready for completion + (by setting the F bit to 1 and keeping it to 1 in a Text Request or + the T bit in the Login Request). As negotiation text may span PDU + boundaries, a Text or Login Request or Text or Login Response PDU + that has the C bit set to 1 MUST NOT have the F/T bit set to 1. + + A target receiving a Text or Login Request with the C bit set to 1 + MUST answer with a Text or Login Response with no data segment + (DataSegmentLength 0). An initiator receiving a Text or Login + Response with the C bit set to 1 MUST answer with a Text or Login + Request with no data segment (DataSegmentLength 0). + + A target or initiator SHOULD NOT use a Text or Login Response or Text + or Login Request with no data segment (DataSegmentLength 0) unless + explicitly required by a general or a key-specific negotiation rule. + + + + +Satran, et al. Standards Track [Page 53] + +RFC 3720 iSCSI April 2004 + + + The format of a declaration is: + + Declarer-> <key>=<valuex> + + The general format of text negotiation is: + + Proposer-> <key>=<valuex> + Acceptor-> <key>={<valuey>|NotUnderstood|Irrelevant|Reject} + + Thus a declaration is a one-way textual exchange while a negotiation + is a two-way exchange. + + The proposer or declarer can either be the initiator or the target, + and the acceptor can either be the target or initiator, respectively. + Targets are not limited to respond to key=value pairs as proposed by + the initiator. The target may propose key=value pairs of its own. + + All negotiations are explicit (i.e., the result MUST only be based on + newly exchanged or declared values). There are no implicit + proposals. If a proposal is not made, then a reply cannot be + expected. Conservative design also requires that default values + should not be relied upon when use of some other value has serious + consequences. + + The value proposed or declared can be a numerical-value, a + numerical-range defined by lower and upper values with both integers + separated by a tilde, a binary value, a text-value, an + iSCSI-name-value, an iSCSI-local-name-value, a boolean-value (Yes or + No), or a list of comma separated text-values. A range, a + large-numerical-value, an iSCSI-name-value and an + iSCSI-local-name-value MAY ONLY be used if it is explicitly allowed. + An accepted value can be a numerical-value, a large-numerical-value, + a text-value, or a boolean-value. + + If a specific key is not relevant for the current negotiation, the + acceptor may answer with the constant "Irrelevant" for all types of + negotiation. However the negotiation is not considered as failed if + the answer is "Irrelevant". The "Irrelevant" answer is meant for + those cases in which several keys are presented by a proposing party + but the selection made by the acceptor for one of the keys makes + other keys irrelevant. The following example illustrates the use of + "Irrelevant": + + I->T OFMarker=Yes,OFMarkInt=2048~8192 + T->I OFMarker=No,OFMarkInt=Irrelevant + + I->T X#vkey1=(bla,alb,None),X#vkey2=(bla,alb) + T->I X#vkey1=None,X#vkey2=Irrelevant + + + +Satran, et al. Standards Track [Page 54] + +RFC 3720 iSCSI April 2004 + + + + Any key not understood by the acceptor may be ignored by the acceptor + without affecting the basic function. However, the answer for a key + not understood MUST be key=NotUnderstood. + + The constants "None", "Reject", "Irrelevant", and "NotUnderstood" are + reserved and MUST ONLY be used as described here. Violation of this + rule is a protocol error (in particular the use of "Reject", + "Irrelevant", and "NotUnderstood" as proposed values). + + Reject or Irrelevant are legitimate negotiation options where allowed + but their excessive use is discouraged. A negotiation is considered + complete when the acceptor has sent the key value pair even if the + value is "Reject", "Irrelevant", or "NotUnderstood. Sending the key + again would be a re-negotiation and is forbidden for many keys. + + If the acceptor sends "Reject" as an answer the negotiated key is + left at its current value (or default if no value was set). If the + current value is not acceptable to the proposer on the connection or + to the session it is sent, the proposer MAY choose to terminate the + connection or session. + + All keys in this document, except for the X extension formats, MUST + be supported by iSCSI initiators and targets when used as specified + here. If used as specified, these keys MUST NOT be answered with + NotUnderstood. + + Implementers may introduce new keys by prefixing them with + "X-", followed by their (reversed) domain name, or with new keys + registered with IANA prefixing them with X#. For example, the entity + owning the domain example.com can issue: + + X-com.example.bar.foo.do_something=3 + + or a new registered key may be used as in: + + X#SuperCalyPhraGilistic=Yes + + Implementers MAY also introduce new values, but ONLY for new keys or + authentication methods (see Section 11 iSCSI Security Text Keys and + Authentication Methods), or digests (see Section 12.1 HeaderDigest + and DataDigest). + + Whenever parameter action or acceptance is dependent on other + parameters, the dependency rules and parameter sequence must be + specified with the parameters. + + + + + +Satran, et al. Standards Track [Page 55] + +RFC 3720 iSCSI April 2004 + + + In the Login Phase (see Section 5.3 Login Phase), every stage is a + separate negotiation. In the FullFeaturePhase, a Text Request + Response sequence is a negotiation. Negotiations MUST be handled as + atomic operations. For example, all negotiated values go into effect + after the negotiation concludes in agreement or are ignored if the + negotiation fails. + + Some parameters may be subject to integrity rules (e.g., parameter-x + must not exceed parameter-y or parameter-u not 1 implies parameter-v + be Yes). Whenever required, integrity rules are specified with the + keys. Checking for compliance with the integrity rule must only be + performed after all the parameters are available (the existent and + the newly negotiated). An iSCSI target MUST perform integrity + checking before the new parameters take effect. An initiator MAY + perform integrity checking. + + An iSCSI initiator or target MAY terminate a negotiation that does + not end within a reasonable time or number of exchanges. + +5.2.1. List negotiations + + In list negotiation, the originator sends a list of values (which may + include "None") in its order of preference. + + The responding party MUST respond with the same key and the first + value that it supports (and is allowed to use for the specific + originator) selected from the originator list. + + The constant "None" MUST always be used to indicate a missing + function. However, "None" is only a valid selection if it is + explicitly proposed. + + If an acceptor does not understand any particular value in a list, it + MUST ignore it. If an acceptor does not support, does not + understand, or is not allowed to use any of the proposed options with + a specific originator, it may use the constant "Reject" or terminate + the negotiation. The selection of a value not proposed MUST be + handled as a protocol error. + +5.2.2. Simple-value Negotiations + + For simple-value negotiations, the accepting party MUST answer with + the same key. The value it selects becomes the negotiation result. + + Proposing a value not admissible (e.g., not within the specified + bounds) MAY be answered with the constant "Reject" or the acceptor + MAY select an admissible value. + + + + +Satran, et al. Standards Track [Page 56] + +RFC 3720 iSCSI April 2004 + + + The selection by the acceptor, of a value not admissible under the + selection rules is considered a protocol error. The selection rules + are key-specific. + + For a numerical range, the value selected must be an integer within + the proposed range or "Reject" (if the range is unacceptable). + + In Boolean negotiations (i.e., those that result in keys taking the + values Yes or No), the accepting party MUST answer with the same key + and the result of the negotiation when the received value does not + determine that result by itself. The last value transmitted becomes + the negotiation result. The rules for selecting the value to answer + with are expressed as Boolean functions of the value received, and + the value that the accepting party would have selected if given a + choice. + + Specifically, the two cases in which answers are OPTIONAL are: + + - The Boolean function is "AND" and the value "No" is received. + The outcome of the negotiation is "No". + - The Boolean function is "OR" and the value "Yes" is received. + The outcome of the negotiation is "Yes". + + Responses are REQUIRED in all other cases, and the value chosen and + sent by the acceptor becomes the outcome of the negotiation. + +5.3. Login Phase + + The Login Phase establishes an iSCSI connection between an initiator + and a target; it also creates a new session or associates the + connection to an existing session. The Login Phase sets the iSCSI + protocol parameters, security parameters, and authenticates the + initiator and target to each other. + + The Login Phase is only implemented via Login Request and Responses. + The whole Login Phase is considered as a single task and has a single + Initiator Task Tag (similar to the linked SCSI commands). + + The default MaxRecvDataSegmentLength is used during Login. + + The Login Phase sequence of requests and responses proceeds as + follows: + + - Login initial request + - Login partial response (optional) + - More Login Requests and Responses (optional) + - Login Final-Response (mandatory) + + + + +Satran, et al. Standards Track [Page 57] + +RFC 3720 iSCSI April 2004 + + + The initial Login Request of any connection MUST include the + InitiatorName key=value pair. The initial Login Request of the first + connection of a session MAY also include the SessionType key=value + pair. For any connection within a session whose type is not + "Discovery", the first Login Request MUST also include the TargetName + key=value pair. + + The Login Final-response accepts or rejects the Login Request. + + The Login Phase MAY include a SecurityNegotiation stage and a + LoginOperationalNegotiation stage or both, but MUST include at least + one of them. The included stage MAY be empty except for the + mandatory names. + + The Login Requests and Responses contain a field (CSG) that indicates + the current negotiation stage (SecurityNegotiation or + LoginOperationalNegotiation). If both stages are used, the + SecurityNegotiation MUST precede the LoginOperationalNegotiation. + + Some operational parameters can be negotiated outside the login + through Text Requests and Responses. + + Security MUST be completely negotiated within the Login Phase. The + use of underlying IPsec security is specified in Chapter 8 and in + [RFC3723]. iSCSI support for security within the protocol only + consists of authentication in the Login Phase. + + In some environments, a target or an initiator is not interested in + authenticating its counterpart. It is possible to bypass + authentication through the Login Request and Response. + + The initiator and target MAY want to negotiate iSCSI authentication + parameters. Once this negotiation is completed, the channel is + considered secure. + + Most of the negotiation keys are only allowed in a specific stage. + The SecurityNegotiation keys appear in Chapter 11 and the + LoginOperationalNegotiation keys appear in Chapter 12. Only a + limited set of keys (marked as Any-Stage in Chapter 12) may be used + in any of the two stages. + + Any given Login Request or Response belongs to a specific stage; this + determines the negotiation keys allowed with the request or response. + It is considered to be a protocol error to send a key that is not + allowed in the current stage. + + + + + + +Satran, et al. Standards Track [Page 58] + +RFC 3720 iSCSI April 2004 + + + Stage transition is performed through a command exchange (request/ + response) that carries the T bit and the same CSG code. During this + exchange, the next stage is selected by the target through the "next + stage" code (NSG). The selected NSG MUST NOT exceed the value stated + by the initiator. The initiator can request a transition whenever it + is ready, but a target can only respond with a transition after one + is proposed by the initiator. + + In a negotiation sequence, the T bit settings in one pair of Login + Request-Responses have no bearing on the T bit settings of the next + pair. An initiator that has a T bit set to 1 in one pair and is + answered with a T bit setting of 0, may issue the next request with + the T bit set to 0. + + When a transition is requested by the initiator and acknowledged by + the target, both the initiator and target switch to the selected + stage. + + Targets MUST NOT submit parameters that require an additional + initiator Login Request in a Login Response with the T bit set to 1. + + Stage transitions during login (including entering and exit) are only + possible as outlined in the following table: + + +-----------------------------------------------------------+ + |From To -> | Security | Operational | FullFeature | + | | | | | | + | V | | | | + +-----------------------------------------------------------+ + | (start) | yes | yes | no | + +-----------------------------------------------------------+ + | Security | no | yes | yes | + +-----------------------------------------------------------+ + | Operational | no | no | yes | + +-----------------------------------------------------------+ + + The Login Final-Response that accepts a Login Request can only come + as a response to a Login Request with the T bit set to 1, and both + the request and response MUST indicate FullFeaturePhase as the next + phase via the NSG field. + + Neither the initiator nor the target should attempt to declare or + negotiate a parameter more than once during login except for + responses to specific keys that explicitly allow repeated key + declarations (e.g., TargetAddress). An attempt to + renegotiate/redeclare parameters not specifically allowed MUST be + detected by the initiator and target. If such an attempt is detected + + + + +Satran, et al. Standards Track [Page 59] + +RFC 3720 iSCSI April 2004 + + + by the target, the target MUST respond with Login reject (initiator + error); if detected by the initiator, the initiator MUST drop the + connection. + +5.3.1. Login Phase Start + + The Login Phase starts with a Login Request from the initiator to the + target. The initial Login Request includes: + + - Protocol version supported by the initiator. + - iSCSI Initiator Name and iSCSI Target Name + - ISID, TSIH, and connection Ids + - Negotiation stage that the initiator is ready to enter. + + A login may create a new session or it may add a connection to an + existing session. Between a given iSCSI Initiator Node (selected + only by an InitiatorName) and a given iSCSI target defined by an + iSCSI TargetName and a Target Portal Group Tag, the login results are + defined by the following table: + + + +------------------------------------------------------------------+ + |ISID | TSIH | CID | Target action | + +------------------------------------------------------------------+ + |new | non-zero | any | fail the login | + | | | | ("session does not exist") | + +------------------------------------------------------------------+ + |new | zero | any | instantiate a new session | + +------------------------------------------------------------------+ + |existing | zero | any | do session reinstatement | + | | | | (see section 5.3.5) | + +------------------------------------------------------------------+ + |existing | non-zero | new | add a new connection to | + | | existing | | the session | + +------------------------------------------------------------------+ + |existing | non-zero |existing| do connection reinstatement| + | | existing | | (see section 5.3.4) | + +------------------------------------------------------------------+ + |existing | non-zero | any | fail the login | + | | new | | ("session does not exist") | + +------------------------------------------------------------------+ + + Determination of "existing" or "new" are made by the target. + + + + + + + + +Satran, et al. Standards Track [Page 60] + +RFC 3720 iSCSI April 2004 + + + Optionally, the Login Request may include: + + - Security parameters + OR + - iSCSI operational parameters + AND/OR + - The next negotiation stage that the initiator is ready to + enter. + + The target can answer the login in the following ways: + + - Login Response with Login reject. This is an immediate rejection + from the target that causes the connection to terminate and the + session to terminate if this is the first (or only) connection of + a new session. The T bit and the CSG and NSG fields are + reserved. + - Login Response with Login Accept as a final response (T bit set + to 1 and the NSG in both request and response are set to + FullFeaturePhase). The response includes the protocol version + supported by the target and the session ID, and may include iSCSI + operational or security parameters (that depend on the current + stage). + - Login Response with Login Accept as a partial response (NSG not + set to FullFeaturePhase in both request and response) that + indicates the start of a negotiation sequence. The response + includes the protocol version supported by the target and either + security or iSCSI parameters (when no security mechanism is + chosen) supported by the target. + + If the initiator decides to forego the SecurityNegotiation stage, it + issues the Login with the CSG set to LoginOperationalNegotiation and + the target may reply with a Login Response that indicates that it is + unwilling to accept the connection (see Section 10.13 Login Response) + without SecurityNegotiation and will terminate the connection with a + response of Authentication failure (see Section 10.13.5 Status-Class + and Status-Detail). + + If the initiator is willing to negotiate iSCSI security, but is + unwilling to make the initial parameter proposal and may accept a + connection without iSCSI security, it issues the Login with the T bit + set to 1, the CSG set to SecurityNegotiation, and the NSG set to + LoginOperationalNegotiation. If the target is also ready to skip + security, the Login Response only contains the TargetPortalGroupTag + key (see Section 12.9 TargetPortalGroupTag), the T bit set to 1, the + CSG set to SecurityNegotiation, and the NSG set to + LoginOperationalNegotiation. + + + + + +Satran, et al. Standards Track [Page 61] + +RFC 3720 iSCSI April 2004 + + + An initiator that chooses to operate without iSCSI security, with all + the operational parameters taking the default values, issues the + Login with the T bit set to 1, the CSG set to + LoginOperationalNegotiation, and the NSG set to FullFeaturePhase. If + the target is also ready to forego security and can finish its + LoginOperationalNegotiation, the Login Response has T bit set to 1, + the CSG set to LoginOperationalNegotiation, and the NSG set to + FullFeaturePhase in the next stage. + + During the Login Phase the iSCSI target MUST return the + TargetPortalGroupTag key with the first Login Response PDU with which + it is allowed to do so (i.e., the first Login Response issued after + the first Login Request with the C bit set to 0) for all session + types when TargetName is given and the response is not a redirection. + The TargetPortalGroupTag key value indicates the iSCSI portal group + servicing the Login Request PDU. If the reconfiguration of iSCSI + portal groups is a concern in a given environment, the iSCSI + initiator should use this key to ascertain that it had indeed + initiated the Login Phase with the intended target portal group. + +5.3.2. iSCSI Security Negotiation + + The security exchange sets the security mechanism and authenticates + the initiator user and the target to each other. The exchange + proceeds according to the authentication method chosen in the + negotiation phase and is conducted using the Login Requests' and + responses' key=value parameters. + + An initiator directed negotiation proceeds as follows: + + - The initiator sends a Login Request with an ordered list of the + options it supports (authentication algorithm). The options are + listed in the initiator's order of preference. The initiator MAY + also send private or public extension options. + + - The target MUST reply with the first option in the list it + supports and is allowed to use for the specific initiator unless + it does not support any, in which case it MUST answer with + "Reject" (see Section 5.2 Text Mode Negotiation). The parameters + are encoded in UTF8 as key=value. For security parameters, see + Chapter 11. + + - When the initiator considers that it is ready to conclude the + SecurityNegotiation stage, it sets the T bit to 1 and the NSG to + what it would like the next stage to be. The target will then + set the T bit to 1 and set the NSG to the next stage in the Login + Response when it finishes sending its security keys. The next + + + + +Satran, et al. Standards Track [Page 62] + +RFC 3720 iSCSI April 2004 + + + stage selected will be the one the target selected. If the next + stage is FullFeaturePhase, the target MUST respond with a Login + Response with the TSIH value. + + If the security negotiation fails at the target, then the target MUST + send the appropriate Login Response PDU. If the security negotiation + fails at the initiator, the initiator SHOULD close the connection. + + It should be noted that the negotiation might also be directed by the + target if the initiator does support security, but is not ready to + direct the negotiation (propose options). + +5.3.3. Operational Parameter Negotiation During the Login Phase + + Operational parameter negotiation during the login MAY be done: + + - Starting with the first Login Request if the initiator does not + propose any security/integrity option. + + - Starting immediately after the security negotiation if the + initiator and target perform such a negotiation. + + Operational parameter negotiation MAY involve several Login + Request-Response exchanges started and terminated by the initiator. + The initiator MUST indicate its intent to terminate the negotiation + by setting the T bit to 1; the target sets the T bit to 1 on the last + response. + + If the target responds to a Login Request that has the T bit set to 1 + with a Login Response that has the T bit set to 0, the initiator + should keep sending the Login Request (even empty) with the T bit set + to 1, while it still wants to switch stage, until it receives the + Login Response that has the T bit set to 1 or it receives a key that + requires it to set the T bit to 0. + + Some session specific parameters can only be specified during the + Login Phase of the first connection of a session (i.e., begun by a + Login Request that contains a zero-valued TSIH) - the leading Login + Phase (e.g., the maximum number of connections that can be used for + this session). + + A session is operational once it has at least one connection in + FullFeaturePhase. New or replacement connections can only be added + to a session after the session is operational. + + For operational parameters, see Chapter 12. + + + + + +Satran, et al. Standards Track [Page 63] + +RFC 3720 iSCSI April 2004 + + +5.3.4. Connection Reinstatement + + Connection reinstatement is the process of an initiator logging in + with an ISID-TSIH-CID combination that is possibly active from the + target's perspective, which causes the implicit logging out of the + connection corresponding to the CID, and reinstating a new Full + Feature Phase iSCSI connection in its place (with the same CID). + Thus, the TSIH in the Login PDU MUST be non-zero and the CID does not + change during a connection reinstatement. The Login Request performs + the logout function of the old connection if an explicit logout was + not performed earlier. In sessions with a single connection, this + may imply the opening of a second connection with the sole purpose of + cleaning up the first. Targets MUST support opening a second + connection even when they do not support multiple connections in Full + Feature Phase if ErrorRecoveryLevel is 2 and SHOULD support opening a + second connection if ErrorRecoveryLevel is less than 2. + + If the operational ErrorRecoveryLevel is 2, connection reinstatement + enables future task reassignment. If the operational + ErrorRecoveryLevel is less than 2, connection reinstatement is the + replacement of the old CID without enabling task reassignment. In + this case, all the tasks that were active on the old CID must be + immediately terminated without further notice to the initiator. + + The initiator connection state MUST be CLEANUP_WAIT (section 7.1.3) + when the initiator attempts a connection reinstatement. + + In practical terms, in addition to the implicit logout of the old + connection, reinstatement is equivalent to a new connection login. + +5.3.5. Session Reinstatement, Closure, and Timeout + + Session reinstatement is the process of the initiator logging in with + an ISID that is possibly active from the target's perspective. Thus + implicitly logging out the session that corresponds to the ISID and + reinstating a new iSCSI session in its place (with the same ISID). + Therefore, the TSIH in the Login PDU MUST be zero to signal session + reinstatement. Session reinstatement causes all the tasks that were + active on the old session to be immediately terminated by the target + without further notice to the initiator. + + The initiator session state MUST be FAILED (Section 7.3 Session State + Diagrams) when the initiator attempts a session reinstatement. + + + + + + + + +Satran, et al. Standards Track [Page 64] + +RFC 3720 iSCSI April 2004 + + + Session closure is an event defined to be one of the following: + + - A successful "session close" logout. + - A successful "connection close" logout for the last Full Feature + Phase connection when no other connection in the session is + waiting for cleanup (Section 7.2 Connection Cleanup State Diagram + for Initiators and Targets) and no tasks in the session are + waiting for reassignment. + + Session timeout is an event defined to occur when the last connection + state timeout expires and no tasks are waiting for reassignment. + This takes the session to the FREE state (N6 transition in the + session state diagram). + +5.3.5.1. Loss of Nexus Notification + + The iSCSI layer provides the SCSI layer with the "I_T nexus loss" + notification when any one of the following events happens: + + a) Successful completion of session reinstatement. + b) Session closure event. + c) Session timeout event. + + Certain SCSI object clearing actions may result due to the + notification in the SCSI end nodes, as documented in Appendix F. + - Clearing Effects of Various Events on Targets -. + +5.3.6. Session Continuation and Failure + + Session continuation is the process by which the state of a + preexisting session continues to be used by connection reinstatement + (Section 5.3.4 Connection Reinstatement), or by adding a connection + with a new CID. Either of these actions associates the new transport + connection with the session state. + + Session failure is an event where the last Full Feature Phase + connection reaches the CLEANUP_WAIT state (Section 7.2 Connection + Cleanup State Diagram for Initiators and Targets), or completes a + successful recovery logout, thus causing all active tasks (that are + formerly allegiant to the connection) to start waiting for task + reassignment. + + + + + + + + + + +Satran, et al. Standards Track [Page 65] + +RFC 3720 iSCSI April 2004 + + +5.4. Operational Parameter Negotiation Outside the Login Phase + + Some operational parameters MAY be negotiated outside (after) the + Login Phase. + + Parameter negotiation in Full Feature Phase is done through Text + requests and responses. Operational parameter negotiation MAY + involve several Text request-response exchanges, which the initiator + always starts and terminates using the same Initiator Task Tag. The + initiator MUST indicate its intent to terminate the negotiation by + setting the F bit to 1; the target sets the F bit to 1 on the last + response. + + If the target responds to a Text request with the F bit set to 1 and + with a Text response with the F bit set to 0, the initiator should + keep sending the Text request (even empty) with the F bit set to 1, + while it still wants to finish the negotiation, until it receives the + Text response with the F bit set to 1. Responding to a Text request + with the F bit set to 1 with an empty (no key=value pairs) response + with the F bit set to 0 is discouraged. + + Targets MUST NOT submit parameters that require an additional + initiator Text request in a Text response with the F bit set to 1. + + In a negotiation sequence, the F bit settings in one pair of Text + request-responses have no bearing on the F bit settings of the next + pair. An initiator that has the F bit set to 1 in a request and is + being answered with an F bit setting of 0 may issue the next request + with the F bit set to 0. + + Whenever the target responds with the F bit set to 0, it MUST set the + Target Transfer Tag to a value other than the default 0xffffffff. + + An initiator MAY reset an operational parameter negotiation by + issuing a Text request with the Target Transfer Tag set to the value + 0xffffffff after receiving a response with the Target Transfer Tag + set to a value other than 0xffffffff. A target may reset an + operational parameter negotiation by answering a Text request with a + Reject PDU. + + Neither the initiator nor the target should attempt to declare or + negotiate a parameter more than once during any negotiation sequence + without an intervening operational parameter negotiation reset, + except for responses to specific keys that explicitly allow repeated + key declarations (e.g., TargetAddress). If detected by the target, + this MUST result in a Reject PDU with a reason of "protocol error". + The initiator MUST reset the negotiation as outlined above. + + + + +Satran, et al. Standards Track [Page 66] + +RFC 3720 iSCSI April 2004 + + + Parameters negotiated by a text exchange negotiation sequence only + become effective after the negotiation sequence is completed. + +6. iSCSI Error Handling and Recovery + +6.1. Overview + +6.1.1. Background + + The following two considerations prompted the design of much of the + error recovery functionality in iSCSI: + + i) An iSCSI PDU may fail the digest check and be dropped, despite + being received by the TCP layer. The iSCSI layer must + optionally be allowed to recover such dropped PDUs. + ii) A TCP connection may fail at any time during the data + transfer. All the active tasks must optionally be allowed to + continue on a different TCP connection within the same + session. + + Implementations have considerable flexibility in deciding what degree + of error recovery to support, when to use it and by which mechanisms + to achieve the required behavior. Only the externally visible + actions of the error recovery mechanisms must be standardized to + ensure interoperability. + + This chapter describes a general model for recovery in support of + interoperability. See Appendix E. - Algorithmic Presentation of + Error Recovery Classes - for further detail on how the described + model may be implemented. Compliant implementations do not have to + match the implementation details of this model as presented, but the + external behavior of such implementations must correspond to the + externally observable characteristics of the presented model. + +6.1.2. Goals + + The major design goals of the iSCSI error recovery scheme are as + follows: + + a) Allow iSCSI implementations to meet different requirements by + defining a collection of error recovery mechanisms that + implementations may choose from. + b) Ensure interoperability between any two implementations + supporting different sets of error recovery capabilities. + c) Define the error recovery mechanisms to ensure command + ordering even in the face of errors, for initiators that + demand ordering. + + + + +Satran, et al. Standards Track [Page 67] + +RFC 3720 iSCSI April 2004 + + + d) Do not make additions in the fast path, but allow moderate + complexity in the error recovery path. + e) Prevent both the initiator and target from attempting to + recover the same set of PDUs at the same time. For example, + there must be a clear "error recovery functionality + distribution" between the initiator and target. + +6.1.3. Protocol Features and State Expectations + + The initiator mechanisms defined in connection with error recovery + are: + + a) NOP-OUT to probe sequence numbers of the target (section + 10.18) + b) Command retry (section 6.2.1) + c) Recovery R2T support (section 6.7) + d) Requesting retransmission of status/data/R2T using the SNACK + facility (section 10.16) + e) Acknowledging the receipt of the data (section 10.16) + f) Reassigning the connection allegiance of a task to a different + TCP connection (section 6.2.2) + g) Terminating the entire iSCSI session to start afresh (section + 6.1.4.4) + + The target mechanisms defined in connection with error recovery are: + + a) NOP-IN to probe sequence numbers of the initiator (section + 10.19) + b) Requesting retransmission of data using the recovery R2T + feature (section 6.7) + c) SNACK support (section 10.16) d) Requesting that parts of + read data be acknowledged (section 10.7.2) + e) Allegiance reassignment support (section 6.2.2) + f) Terminating the entire iSCSI session to force the initiator to + start over (section 6.1.4.4) + + For any outstanding SCSI command, it is assumed that iSCSI, in + conjunction with SCSI at the initiator, is able to keep enough + information to be able to rebuild the command PDU, and that outgoing + data is available (in host memory) for retransmission while the + command is outstanding. It is also assumed that at the target, + incoming data (read data) MAY be kept for recovery or it can be + reread from a device server. + + It is further assumed that a target will keep the "status & sense" + for a command it has executed if it supports status retransmission. + A target that agrees to support data retransmission is expected to be + prepared to retransmit the outgoing data (i.e., Data-In) on request + + + +Satran, et al. Standards Track [Page 68] + +RFC 3720 iSCSI April 2004 + + + until either the status for the completed command is acknowledged, or + the data in question has been separately acknowledged. + +6.1.4. Recovery Classes + + iSCSI enables the following classes of recovery (in the order of + increasing scope of affected iSCSI tasks): + + - Within a command (i.e., without requiring command restart). + - Within a connection (i.e., without requiring the connection to + be rebuilt, but perhaps requiring command restart). + - Connection recovery (i.e., perhaps requiring connections to be + rebuilt and commands to be reissued). + - Session recovery. + + The recovery scenarios detailed in the rest of this section are + representative rather than exclusive. In every case, they detail the + lowest class recovery that MAY be attempted. The implementer is left + to decide under which circumstances to escalate to the next recovery + class and/or what recovery classes to implement. Both the iSCSI + target and initiator MAY escalate the error handling to an error + recovery class, which impacts a larger number of iSCSI tasks in any + of the cases identified in the following discussion. + + In all classes, the implementer has the choice of deferring errors to + the SCSI initiator (with an appropriate response code), in which case + the task, if any, has to be removed from the target and all the side + effects, such as ACA, must be considered. + + Use of within-connection and within-command recovery classes MUST NOT + be attempted before the connection is in Full Feature Phase. + + In the detailed description of the recovery classes, the mandating + terms (MUST, SHOULD, MAY, etc.) indicate normative actions to be + executed if the recovery class is supported and used. + +6.1.4.1. Recovery Within-command + + At the target, the following cases lend themselves to + within-command recovery: + + - Lost data PDU - realized through one of the following: + + a) Data digest error - dealt with as specified in Section 6.7 + Digest Errors, using the option of a recovery R2T. + + + + + + +Satran, et al. Standards Track [Page 69] + +RFC 3720 iSCSI April 2004 + + + b) Sequence reception timeout (no data or + partial-data-and-no-F-bit) - considered an implicit sequence + error and dealt with as specified in Section 6.8 Sequence + Errors, using the option of a recovery R2T. + c) Header digest error, which manifests as a sequence reception + timeout or a sequence error - dealt with as specified in + Section 6.8 Sequence Errors, using the option of a recovery + R2T. + + At the initiator, the following cases lend themselves to + within-command recovery: + + Lost data PDU or lost R2T - realized through one of the + following: + + a) Data digest error - dealt with as specified in Section 6.7 + Digest Errors, using the option of a SNACK. + b) Sequence reception timeout (no status) or response reception + timeout - dealt with as specified in Section 6.8 Sequence + Errors, using the option of a SNACK. + c) Header digest error, which manifests as a sequence reception + timeout or a sequence error - dealt with as specified in + Section 6.8 Sequence Errors, using the option of a SNACK. + + To avoid a race with the target, which may already have a recovery + R2T or a termination response on its way, an initiator SHOULD NOT + originate a SNACK for an R2T based on its internal timeouts (if any). + Recovery in this case is better left to the target. + + The timeout values used by the initiator and target are outside the + scope of this document. Sequence reception timeout is generally a + large enough value to allow the data sequence transfer to be + complete. + +6.1.4.2. Recovery Within-connection + + At the initiator, the following cases lend themselves to + within-connection recovery: + + - Requests not acknowledged for a long time. Requests are + acknowledged explicitly through ExpCmdSN or implicitly by + receiving data and/or status. The initiator MAY retry + non-acknowledged commands as specified in Section 6.2 Retry and + Reassign in Recovery. + + + + + + + +Satran, et al. Standards Track [Page 70] + +RFC 3720 iSCSI April 2004 + + + - Lost iSCSI numbered Response. It is recognized by either + identifying a data digest error on a Response PDU or a Data-In + PDU carrying the status, or by receiving a Response PDU with a + higher StatSN than expected. In the first case, digest error + handling is done as specified in Section 6.7 Digest Errors using + the option of a SNACK. In the second case, sequence error + handling is done as specified in Section 6.8 Sequence Errors, + using the option of a SNACK. + + At the target, the following cases lend themselves to + within-connection recovery: + + - Status/Response not acknowledged for a long time. The target MAY + issue a NOP-IN (with a valid Target Transfer Tag or otherwise) + that carries the next status sequence number it is going to use + in the StatSN field. This helps the initiator detect any missing + StatSN(s) and issue a SNACK for the status. + + The timeout values used by the initiator and the target are outside + the scope of this document. + +6.1.4.3. Connection Recovery + + At an iSCSI initiator, the following cases lend themselves to + connection recovery: + + - TCP connection failure: The initiator MUST close the connection. + It then MUST either implicitly or explicitly logout the failed + connection with the reason code "remove the connection for + recovery" and reassign connection allegiance for all commands + still in progress associated with the failed connection on one or + more connections (some or all of which MAY be newly established + connections) using the "Task reassign" task management function + (see Section 10.5.1 Function). For an initiator, a command is in + progress as long as it has not received a response or a Data-In + PDU including status. + + Note: The logout function is mandatory. However, a new connection + establishment is only mandatory if the failed connection was the + last or only connection in the session. + + - Receiving an Asynchronous Message that indicates one or all + connections in a session has been dropped. The initiator MUST + handle it as a TCP connection failure for the connection(s) + referred to in the Message. + + + + + + +Satran, et al. Standards Track [Page 71] + +RFC 3720 iSCSI April 2004 + + + At an iSCSI target, the following cases lend themselves to connection + recovery: + + - TCP connection failure. The target MUST close the connection and, + if more than one connection is available, the target SHOULD send + an Asynchronous Message that indicates it has dropped the + connection. Then, the target will wait for the initiator to + continue recovery. + +6.1.4.4. Session Recovery + + Session recovery should be performed when all other recovery attempts + have failed. Very simple initiators and targets MAY perform session + recovery on all iSCSI errors and rely on recovery on the SCSI layer + and above. + + Session recovery implies the closing of all TCP connections, + internally aborting all executing and queued tasks for the given + initiator at the target, terminating all outstanding SCSI commands + with an appropriate SCSI service response at the initiator, and + restarting a session on a new set of connection(s) (TCP connection + establishment and login on all new connections). + + For possible clearing effects of session recovery on SCSI and iSCSI + objects, refer to Appendix F. - Clearing Effects of Various Events on + Targets -. + +6.1.5. Error Recovery Hierarchy + + The error recovery classes described so far are organized into a + hierarchy for ease in understanding and to limit the implementation + complexity. With few and well defined recovery levels + interoperability is easier to achieve. The attributes of this + hierarchy are as follows: + + a) Each level is a superset of the capabilities of the previous + level. For example, Level 1 support implies supporting all + capabilities of Level 0 and more. + b) As a corollary, supporting a higher error recovery level means + increased sophistication and possibly an increase in resource + requirements. + c) Supporting error recovery level "n" is advertised and + negotiated by each iSCSI entity by exchanging the text key + "ErrorRecoveryLevel=n". The lower of the two exchanged values + is the operational ErrorRecoveryLevel for the session. + + + + + + +Satran, et al. Standards Track [Page 72] + +RFC 3720 iSCSI April 2004 + + + The following diagram represents the error recovery hierarchy. + + + + / + / 2 \ <-- Connection recovery + +-----+ + / 1 \ <-- Digest failure recovery + +---------+ + / 0 \ <-- Session failure recovery + +-------------+ + + The following table lists the error recovery capabilities expected + from the implementations that support each error recovery level. + + +-------------------+--------------------------------------------+ + |ErrorRecoveryLevel | Associated Error recovery capabilities | + +-------------------+--------------------------------------------+ + | 0 | Session recovery class | + | | (Section 6.1.4.4 Session Recovery) | + +-------------------+--------------------------------------------+ + | 1 | Digest failure recovery (See Note below.) | + | | plus the capabilities of ER Level 0 | + +-------------------+--------------------------------------------+ + | 2 | Connection recovery class | + | | (Section 6.1.4.3 Connection Recovery) | + | | plus the capabilities of ER Level 1 | + +-------------------+--------------------------------------------+ + + Note: Digest failure recovery is comprised of two recovery classes: + Within-Connection recovery class (Section 6.1.4.2 Recovery Within- + connection) and Within-Command recovery class (Section 6.1.4.1 + Recovery Within-command). + + When a defined value of ErrorRecoveryLevel is proposed by an + originator in a text negotiation, the originator MUST support the + functionality defined for the proposed value and additionally, the + functionality corresponding to any defined value numerically less + than the proposed. When a defined value of ErrorRecoveryLevel is + returned by a responder in a text negotiation, the responder MUST + support the functionality corresponding to the ErrorRecoveryLevel it + is accepting. + + When either party attempts to use error recovery functionality beyond + what is negotiated, the recovery attempts MAY fail unless an a priori + agreement outside the scope of this document exists between the two + parties to provide such support. + + + + + +Satran, et al. Standards Track [Page 73] + +RFC 3720 iSCSI April 2004 + + + Implementations MUST support error recovery level "0", while the rest + are OPTIONAL to implement. In implementation terms, the above + striation means that the following incremental sophistication with + each level is required. + + +-------------------+---------------------------------------------+ + |Level transition | Incremental requirement | + +-------------------+---------------------------------------------+ + | 0->1 | PDU retransmissions on the same connection | + +-------------------+---------------------------------------------+ + | 1->2 | Retransmission across connections and | + | | allegiance reassignment | + +-------------------+---------------------------------------------+ + +6.2. Retry and Reassign in Recovery + + This section summarizes two important and somewhat related iSCSI + protocol features used in error recovery. + +6.2.1. Usage of Retry + + By resending the same iSCSI command PDU ("retry") in the absence of a + command acknowledgement (by way of an ExpCmdSN update) or a response, + an initiator attempts to "plug" (what it thinks are) the + discontinuities in CmdSN ordering on the target end. Discarded + command PDUs, due to digest errors, may have created these + discontinuities. + + Retry MUST NOT be used for reasons other than plugging command + sequence gaps, and in particular, cannot be used for requesting PDU + retransmissions from a target. Any such PDU retransmission requests + for a currently allegiant command in progress may be made using the + SNACK mechanism described in section 10.16, although the usage of + SNACK is OPTIONAL. + + If initiators, as part of plugging command sequence gaps as described + above, inadvertently issue retries for allegiant commands already in + progress (i.e., targets did not see the discontinuities in CmdSN + ordering), the duplicate commands are silently ignored by targets as + specified in section 3.2.2.1. + + When an iSCSI command is retried, the command PDU MUST carry the + original Initiator Task Tag and the original operational attributes + (e.g., flags, function names, LUN, CDB etc.) as well as the original + CmdSN. The command being retried MUST be sent on the same connection + as the original command unless the original connection was already + successfully logged out. + + + + +Satran, et al. Standards Track [Page 74] + +RFC 3720 iSCSI April 2004 + + +6.2.2. Allegiance Reassignment + + By issuing a "task reassign" task management request (Section 10.5.1 + Function), the initiator signals its intent to continue an already + active command (but with no current connection allegiance) as part of + connection recovery. This means that a new connection allegiance is + requested for the command, which seeks to associate it to the + connection on which the task management request is being issued. + Before the allegiance reassignment is attempted for a task, an + implicit or explicit Logout with the reason code "remove the + connection for recovery" ( see section 10.14) MUST be successfully + completed for the previous connection to which the task was + allegiant. + + In reassigning connection allegiance for a command, the targets + SHOULD continue the command from its current state. For example, + when reassigning read commands, the target SHOULD take advantage of + the ExpDataSN field provided by the Task Management function request + (which must be set to zero if there was no data transfer) and bring + the read command to completion by sending the remaining data and + sending (or resending) the status. ExpDataSN acknowledges all data + sent up to, but not including, the Data-In PDU and or R2T with DataSN + (or R2TSN) equal to ExpDataSN. However, targets may choose to + send/receive all unacknowledged data or all of the data on a + reassignment of connection allegiance if unable to recover or + maintain an accurate state. Initiators MUST not subsequently request + data retransmission through Data SNACK for PDUs numbered less than + ExpDataSN (i.e., prior to the acknowledged sequence number). For all + types of commands, a reassignment request implies that the task is + still considered in progress by the initiator and the target must + conclude the task appropriately if the target returns the "Function + Complete" response to the reassignment request. This might possibly + involve retransmission of data/R2T/status PDUs as necessary, but MUST + involve the (re)transmission of the status PDU. + + It is OPTIONAL for targets to support the allegiance reassignment. + This capability is negotiated via the ErrorRecoveryLevel text key + during the login time. When a target does not support allegiance + reassignment, it MUST respond with a Task Management response code of + "Allegiance reassignment not supported". If allegiance reassignment + is supported by the target, but the task is still allegiant to a + different connection, or a successful recovery Logout of the + previously allegiant connection was not performed, the target MUST + respond with a Task Management response code of "Task still + allegiant". + + + + + + +Satran, et al. Standards Track [Page 75] + +RFC 3720 iSCSI April 2004 + + + If allegiance reassignment is supported by the target, the Task + Management response to the reassignment request MUST be issued before + the reassignment becomes effective. + + If a SCSI Command that involves data input is reassigned, any SNACK + Tag it holds for a final response from the original connection is + deleted and the default value of 0 MUST be used instead. + +6.3. Usage Of Reject PDU in Recovery + + Targets MUST NOT implicitly terminate an active task by sending a + Reject PDU for any PDU exchanged during the life of the task. If the + target decides to terminate the task, a Response PDU (SCSI, Text, + Task, etc.) must be returned by the target to conclude the task. If + the task had never been active before the Reject (i.e., the Reject is + on the command PDU), targets should not send any further responses + because the command itself is being discarded. + + The above rule means that the initiator can eventually expect a + response on receiving Rejects, if the received Reject is for a PDU + other than the command PDU itself. The non-command Rejects only have + diagnostic value in logging the errors, and they can be used for + retransmission decisions by the initiators. + + The CmdSN of the rejected command PDU (if it is a non-immediate + command) MUST NOT be considered received by the target (i.e., a + command sequence gap must be assumed for the CmdSN), even though the + CmdSN of the rejected command PDU may be reliably ascertained. Upon + receiving the Reject, the initiator MUST plug the CmdSN gap in order + to continue to use the session. The gap may be plugged either by + transmitting a command PDU with the same CmdSN, or by aborting the + task (see section 6.9 on how an abort may plug a CmdSN gap). + + When a data PDU is rejected and its DataSN can be ascertained, a + target MUST advance ExpDataSN for the current data burst if a + recovery R2T is being generated. The target MAY advance its + ExpDataSN if it does not attempt to recover the lost data PDU. + +6.4. Connection Timeout Management + + iSCSI defines two session-global timeout values (in seconds) + - Time2Wait and Time2Retain - that are applicable when an iSCSI Full + Feature Phase connection is taken out of service either intentionally + or by an exception. Time2Wait is the initial "respite time" before + attempting an explicit/implicit Logout for the CID in question or + task reassignment for the affected tasks (if any). Time2Retain is + the maximum time after the initial respite interval that the task + and/or connection state(s) is/are guaranteed to be maintained on the + + + +Satran, et al. Standards Track [Page 76] + +RFC 3720 iSCSI April 2004 + + + target to cater to a possible recovery attempt. Recovery attempts + for the connection and/or task(s) SHOULD NOT be made before Time2Wait + seconds, but MUST be completed within Time2Retain seconds after that + initial Time2Wait waiting period. + +6.4.1. Timeouts on Transport Exception Events + + A transport connection shutdown or a transport reset without any + preceding iSCSI protocol interactions informing the end-points of the + fact causes a Full Feature Phase iSCSI connection to be abruptly + terminated. The timeout values to be used in this case are the + negotiated values of defaultTime2Wait (Section 12.15 + DefaultTime2Wait) and DefaultTime2Retain (Section 12.16 + DefaultTime2Retain) text keys for the session. + +6.4.2. Timeouts on Planned Decommissioning + + Any planned decommissioning of a Full Feature Phase iSCSI connection + is preceded by either a Logout Response PDU, or an Async Message PDU. + The Time2Wait and Time2Retain field values (section 10.15) in a + Logout Response PDU, and the Parameter2 and Parameter3 fields of an + Async Message (AsyncEvent types "drop the connection" or "drop all + the connections"; section 10.9.1) specify the timeout values to be + used in each of these cases. + + These timeout values are only applicable for the affected connection, + and the tasks active on that connection. These timeout values have + no bearing on initiator timers (if any) that are already running on + connections or tasks associated with that session. + +6.5. Implicit Termination of Tasks + + A target implicitly terminates the active tasks due to iSCSI protocol + dynamics in the following cases: + + a) When a connection is implicitly or explicitly logged out with + the reason code of "Close the connection" and there are active + tasks allegiant to that connection. + + b) When a connection fails and the connection state eventually + times out (state transition M1 in Section 7.2.2 State + Transition Descriptions for Initiators and Targets) and there + are active tasks allegiant to that connection. + + c) When a successful Logout with the reason code of "remove the + connection for recovery" is performed while there are active + tasks allegiant to that connection, and those tasks eventually + + + + +Satran, et al. Standards Track [Page 77] + +RFC 3720 iSCSI April 2004 + + + time out after the Time2Wait and Time2Retain periods without + allegiance reassignment. + + d) When a connection is implicitly or explicitly logged out with + the reason code of "Close the session" and there are active + tasks in that session. + + If the tasks terminated in the above cases a), b, c) and d)are SCSI + tasks, they must be internally terminated as if with CHECK CONDITION + status. This status is only meaningful for appropriately handling + the internal SCSI state and SCSI side effects with respect to + ordering because this status is never communicated back as a + terminating status to the initiator. However additional actions may + have to be taken at SCSI level depending on the SCSI context as + defined by the SCSI standards (e.g., queued commands and ACA, in + cases a), b), and c), after the tasks are terminated, the target MUST + report a Unit Attention condition on the next command processed on + any connection for each affected I_T_L nexus with the status of CHECK + CONDITION, and the ASC/ASCQ value of 47h/7Fh - "SOME COMMANDS CLEARED + BY ISCSI PROTOCOL EVENT" , etc. - see [SAM2] and [SPC3]). + +6.6. Format Errors + + The following two explicit violations of PDU layout rules are format + errors: + + a) Illegal contents of any PDU header field except the Opcode + (legal values are specified in Section 10 iSCSI PDU Formats). + b) Inconsistent field contents (consistent field contents are + specified in Section 10 iSCSI PDU Formats). + + Format errors indicate a major implementation flaw in one of the + parties. + + When a target or an initiator receives an iSCSI PDU with a format + error, it MUST immediately terminate all transport connections in the + session either with a connection close or with a connection reset and + escalate the format error to session recovery (see Section 6.1.4.4 + Session Recovery). + +6.7. Digest Errors + + The discussion of the legal choices in handling digest errors below + excludes session recovery as an explicit option, but either party + detecting a digest error may choose to escalate the error to session + recovery. + + + + + +Satran, et al. Standards Track [Page 78] + +RFC 3720 iSCSI April 2004 + + + When a target or an initiator receives any iSCSI PDU, with a header + digest error, it MUST either discard the header and all data up to + the beginning of a later PDU or close the connection. Because the + digest error indicates that the length field of the header may have + been corrupted, the location of the beginning of a later PDU needs to + be reliably ascertained by other means such as the operation of a + sync and steering layer. + + When a target receives any iSCSI PDU with a payload digest error, it + MUST answer with a Reject PDU with a reason code of + Data-Digest-Error and discard the PDU. + + - If the discarded PDU is a solicited or unsolicited iSCSI data + PDU (for immediate data in a command PDU, non-data PDU rule + below applies), the target MUST do one of the following: + a) Request retransmission with a recovery R2T. + b) Terminate the task with a response PDU with a CHECK + CONDITION Status and an iSCSI Condition of "protocol service + CRC error" (Section 10.4.7.2 Sense Data). If the target + chooses to implement this option, it MUST wait to receive + all the data (signaled by a Data PDU with the final bit set + for all outstanding R2Ts) before sending the response PDU. + A task management command (such as an abort task) from the + initiator during this wait may also conclude the task. + - No further action is necessary for targets if the discarded PDU + is a non-data PDU. In case of immediate data being present on + a discarded command, the immediate data is implicitly recovered + when the task is retried (see section 6.2.1), followed by the + entire data transfer for the task. + + When an initiator receives any iSCSI PDU with a payload digest error, + it MUST discard the PDU. + + - If the discarded PDU is an iSCSI data PDU, the initiator MUST do + one of the following: + + a) Request the desired data PDU through SNACK. In response to the + SNACK, the target MUST either resend the data PDU or reject the + SNACK with a Reject PDU with a reason code of "SNACK reject" in + which case: + i) If the status has not already been sent for the command, + the target MUST terminate the command with a CHECK + CONDITION Status and an iSCSI Condition of "SNACK rejected" + (Section 10.4.7.2 Sense Data). + ii) If the status was already sent, no further action is + necessary for the target. The initiator in this case MUST + wait for the status to be received and then discard it, so + as to internally signal the completion with CHECK CONDITION + + + +Satran, et al. Standards Track [Page 79] + +RFC 3720 iSCSI April 2004 + + + Status and an iSCSI Condition of "protocol service CRC + error" (Section 10.4.7.2 Sense Data). + b) Abort the task and terminate the command with an error. + + - If the discarded PDU is a response PDU, the initiator MUST do one + of the following: + + a) Request PDU retransmission with a status SNACK. + b) Logout the connection for recovery and continue the tasks on a + different connection instance as described in Section 6.2 Retry + and Reassign in Recovery. + c) Logout to close the connection (abort all the commands + associated with the connection). + + - No further action is necessary for initiators if the discarded PDU + is an unsolicited PDU (e.g., Async, Reject). Task timeouts as in + the initiator waiting for a command completion, or process + timeouts, as in the target waiting for a Logout, will ensure that + the correct operational behavior will result in these cases + despite the discarded PDU. + +6.8. Sequence Errors + + When an initiator receives an iSCSI R2T/data PDU with an out of order + R2TSN/DataSN or a SCSI response PDU with an ExpDataSN that implies + missing data PDU(s), it means that the initiator must have detected a + header or payload digest error on one or more earlier R2T/data PDUs. + The initiator MUST address these implied digest errors as described + in Section 6.7 Digest Errors. When a target receives a data PDU with + an out of order DataSN, it means that the target must have hit a + header or payload digest error on at least one of the earlier data + PDUs. The target MUST address these implied digest errors as + described in Section 6.7 Digest Errors. + + When an initiator receives an iSCSI status PDU with an out of order + StatSN that implies missing responses, it MUST address the one or + more missing status PDUs as described in Section 6.7 Digest Errors. + As a side effect of receiving the missing responses, the initiator + may discover missing data PDUs. If the initiator wants to recover + the missing data for a command, it MUST NOT acknowledge the received + responses that start from the StatSN of the relevant command, until + it has completed receiving all the data PDUs of the command. + + When an initiator receives duplicate R2TSNs (due to proactive + retransmission of R2Ts by the target) or duplicate DataSNs (due to + proactive SNACKs by the initiator), it MUST discard the duplicates. + + + + + +Satran, et al. Standards Track [Page 80] + +RFC 3720 iSCSI April 2004 + + +6.9. SCSI Timeouts + + An iSCSI initiator MAY attempt to plug a command sequence gap on the + target end (in the absence of an acknowledgement of the command by + way of ExpCmdSN) before the ULP timeout by retrying the + unacknowledged command, as described in Section 6.2 Retry and + Reassign in Recovery. + + On a ULP timeout for a command (that carried a CmdSN of n), if the + iSCSI initiator intends to continue the session, it MUST abort the + command by either using an appropriate Task Management function + request for the specific command, or a "close the connection" Logout. + When using an ABORT TASK, if the ExpCmdSN is still less than (n+1), + the target may see the abort request while missing the original + command itself due to one of the following reasons: + + - Original command was dropped due to digest error. + - Connection on which the original command was sent was + successfully logged out. Upon logout, the unacknowledged + commands issued on the connection being logged out are + discarded. + + If the abort request is received and the original command is missing, + targets MUST consider the original command with that RefCmdSN to be + received and issue a Task Management response with the response code: + "Function Complete". This response concludes the task on both ends. + If the abort request is received and the target can determine (based + on the Referenced Task Tag) that the command was received and + executed and also that the response was sent prior to the abort, then + the target MUST respond with the response code of "Task Does Not + Exist". + +6.10. Negotiation Failures + + Text request and response sequences, when used to set/negotiate + operational parameters, constitute the negotiation/parameter setting. + A negotiation failure is considered to be one or more of the + following: + + - None of the choices, or the stated value, is acceptable to one + of the sides in the negotiation. + - The text request timed out and possibly terminated. + - The text request was answered with a Reject PDU. + + + + + + + + +Satran, et al. Standards Track [Page 81] + +RFC 3720 iSCSI April 2004 + + + The following two rules should be used to address negotiation + failures: + + - During Login, any failure in negotiation MUST be considered a + login process failure and the Login Phase must be terminated, + and with it, the connection. If the target detects the + failure, it must terminate the login with the appropriate Login + Response code. + + - A failure in negotiation, while in the Full Feature Phase, will + terminate the entire negotiation sequence that may consist of a + series of text requests that use the same Initiator Task Tag. + The operational parameters of the session or the connection + MUST continue to be the values agreed upon during an earlier + successful negotiation (i.e., any partial results of this + unsuccessful negotiation MUST NOT take effect and MUST be + discarded). + +6.11. Protocol Errors + + Mapping framed messages over a "stream" connection, such as TCP, + makes the proposed mechanisms vulnerable to simple software framing + errors. On the other hand, the introduction of framing mechanisms to + limit the effects of these errors may be onerous on performance for + simple implementations. Command Sequence Numbers and the above + mechanisms for connection drop and reestablishment help handle this + type of mapping errors. + + All violations of iSCSI PDU exchange sequences specified in this + document are also protocol errors. This category of errors can only + be addressed by fixing the implementations; iSCSI defines Reject and + response codes to enable this. + +6.12. Connection Failures + + iSCSI can keep a session in operation if it is able to + keep/establish at least one TCP connection between the initiator and + the target in a timely fashion. Targets and/or initiators may + recognize a failing connection by either transport level means (TCP), + a gap in the command sequence number, a response stream that is not + filled for a long time, or by a failing iSCSI NOP (acting as a ping). + The latter MAY be used periodically to increase the speed and + likelihood of detecting connection failures. Initiators and targets + MAY also use the keep-alive option on the TCP connection to enable + early link failure detection on otherwise idle links. + + + + + + +Satran, et al. Standards Track [Page 82] + +RFC 3720 iSCSI April 2004 + + + On connection failure, the initiator and target MUST do one of the + following: + + - Attempt connection recovery within the session (Section 6.1.4.3 + Connection Recovery). + + - Logout the connection with the reason code "closes the + connection" (Section 10.14.5 Implicit termination of tasks), + re-issue missing commands, and implicitly terminate all active + commands. This option requires support for the + within-connection recovery class (Section 6.1.4.2 Recovery + Within-connection). + + - Perform session recovery (Section 6.1.4.4 Session Recovery). + + Either side may choose to escalate to session recovery (via the + initiator dropping all the connections, or via an Async Message that + announces the similar intent from a target), and the other side MUST + give it precedence. On a connection failure, a target MUST terminate + and/or discard all of the active immediate commands regardless of + which of the above options is used (i.e., immediate commands are not + recoverable across connection failures). + +6.13. Session Errors + + If all of the connections of a session fail and cannot be + reestablished in a short time, or if initiators detect protocol + errors repeatedly, an initiator may choose to terminate a session and + establish a new session. + + In this case, the initiator takes the following actions: + + - Resets or closes all the transport connections. + - Terminates all outstanding requests with an appropriate + response before initiating a new session. If the same I_T + nexus is intended to be reestablished, the initiator MUST + employ session reinstatement (see section 5.3.5). + + When the session timeout (the connection state timeout for the last + failed connection) happens on the target, it takes the following + actions: + + - Resets or closes the TCP connections (closes the session). + - Terminates all active tasks that were allegiant to the + connection(s) that constituted the session. + + A target MUST also be prepared to handle a session reinstatement + request from the initiator, that may be addressing session errors. + + + +Satran, et al. Standards Track [Page 83] + +RFC 3720 iSCSI April 2004 + + +7. State Transitions + + iSCSI connections and iSCSI sessions go through several well-defined + states from the time they are created to the time they are cleared. + + The connection state transitions are described in two separate but + dependent state diagrams for ease in understanding. The first + diagram, "standard connection state diagram", describes the + connection state transitions when the iSCSI connection is not waiting + for, or undergoing, a cleanup by way of an explicit or implicit + Logout. The second diagram, "connection cleanup state diagram", + describes the connection state transitions while performing the iSCSI + connection cleanup. + + The "session state diagram" describes the state transitions an iSCSI + session would go through during its lifetime, and it depends on the + states of possibly multiple iSCSI connections that participate in the + session. + + States and state transitions are described in the text, tables and + diagrams. The diagrams are used for illustration. The text and the + tables are the governing specification. + +7.1. Standard Connection State Diagrams + +7.1.1. State Descriptions for Initiators and Targets + + State descriptions for the standard connection state diagram are as + follows: + + -S1: FREE + -initiator: State on instantiation, or after successful + connection closure. + -target: State on instantiation, or after successful connection + closure. + -S2: XPT_WAIT + -initiator: Waiting for a response to its transport connection + establishment request. + -target: Illegal + -S3: XPT_UP + -initiator: Illegal + -target: Waiting for the Login process to commence. + -S4: IN_LOGIN + -initiator: Waiting for the Login process to conclude, possibly + involving several PDU exchanges. + -target: Waiting for the Login process to conclude, possibly + involving several PDU exchanges. + + + + +Satran, et al. Standards Track [Page 84] + +RFC 3720 iSCSI April 2004 + + + -S5: LOGGED_IN + -initiator: In Full Feature Phase, waiting for all internal, + iSCSI, and transport events. + -target: In Full Feature Phase, waiting for all internal, iSCSI, + and transport events. + -S6: IN_LOGOUT + -initiator: Waiting for a Logout response. + -target: Waiting for an internal event signaling completion of + logout processing. + -S7: LOGOUT_REQUESTED + -initiator: Waiting for an internal event signaling readiness to + proceed with Logout. + -target: Waiting for the Logout process to start after having + requested a Logout via an Async Message. + -S8: CLEANUP_WAIT + -initiator: Waiting for the context and/or resources to initiate + the cleanup processing for this CSM. + -target: Waiting for the cleanup process to start for this CSM. + +7.1.2. State Transition Descriptions for Initiators and Targets + + -T1: + -initiator: Transport connect request was made (e.g., TCP SYN + sent). + -target: Illegal + -T2: + -initiator: Transport connection request timed out, a transport + reset was received, or an internal event of receiving a + Logout response (success) on another connection for a + "close the session" Logout request was received. + -target:Illegal + -T3: + -initiator: Illegal + -target: Received a valid transport connection request that + establishes the transport connection. + -T4: + -initiator: Transport connection established, thus prompting the + initiator to start the iSCSI Login. + -target: Initial iSCSI Login Request was received. + -T5: + -initiator: The final iSCSI Login Response with a Status-Class + of zero was received. + -target: The final iSCSI Login Request to conclude the Login + Phase was received, thus prompting the target to send the + final iSCSI Login Response with a Status-Class of zero. + + + + + + +Satran, et al. Standards Track [Page 85] + +RFC 3720 iSCSI April 2004 + + + -T6: + -initiator: Illegal + -target: Timed out waiting for an iSCSI Login, transport + disconnect indication was received, transport reset was + received, or an internal event indicating a transport + timeout was received. In all these cases, the connection is + to be closed. + -T7: + -initiator - one of the following events caused the transition: + - The final iSCSI Login Response was received with a + non-zero Status-Class. + - Login timed out. + - A transport disconnect indication was received. + - A transport reset was received. + - An internal event was received indicating a transport + timeout. + - An internal event of receiving a Logout response (success) + on another connection for a "close the session" Logout + request was received. + + In all these cases, the transport connection is closed. + + -target - one of the following events caused the transition: + - The final iSCSI Login Request to conclude the Login Phase + was received, prompting the target to send the final iSCSI + Login Response with a non-zero Status-Class. + - Login timed out. + - Transport disconnect indication was received. + - Transport reset was received. + - An internal event indicating a transport timeout was + received. + - On another connection a "close the session" Logout request + was received. + In all these cases, the connection is to be closed. + -T8: + -initiator: An internal event of receiving a Logout response + (success) on another connection for a "close the session" + Logout request was received, thus closing this connection + requiring no further cleanup. + -target: An internal event of sending a Logout response + (success) on another connection for a "close the session" + Logout request was received, or an internal event of a + successful connection/session reinstatement is received, + thus prompting the target to close this connection cleanly. + + + + + + + +Satran, et al. Standards Track [Page 86] + +RFC 3720 iSCSI April 2004 + + + -T9, T10: + -initiator: An internal event that indicates the readiness to + start the Logout process was received, thus prompting an + iSCSI Logout to be sent by the initiator. + -target: An iSCSI Logout request was received. + -T11, T12: + -initiator: Async PDU with AsyncEvent "Request Logout" was + received. + -target: An internal event that requires the decommissioning of + the connection is received, thus causing an Async PDU with + an AsyncEvent "Request Logout" to be sent. + -T13: + -initiator: An iSCSI Logout response (success) was received, or + an internal event of receiving a Logout response (success) + on another connection for a "close the session" Logout + request was received. + -target: An internal event was received that indicates + successful processing of the Logout, which prompts an iSCSI + Logout response (success) to be sent; an internal event of + sending a Logout response (success) on another connection + for a "close the session" Logout request was received; or an + internal event of a successful connection/session + reinstatement is received. In all these cases, the + transport connection is closed. + + -T14: + -initiator: Async PDU with AsyncEvent "Request Logout" was + received again. + -target: Illegal + -T15, T16: + -initiator: One or more of the following events caused this + transition: + -Internal event that indicates a transport connection + timeout was received thus prompting transport RESET or + transport connection closure. + -A transport RESET. + -A transport disconnect indication. + -Async PDU with AsyncEvent "Drop connection" (for this CID). + -Async PDU with AsyncEvent "Drop all connections". + -target: One or more of the following events caused this + transition: + -Internal event that indicates a transport connection + timeout was received, thus prompting transport RESET or + transport connection closure. + -An internal event of a failed connection/session + reinstatement is received. + -A transport RESET. + -A transport disconnect indication. + + + +Satran, et al. Standards Track [Page 87] + +RFC 3720 iSCSI April 2004 + + + -Internal emergency cleanup event was received which prompts + an Async PDU with AsyncEvent "Drop connection" (for this + CID), or event "Drop all connections". + -T17: + -initiator: One or more of the following events caused this + transition: + -Logout response, (failure i.e., a non-zero status) was + received, or Logout timed out. + -Any of the events specified for T15 and T16. + -target: One or more of the following events caused this + transition: + -Internal event that indicates a failure of the Logout + processing was received, which prompts a Logout response + (failure, i.e., a non-zero status) to be sent. + -Any of the events specified for T15 and T16. + -T18: + -initiator: An internal event of receiving a Logout response + (success) on another connection for a "close the session" + Logout request was received. + -target: An internal event of sending a Logout response + (success) on another connection for a "close the session" + Logout request was received, or an internal event of a + successful connection/session reinstatement is received. In + both these cases, the connection is closed. + + The CLEANUP_WAIT state (S8) implies that there are possible iSCSI + tasks that have not reached conclusion and are still considered busy. + +7.1.3. Standard Connection State Diagram for an Initiator + + Symbolic names for States: + + S1: FREE + S2: XPT_WAIT + S4: IN_LOGIN + S5: LOGGED_IN + S6: IN_LOGOUT + S7: LOGOUT_REQUESTED + S8: CLEANUP_WAIT + + + + + + + + + + + + +Satran, et al. Standards Track [Page 88] + +RFC 3720 iSCSI April 2004 + + + States S5, S6, and S7 constitute the Full Feature Phase operation of + the connection. + + The state diagram is as follows: + + -------<-------------+ + +--------->/ S1 \<----+ | + T13| +->\ /<-+ \ | + | / ---+--- \ \ | + | / | T2 \ | | + | T8 | |T1 | | | + | | | / |T7 | + | | | / | | + | | | / | | + | | V / / | + | | ------- / / | + | | / S2 \ / | + | | \ / / | + | | ---+--- / | + | | |T4 / | + | | V / | T18 + | | ------- / | + | | / S4 \ | + | | \ / | + | | ---+--- | T15 + | | |T5 +--------+---------+ + | | | /T16+-----+------+ | + | | | / -+-----+--+ | | + | | | / / S7 \ |T12| | + | | | / +->\ /<-+ V V + | | | / / -+----- ------- + | | | / /T11 |T10 / S8 \ + | | V / / V +----+ \ / + | | ---+-+- ----+-- | ------- + | | / S5 \T9 / S6 \<+ ^ + | +-----\ /--->\ / T14 | + | ------- --+----+------+T17 + +---------------------------+ + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 89] + +RFC 3720 iSCSI April 2004 + + + The following state transition table represents the above diagram. + Each row represents the starting state for a given transition, which + after taking a transition marked in a table cell would end in the + state represented by the column of the cell. For example, from state + S1, the connection takes the T1 transition to arrive at state S2. + The fields marked "-" correspond to undefined transitions. + + +----+---+---+---+---+----+---+ + |S1 |S2 |S4 |S5 |S6 |S7 |S8 | + ---+----+---+---+---+---+----+---+ + S1| - |T1 | - | - | - | - | - | + ---+----+---+---+---+---+----+---+ + S2|T2 |- |T4 | - | - | - | - | + ---+----+---+---+---+---+----+---+ + S4|T7 |- |- |T5 | - | - | - | + ---+----+---+---+---+---+----+---+ + S5|T8 |- |- | - |T9 |T11 |T15| + ---+----+---+---+---+---+----+---+ + S6|T13 |- |- | - |T14|- |T17| + ---+----+---+---+---+---+----+---+ + S7|T18 |- |- | - |T10|T12 |T16| + ---+----+---+---+---+---+----+---+ + S8| - |- |- | - | - | - | - | + ---+----+---+---+---+---+----+---+ + +7.1.4. Standard Connection State Diagram for a Target + + Symbolic names for States: + + S1: FREE + S3: XPT_UP + S4: IN_LOGIN + S5: LOGGED_IN + S6: IN_LOGOUT + S7: LOGOUT_REQUESTED + S8: CLEANUP_WAIT + + States S5, S6, and S7 constitute the Full Feature Phase operation of + the connection. + + + + + + + + + + + + +Satran, et al. Standards Track [Page 90] + +RFC 3720 iSCSI April 2004 + + + The state diagram is as follows: + + -------<-------------+ + +--------->/ S1 \<----+ | + T13| +->\ /<-+ \ | + | / ---+--- \ \ | + | / | T6 \ | | + | T8 | |T3 | | | + | | | / |T7 | + | | | / | | + | | | / | | + | | V / / | + | | ------- / / | + | | / S3 \ / | + | | \ / / | T18 + | | ---+--- / | + | | |T4 / | + | | V / | + | | ------- / | + | | / S4 \ | + | | \ / | + | | ---+--- T15 | + | | |T5 +--------+---------+ + | | | /T16+-----+------+ | + | | | / -+-----+---+ | | + | | | / / S7 \ |T12| | + | | | / +->\ /<-+ V V + | | | / / -+----- ------- + | | | / /T11 |T10 / S8 \ + | | V / / V \ / + | | ---+-+- ------- ------- + | | / S5 \T9 / S6 \ ^ + | +-----\ /--->\ / | + | ------- --+----+--------+T17 + +---------------------------+ + + The following state transition table represents the above diagram, + and follows the conventions described for the initiator diagram. + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 91] + +RFC 3720 iSCSI April 2004 + + + +----+---+---+---+---+----+---+ + |S1 |S3 |S4 |S5 |S6 |S7 |S8 | + ---+----+---+---+---+---+----+---+ + S1| - |T3 | - | - | - | - | - | + ---+----+---+---+---+---+----+---+ + S3|T6 |- |T4 | - | - | - | - | + ---+----+---+---+---+---+----+---+ + S4|T7 |- |- |T5 | - | - | - | + ---+----+---+---+---+---+----+---+ + S5|T8 |- |- | - |T9 |T11 |T15| + ---+----+---+---+---+---+----+---+ + S6|T13 |- |- | - |- |- |T17| + ---+----+---+---+---+---+----+---+ + S7|T18 |- |- | - |T10|T12 |T16| + ---+----+---+---+---+---+----+---+ + S8| - |- |- | - | - | - | - | + ---+----+---+---+---+---+----+---+ + +7.2. Connection Cleanup State Diagram for Initiators and Targets + + Symbolic names for states: + + R1: CLEANUP_WAIT (same as S8) + R2: IN_CLEANUP + R3: FREE (same as S1) + + Whenever a connection state machine (e.g., CSM-C) enters the + CLEANUP_WAIT state (S8), it must go through the state transitions + described in the connection cleanup state diagram either a) using a + separate full-feature phase connection (let's call it CSM-E) in the + LOGGED_IN state in the same session, or b) using a new transport + connection (let's call it CSM-I) in the FREE state that is to be + added to the same session. In the CSM-E case, an explicit logout for + the CID that corresponds to CSM-C (either as a connection or session + logout) needs to be performed to complete the cleanup. In the CSM-I + case, an implicit logout for the CID that corresponds to CSM-C needs + to be performed by way of connection reinstatement (section 5.3.4) + for that CID. In either case, the protocol exchanges on CSM-E or + CSM-I determine the state transitions for CSM-C. Therefore, this + cleanup state diagram is only applicable to the instance of the + connection in cleanup (i.e., CSM-C). In the case of an implicit + logout for example, CSM-C reaches FREE (R3) at the time CSM-I reaches + LOGGED_IN. In the case of an explicit logout, CSM-C reaches FREE + (R3) when CSM-E receives a successful logout response while + continuing to be in the LOGGED_IN state. + + + + + + +Satran, et al. Standards Track [Page 92] + +RFC 3720 iSCSI April 2004 + + + An initiator must initiate an explicit or implicit connection logout + for a connection in the CLEANUP_WAIT state, if the initiator intends + to continue using the associated iSCSI session. + + The following state diagram applies to both initiators and targets. + + ------- + / R1 \ + +--\ /<-+ + / ---+--- + / | \ M3 + M1 | |M2 | + | | / + | | / + | | / + | V / + | ------- / + | / R2 \ + | \ / + | ------- + | | + | |M4 + | | + | | + | | + | V + | ------- + | / R3 \ + +---->\ / + ------- + + The following state transition table represents the above diagram, + and follows the same conventions as in earlier sections. + + +----+----+----+ + |R1 |R2 |R3 | + -----+----+----+----+ + R1 | - |M2 |M1 | + -----+----+----+----+ + R2 |M3 | - |M4 | + -----+----+----+----+ + R3 | - | - | - | + -----+----+----+----+ + + + + + + + + +Satran, et al. Standards Track [Page 93] + +RFC 3720 iSCSI April 2004 + + +7.2.1. State Descriptions for Initiators and Targets + + -R1: CLEANUP_WAIT (Same as S8) + -initiator: Waiting for the internal event to initiate the + cleanup processing for CSM-C. + -target: Waiting for the cleanup process to start for CSM-C. + -R2: IN_CLEANUP + -initiator: Waiting for the connection cleanup process to + conclude for CSM-C. + -target: Waiting for the connection cleanup process to conclude + for CSM-C. + -R3: FREE (Same as S1) + -initiator: End state for CSM-C. + -target: End state for CSM-C. + +7.2.2. State Transition Descriptions for Initiators and Targets + + -M1: One or more of the following events was received: + -initiator: + -An internal event that indicates connection state timeout. + -An internal event of receiving a successful Logout response + on a different connection for a "close the session" + Logout. + -target: + -An internal event that indicates connection state timeout. + -An internal event of sending a Logout response (success) on + a different connection for a "close the session" Logout + request. + + -M2: An implicit/explicit logout process was initiated by the + initiator. + -In CSM-I usage: + -initiator: An internal event requesting the connection (or + session) reinstatement was received, thus prompting a + connection (or session) reinstatement Login to be sent + transitioning CSM-I to state IN_LOGIN. + -target: A connection/session reinstatement Login was + received while in state XPT_UP. + -In CSM-E usage: + -initiator: An internal event that indicates that an + explicit logout was sent for this CID in state LOGGED_IN. + -target: An explicit logout was received for this CID in + state LOGGED_IN. + + + + + + + + +Satran, et al. Standards Track [Page 94] + +RFC 3720 iSCSI April 2004 + + + -M3: Logout failure detected + -In CSM-I usage: + -initiator: CSM-I failed to reach LOGGED_IN and arrived into + FREE instead. + -target: CSM-I failed to reach LOGGED_IN and arrived into + FREE instead. + -In CSM-E usage: + -initiator: CSM-E either moved out of LOGGED_IN, or Logout + timed out and/or aborted, or Logout response (failure) + was received. + -target: CSM-E either moved out of LOGGED_IN, Logout timed + out and/or aborted, or an internal event that indicates a + failed Logout processing was received. A Logout response + (failure) was sent in the last case. + + -M4: Successful implicit/explicit logout was performed. + + - In CSM-I usage: + -initiator: CSM-I reached state LOGGED_IN, or an internal + event of receiving a Logout response (success) on another + connection for a "close the session" Logout request was + received. + -target: CSM-I reached state LOGGED_IN, or an internal event + of sending a Logout response (success) on a different + connection for a "close the session" Logout request was + received. + - In CSM-E usage: + -initiator: CSM-E stayed in LOGGED_IN and received a Logout + response (success), or an internal event of receiving a + Logout response (success) on another connection for a + "close the session" Logout request was received. + -target: CSM-E stayed in LOGGED_IN and an internal event + indicating a successful Logout processing was received, + or an internal event of sending a Logout response + (success) on a different connection for a "close the + session" Logout request was received. + +7.3. Session State Diagrams + +7.3.1. Session State Diagram for an Initiator + + Symbolic Names for States: + + Q1: FREE + Q3: LOGGED_IN + Q4: FAILED + + State Q3 represents the Full Feature Phase operation of the session. + + + +Satran, et al. Standards Track [Page 95] + +RFC 3720 iSCSI April 2004 + + + The state diagram is as follows: + + ------- + / Q1 \ + +------>\ /<-+ + / ---+--- | + / | |N3 + N6 | |N1 | + | | | + | N4 | | + | +--------+ | / + | | | | / + | | | | / + | | V V / + -+--+-- -----+- + / Q4 \ N5 / Q3 \ + \ /<---\ / + ------- ------- + + The state transition table is as follows: + + +----+----+----+ + |Q1 |Q3 |Q4 | + -----+----+----+----+ + Q1 | - |N1 | - | + -----+----+----+----+ + Q3 |N3 | - |N5 | + -----+----+----+----+ + Q4 |N6 |N4 | - | + -----+----+----+----+ + +7.3.2. Session State Diagram for a Target + + Symbolic Names for States: + + Q1: FREE + Q2: ACTIVE + Q3: LOGGED_IN + Q4: FAILED + Q5: IN_CONTINUE + + State Q3 represents the Full Feature Phase operation of the session. + + + + + + + + + +Satran, et al. Standards Track [Page 96] + +RFC 3720 iSCSI April 2004 + + + The state diagram is as follows: + + ------- + +------------------>/ Q1 \ + / +-------------->\ /<-+ + | | ---+--- | + | | ^ | |N3 + N6 | |N11 N9| V N1 | + | | +------ | + | | / Q2 \ | + | | \ / | + | --+---- +--+--- | + | / Q5 \ | | + | \ / N10 | | + | +-+---+------------+ |N2 / + | ^ | | | / + |N7| |N8 | | / + | | | | V / + -+--+-V V----+- + / Q4 \ N5 / Q3 \ + \ /<-------------\ / + ------- ------- + + The state transition table is as follows: + + +----+----+----+----+----+ + |Q1 |Q2 |Q3 |Q4 |Q5 | + -----+----+----+----+----+----+ + Q1 | - |N1 | - | - | - | + -----+----+----+----+----+----+ + Q2 |N9 | - |N2 | - | - | + -----+----+----+----+----+----+ + Q3 |N3 | - | - |N5 | - | + -----+----+----+----+----+----+ + Q4 |N6 | - | - | - |N7 | + -----+----+----+----+----+----+ + Q5 |N11 | - |N10 |N8 | - | + -----+----+----+----+----+----+ + +7.3.3. State Descriptions for Initiators and Targets + + -Q1: FREE + -initiator: State on instantiation or after cleanup. + -target: State on instantiation or after cleanup. + + + + + + + +Satran, et al. Standards Track [Page 97] + +RFC 3720 iSCSI April 2004 + + + -Q2: ACTIVE + -initiator: Illegal. + -target: The first iSCSI connection in the session transitioned + to IN_LOGIN, waiting for it to complete the login process. + + -Q3: LOGGED_IN + -initiator: Waiting for all session events. + -target: Waiting for all session events. + + -Q4: FAILED + -initiator: Waiting for session recovery or session + continuation. + -target: Waiting for session recovery or session continuation. + + -Q5: IN_CONTINUE + -initiator: Illegal. + -target: Waiting for session continuation attempt to reach a + conclusion. + +7.3.4. State Transition Descriptions for Initiators and Targets + + -N1: + -initiator: At least one transport connection reached the + LOGGED_IN state. + -target: The first iSCSI connection in the session had reached + the IN_LOGIN state. + + -N2: + -initiator: Illegal. + -target: At least one iSCSI connection reached the LOGGED_IN + state. + + -N3: + -initiator: Graceful closing of the session via session closure + (Section 5.3.6 Session Continuation and Failure). + -target: Graceful closing of the session via session closure + (Section 5.3.6 Session Continuation and Failure) or a + successful session reinstatement cleanly closed the session. + + -N4: + -initiator: A session continuation attempt succeeded. + -target: Illegal. + + -N5: + -initiator: Session failure (Section 5.3.6 Session Continuation + and Failure) occurred. + -target: Session failure (Section 5.3.6 Session Continuation and + Failure) occurred. + + + +Satran, et al. Standards Track [Page 98] + +RFC 3720 iSCSI April 2004 + + + -N6: + -initiator: Session state timeout occurred, or a session + reinstatement cleared this session instance. This results + in the freeing of all associated resources and the session + state is discarded. + -target: Session state timeout occurred, or a session + reinstatement cleared this session instance. This results + in the freeing of all associated resources and the session + state is discarded. + + -N7: + -initiator: Illegal. + -target: A session continuation attempt is initiated. + + -N8: + -initiator: Illegal. + -target: The last session continuation attempt failed. + + -N9: + -initiator: Illegal. + -target: Login attempt on the leading connection failed. + + -N10: + -initiator: Illegal. + -target: A session continuation attempt succeeded. + + -N11: + -initiator: Illegal. + -target: A successful session reinstatement cleanly closed the + session. + +8. Security Considerations + + Historically, native storage systems have not had to consider + security because their environments offered minimal security risks. + That is, these environments consisted of storage devices either + directly attached to hosts or connected via a Storage Area Network + (SAN) distinctly separate from the communications network. The use + of storage protocols, such as SCSI, over IP-networks requires that + security concerns be addressed. iSCSI implementations MUST provide + means of protection against active attacks (e.g., pretending to be + another identity, message insertion, deletion, modification, and + replaying) and passive attacks (e.g., eavesdropping, gaining + advantage by analyzing the data sent over the line). + + + + + + + +Satran, et al. Standards Track [Page 99] + +RFC 3720 iSCSI April 2004 + + + Although technically possible, iSCSI SHOULD NOT be configured without + security. iSCSI configured without security should be confined, in + extreme cases, to closed environments without any security risk. + [RFC3723] specifies the mechanisms that must be used in order to + mitigate risks fully described in that document. + + The following section describes the security mechanisms provided by + an iSCSI implementation. + +8.1. iSCSI Security Mechanisms + + The entities involved in iSCSI security are the initiator, target, + and the IP communication end points. iSCSI scenarios in which + multiple initiators or targets share a single communication end point + are expected. To accommodate such scenarios, iSCSI uses two separate + security mechanisms: In-band authentication between the initiator and + the target at the iSCSI connection level (carried out by exchange of + iSCSI Login PDUs), and packet protection (integrity, authentication, + and confidentiality) by IPsec at the IP level. The two security + mechanisms complement each other. The in-band authentication + provides end-to-end trust (at login time) between the iSCSI initiator + and the target while IPsec provides a secure channel between the IP + communication end points. + + Further details on typical iSCSI scenarios and the relation between + the initiators, targets, and the communication end points can be + found in [RFC3723]. + +8.2. In-band Initiator-Target Authentication + + During login, the target MAY authenticate the initiator and the + initiator MAY authenticate the target. The authentication is + performed on every new iSCSI connection by an exchange of iSCSI Login + PDUs using a negotiated authentication method. + + The authentication method cannot assume an underlying IPsec + protection, because IPsec is optional to use. An attacker should + gain as little advantage as possible by inspecting the authentication + phase PDUs. Therefore, a method using clear text (or equivalent) + passwords is not acceptable; on the other hand, identity protection + is not strictly required. + + The authentication mechanism protects against an unauthorized login + to storage resources by using a false identity (spoofing). Once the + authentication phase is completed, if the underlying IPsec is not + used, all PDUs are sent and received in clear. The authentication + + + + + +Satran, et al. Standards Track [Page 100] + +RFC 3720 iSCSI April 2004 + + + mechanism alone (without underlying IPsec) should only be used when + there is no risk of eavesdropping, message insertion, deletion, + modification, and replaying. + + Section 11 iSCSI Security Text Keys and Authentication Methods + defines several authentication methods and the exact steps that must + be followed in each of them, including the iSCSI-text-keys and their + allowed values in each step. Whenever an iSCSI initiator gets a + response whose keys, or their values, are not according to the step + definition, it MUST abort the connection. Whenever an iSCSI target + gets a response whose keys, or their values, are not according to the + step definition, it MUST answer with a Login reject with the + "Initiator Error" or "Missing Parameter" status. These statuses are + not intended for cryptographically incorrect values such as the CHAP + response, for which "Authentication Failure" status MUST be + specified. The importance of this rule can be illustrated in CHAP + with target authentication (see Section 11.1.4 Challenge Handshake + Authentication Protocol (CHAP)) where the initiator would have been + able to conduct a reflection attack by omitting his response key + (CHAP_R) using the same CHAP challenge as the target and reflecting + the target's response back to the target. In CHAP, this is prevented + because the target must answer the missing CHAP_R key with a Login + reject with the "Missing Parameter" status. + + For some of the authentication methods, a key specifies the identity + of the iSCSI initiator or target for authentication purposes. The + value associated with that key MAY be different from the iSCSI name + and SHOULD be configurable. (CHAP_N, see Section 11.1.4 Challenge + Handshake Authentication Protocol (CHAP) and SRP_U, see Section + 11.1.3 Secure Remote Password (SRP)). + +8.2.1. CHAP Considerations + + Compliant iSCSI initiators and targets MUST implement the CHAP + authentication method [RFC1994] (according to Section 11.1.4 + Challenge Handshake Authentication Protocol (CHAP) including the + target authentication option). + + When CHAP is performed over a non-encrypted channel, it is vulnerable + to an off-line dictionary attack. Implementations MUST support use + of up to 128 bit random CHAP secrets, including the means to generate + such secrets and to accept them from an external generation source. + Implementations MUST NOT provide secret generation (or expansion) + means other than random generation. + + An administrative entity of an environment in which CHAP is used with + a secret that has less than 96 random bits MUST enforce IPsec + encryption (according to the implementation requirements in Section + + + +Satran, et al. Standards Track [Page 101] + +RFC 3720 iSCSI April 2004 + + + 8.3.2 Confidentiality) to protect the connection. Moreover, in this + case IKE authentication with group pre-shared cryptographic keys + SHOULD NOT be used unless it is not essential to protect group + members against off-line dictionary attacks by other members. + + CHAP secrets MUST be an integral number of bytes (octets). A + compliant implementation SHOULD NOT continue with the login step in + which it should send a CHAP response (CHAP_R, Section 11.1.4 + Challenge Handshake Authentication Protocol (CHAP)) unless it can + verify that the CHAP secret is at least 96 bits, or that IPsec + encryption is being used to protect the connection. + + Any CHAP secret used for initiator authentication MUST NOT be + configured for authentication of any target, and any CHAP secret used + for target authentication MUST NOT be configured for authentication + of any initiator. If the CHAP response received by one end of an + iSCSI connection is the same as the CHAP response that the receiving + endpoint would have generated for the same CHAP challenge, the + response MUST be treated as an authentication failure and cause the + connection to close (this ensures that the same CHAP secret is not + used for authentication in both directions). Also, if an iSCSI + implementation can function as both initiator and target, different + CHAP secrets and identities MUST be configured for these two roles. + The following is an example of the attacks prevented by the above + requirements: + + Rogue wants to impersonate Storage to Alice, and knows that a + single secret is used for both directions of Storage-Alice + authentication. + + Rogue convinces Alice to open two connections to Rogue, and Rogue + identifies itself as Storage on both connections. + + Rogue issues a CHAP challenge on connection 1, waits for Alice to + respond, and then reflects Alice's challenge as the initial + challenge to Alice on connection 2. + + If Alice doesn't check for the reflection across connections, + Alice's response on connection 2 enables Rogue to impersonate + Storage on connection 1, even though Rogue does not know the + Alice-Storage CHAP secret. + + Originators MUST NOT reuse the CHAP challenge sent by the Responder + for the other direction of a bidirectional authentication. + Responders MUST check for this condition and close the iSCSI TCP + connection if it occurs. + + + + + +Satran, et al. Standards Track [Page 102] + +RFC 3720 iSCSI April 2004 + + + The same CHAP secret SHOULD NOT be configured for authentication of + multiple initiators or multiple targets, as this enables any of them + to impersonate any other one of them, and compromising one of them + enables the attacker to impersonate any of them. It is recommended + that iSCSI implementations check for use of identical CHAP secrets by + different peers when this check is feasible, and take appropriate + measures to warn users and/or administrators when this is detected. + + When an iSCSI initiator or target authenticates itself to + counterparts in multiple administrative domains, it SHOULD use a + different CHAP secret for each administrative domain to avoid + propagating security compromises across domains. + + Within a single administrative domain: + - A single CHAP secret MAY be used for authentication of an initiator + to multiple targets. + - A single CHAP secret MAY be used for an authentication of a target + to multiple initiators when the initiators use an external server + (e.g., RADIUS) to verify the target's CHAP responses and do not know + the target's CHAP secret. + + If an external response verification server (e.g., RADIUS) is not + used, employing a single CHAP secret for authentication of a target + to multiple initiators requires that all such initiators know that + target secret. Any of these initiators can impersonate the target to + any other such initiator, and compromise of such an initiator enables + an attacker to impersonate the target to all such initiators. + Targets SHOULD use separate CHAP secrets for authentication to each + initiator when such risks are of concern; in this situation it may be + useful to configure a separate logical iSCSI target with its own + iSCSI Node Name for each initiator or group of initiators among which + such separation is desired. + +8.2.2. SRP Considerations + + The strength of the SRP authentication method (specified in + [RFC2945]) is dependent on the characteristics of the group being + used (i.e., the prime modulus N and generator g). As described in + [RFC2945], N is required to be a Sophie-German prime (of the form + N = 2q + 1, where q is also prime) and the generator g is a primitive + root of GF(n). In iSCSI authentication, the prime modulus N MUST be + at least 768 bits. + + The list of allowed SRP groups is provided in [RFC3723]. + + + + + + + +Satran, et al. Standards Track [Page 103] + +RFC 3720 iSCSI April 2004 + + +8.3. IPsec + + iSCSI uses the IPsec mechanism for packet protection (cryptographic + integrity, authentication, and confidentiality) at the IP level + between the iSCSI communicating end points. The following sections + describe the IPsec protocols that must be implemented for data + integrity and authentication, confidentiality, and cryptographic key + management. + + An iSCSI initiator or target may provide the required IPsec support + fully integrated or in conjunction with an IPsec front-end device. + In the latter case, the compliance requirements with regard to IPsec + support apply to the "combined device". Only the "combined device" + is to be considered an iSCSI device. + + Detailed considerations and recommendations for using IPsec for iSCSI + are provided in [RFC3723]. + +8.3.1. Data Integrity and Authentication + + Data authentication and integrity is provided by a cryptographic + keyed Message Authentication Code in every sent packet. This code + protects against message insertion, deletion, and modification. + Protection against message replay is realized by using a sequence + counter. + + An iSCSI compliant initiator or target MUST provide data integrity + and authentication by implementing IPsec [RFC2401] with ESP [RFC2406] + in tunnel mode and MAY provide data integrity and authentication by + implementing IPsec with ESP in transport mode. The IPsec + implementation MUST fulfill the following iSCSI specific + requirements: + + - HMAC-SHA1 MUST be implemented [RFC2404]. + - AES CBC MAC with XCBC extensions SHOULD be implemented + [RFC3566]. + + The ESP anti-replay service MUST also be implemented. + + At the high speeds iSCSI is expected to operate, a single IPsec SA + could rapidly cycle through the 32-bit IPsec sequence number space. + In view of this, it may be desirable in the future for an iSCSI + implementation that operates at speeds of 1 Gbps or greater to + implement the IPsec sequence number extension [SEQ-EXT]. + + + + + + + +Satran, et al. Standards Track [Page 104] + +RFC 3720 iSCSI April 2004 + + +8.3.2. Confidentiality + + Confidentiality is provided by encrypting the data in every packet. + When confidentiality is used it MUST be accompanied by data integrity + and authentication to provide comprehensive protection against + eavesdropping, message insertion, deletion, modification, and + replaying. + + An iSCSI compliant initiator or target MUST provide confidentiality + by implementing IPsec [RFC2401] with ESP [RFC2406] in tunnel mode and + MAY provide confidentiality by implementing IPsec with ESP in + transport mode, with the following iSCSI specific requirements: + + - 3DES in CBC mode MUST be implemented [RFC2451]. + - AES in Counter mode SHOULD be implemented [RFC3686]. + + DES in CBC mode SHOULD NOT be used due to its inherent weakness. The + NULL encryption algorithm MUST also be implemented. + +8.3.3. Policy, Security Associations, and Cryptographic Key Management + + A compliant iSCSI implementation MUST meet the cryptographic key + management requirements of the IPsec protocol suite. Authentication, + security association negotiation, and cryptographic key management + MUST be provided by implementing IKE [RFC2409] using the IPsec DOI + [RFC2407] with the following iSCSI specific requirements: + + - Peer authentication using a pre-shared cryptographic key MUST be + supported. Certificate-based peer authentication using digital + signatures MAY be supported. Peer authentication using the + public key encryption methods outlined in IKE sections 5.2 and + 5.3[7] SHOULD NOT be used. + + - When digital signatures are used to achieve authentication, an + IKE negotiator SHOULD use IKE Certificate Request Payload(s) to + specify the certificate authority. IKE negotiators SHOULD check + the pertinent Certificate Revocation List (CRL) before accepting + a PKI certificate for use in IKE authentication procedures. + + - Conformant iSCSI implementations MUST support IKE Main Mode and + SHOULD support Aggressive Mode. IKE main mode with pre-shared + key authentication method SHOULD NOT be used when either the + initiator or the target uses dynamically assigned IP addresses. + While in many cases pre-shared keys offer good security, + situations in which dynamically assigned addresses are used force + the use of a group pre-shared key, which creates vulnerability to + a man-in-the-middle attack. + + + + +Satran, et al. Standards Track [Page 105] + +RFC 3720 iSCSI April 2004 + + + - In the IKE Phase 2 Quick Mode, exchanges for creating the Phase 2 + SA, the Identity Payload, fields MUST be present. ID_IPV4_ADDR, + ID_IPV6_ADDR (if the protocol stack supports IPv6) and ID_FQDN + Identity payloads MUST be supported; ID_USER_FQDN SHOULD be + supported. The IP Subnet, IP Address Range, ID_DER_ASN1_DN, and + ID_DER_ASN1_GN formats SHOULD NOT be used. The ID_KEY_ID + Identity Payload MUST NOT be used. + + Manual cryptographic keying MUST NOT be used because it does not + provide the necessary re-keying support. + + When IPsec is used, the receipt of an IKE Phase 2 delete message + SHOULD NOT be interpreted as a reason for tearing down the iSCSI TCP + connection. If additional traffic is sent on it, a new IKE Phase 2 + SA will be created to protect it. + + The method used by the initiator to determine whether the target + should be connected using IPsec is regarded as an issue of IPsec + policy administration, and thus not defined in the iSCSI standard. + + If an iSCSI target is discovered via a SendTargets request in a + discovery session not using IPsec, the initiator should assume that + it does not need IPsec to establish a session to that target. If an + iSCSI target is discovered using a discovery session that does use + IPsec, the initiator SHOULD use IPsec when establishing a session to + that target. + +9. Notes to Implementers + + This section notes some of the performance and reliability + considerations of the iSCSI protocol. This protocol was designed to + allow efficient silicon and software implementations. The iSCSI task + tag mechanism was designed to enable Direct Data Placement (DDP - a + DMA form) at the iSCSI level or lower. + + The guiding assumption made throughout the design of this protocol is + that targets are resource constrained relative to initiators. + + Implementers are also advised to consider the implementation + consequences of the iSCSI to SCSI mapping model as outlined in + Section 3.4.3 Consequences of the Model. + +9.1. Multiple Network Adapters + + The iSCSI protocol allows multiple connections, not all of which need + to go over the same network adapter. If multiple network connections + are to be utilized with hardware support, the iSCSI protocol + + + + +Satran, et al. Standards Track [Page 106] + +RFC 3720 iSCSI April 2004 + + + command-data-status allegiance to one TCP connection ensures that + there is no need to replicate information across network adapters or + otherwise require them to cooperate. + + However, some task management commands may require some loose form of + cooperation or replication at least on the target. + +9.1.1. Conservative Reuse of ISIDs + + Historically, the SCSI model (and implementations and applications + based on that model) has assumed that SCSI ports are static, physical + entities. Recent extensions to the SCSI model have taken advantage + of persistent worldwide unique names for these ports. In iSCSI + however, the SCSI initiator ports are the endpoints of dynamically + created sessions, so the presumptions of "static and physical" do not + apply. In any case, the model clauses (particularly, Section 3.4.2 + SCSI Architecture Model) provide for persistent, reusable names for + the iSCSI-type SCSI initiator ports even though there does not need + to be any physical entity bound to these names. + + To both minimize the disruption of legacy applications and to better + facilitate the SCSI features that rely on persistent names for SCSI + ports, iSCSI implementations SHOULD attempt to provide a stable + presentation of SCSI Initiator Ports (both to the upper OS-layers and + to the targets to which they connect). This can be achieved in an + initiator implementation by conservatively reusing ISIDs. In other + words, the same ISID should be used in the Login process to multiple + target portal groups (of the same iSCSI Target or different iSCSI + Targets). The ISID RULE (Section 3.4.3 Consequences of the Model) + only prohibits reuse to the same target portal group. It does not + "preclude" reuse to other target portal groups. The principle of + conservative reuse "encourages" reuse to other target portal groups. + When a SCSI target device sees the same (InitiatorName, ISID) pair in + different sessions to different target portal groups, it can identify + the underlying SCSI Initiator Port on each session as the same SCSI + port. In effect, it can recognize multiple paths from the same + source. + +9.1.2. iSCSI Name, ISID, and TPGT Use + + The designers of the iSCSI protocol envisioned there being one iSCSI + Initiator Node Name per operating system image on a machine. This + enables SAN resource configuration and authentication schemes based + on a system's identity. It supports the notion that it should be + possible to assign access to storage resources based on "initiator + device" identity. + + + + + +Satran, et al. Standards Track [Page 107] + +RFC 3720 iSCSI April 2004 + + + When there are multiple hardware or software components coordinated + as a single iSCSI Node, there must be some (logical) entity that + represents the iSCSI Node that makes the iSCSI Node Name available to + all components involved in session creation and login. Similarly, + this entity that represents the iSCSI Node must be able to coordinate + session identifier resources (ISID for initiators) to enforce both + the ISID and TSIH RULES (see Section 3.4.3 Consequences of the + Model). + + For targets, because of the closed environment, implementation of + this entity should be straightforward. However, vendors of iSCSI + hardware (e.g., NICs or HBAs) intended for targets, SHOULD provide + mechanisms for configuration of the iSCSI Node Name across the portal + groups instantiated by multiple instances of these components within + a target. + + However, complex targets making use of multiple Target Portal Group + Tags may reconfigure them to achieve various quality goals. The + initiators have two mechanisms at their disposal to discover and/or + check reconfiguring targets - the discovery session type and a key + returned by the target during login to confirm the TPGT. An + initiator should attempt to "rediscover" the target configuration + anytime a session is terminated unexpectedly. + + For initiators, in the long term, it is expected that operating + system vendors will take on the role of this entity and provide + standard APIs that can inform components of their iSCSI Node Name and + can configure and/or coordinate ISID allocation, use, and reuse. + + Recognizing that such initiator APIs are not available today, other + implementations of the role of this entity are possible. For + example, a human may instantiate the (common) Node name as part of + the installation process of each iSCSI component involved in session + creation and login. This may be done either by pointing the + component to a vendor-specific location for this datum or to a + system-wide location. The structure of the ISID namespace (see + Section 10.12.5 ISID and [RFC3721]) facilitates implementation of the + ISID coordination by allowing each component vendor to independently + (of other vendor's components) coordinate allocation, use, and reuse + of its own partition of the ISID namespace in a vendor-specific + manner. Partitioning of the ISID namespace within initiator portal + groups managed by that vendor allows each such initiator portal group + to act independently of all other portal groups when selecting an + ISID for a login; this facilitates enforcement of the ISID RULE (see + Section 3.4.3 Consequences of the Model) at the initiator. + + + + + + +Satran, et al. Standards Track [Page 108] + +RFC 3720 iSCSI April 2004 + + + A vendor of iSCSI hardware (e.g., NICs or HBAs) intended for use in + initiators MUST implement a mechanism for configuring the iSCSI Node + Name. Vendors, and administrators must ensure that iSCSI Node Names + are unique worldwide. It is therefore important that when one + chooses to reuse the iSCSI Node Name of a disabled unit, not to + re-assign that name to the original unit unless its worldwide + uniqueness can be ascertained again. + + In addition, a vendor of iSCSI hardware must implement a mechanism to + configure and/or coordinate ISIDs for all sessions managed by + multiple instances of that hardware within a given iSCSI Node. Such + configuration might be either permanently pre-assigned at the factory + (in a necessarily globally unique way), statically assigned (e.g., + partitioned across all the NICs at initialization in a locally unique + way), or dynamically assigned (e.g., on-line allocator, also in a + locally unique way). In the latter two cases, the configuration may + be via public APIs (perhaps driven by an independent vendor's + software, such as the OS vendor) or via private APIs driven by the + vendor's own software. + +9.2. Autosense and Auto Contingent Allegiance (ACA) + + Autosense refers to the automatic return of sense data to the + initiator in case a command did not complete successfully. iSCSI + initiators and targets MUST support and use autosense. + + ACA helps preserve ordered command execution in the presence of + errors. As iSCSI can have many commands in-flight between initiator + and target, iSCSI initiators and targets SHOULD support ACA. + +9.3. iSCSI Timeouts + + iSCSI recovery actions are often dependent on iSCSI time-outs being + recognized and acted upon before SCSI time-outs. Determining the + right time-outs to use for various iSCSI actions (command + acknowledgements expected, status acknowledgements, etc.) is very + much dependent on infrastructure (hardware, links, TCP/IP stack, + iSCSI driver). As a guide, the implementer may use an average + Nop-Out/Nop-In turnaround delay multiplied by a "safety factor" + (e.g., 4) as a good estimate for the basic delay of the iSCSI stack + for a given connection. The safety factor should account for the + network load variability. For connection teardown the implementer + may want to consider also the TCP common practice for the given + infrastructure. + + + + + + + +Satran, et al. Standards Track [Page 109] + +RFC 3720 iSCSI April 2004 + + + Text negotiations MAY also be subject to either time-limits or limits + in the number of exchanges. Those SHOULD be generous enough to avoid + affecting interoperability (e.g., allowing each key to be negotiated + on a separate exchange). + + The relation between iSCSI timeouts and SCSI timeouts should also be + considered. SCSI timeouts should be longer than iSCSI timeouts plus + the time required for iSCSI recovery whenever iSCSI recovery is + planned. Alternatively, an implementer may choose to interlock iSCSI + timeouts and recovery with SCSI timeouts so that SCSI recovery will + become active only where iSCSI is not planned to, or failed to, + recover. + + The implementer may also want to consider the interaction between + various iSCSI exception events - such as a digest failure - and + subsequent timeouts. When iSCSI error recovery is active, a digest + failure is likely to result in discovering a missing command or data + PDU. In these cases, an implementer may want to lower the timeout + values to enable faster initiation for recovery procedures. + +9.4. Command Retry and Cleaning Old Command Instances + + To avoid having old, retried command instances appear in a valid + command window after a command sequence number wrap around, the + protocol requires (see Section 3.2.2.1 Command Numbering and + Acknowledging) that on every connection on which a retry has been + issued, a non-immediate command be issued and acknowledged within a + 2**31-1 commands interval from the CmdSN of the retried command. + This requirement can be fulfilled by an implementation in several + ways. + + The simplest technique to use is to send a (non-retry) non-immediate + SCSI command (or a NOP if no SCSI command is available for a while) + after every command retry on the connection on which the retry was + attempted. As errors are deemed rare events, this technique is + probably the most effective, as it does not involve additional checks + at the initiator when issuing commands. + +9.5. Synch and Steering Layer and Performance + + While a synch and steering layer is optional, an initiator/target + that does not have it working against a target/initiator that demands + synch and steering may experience performance degradation caused by + packet reordering and loss. Providing a synch and steering mechanism + is recommended for all high-speed implementations. + + + + + + +Satran, et al. Standards Track [Page 110] + +RFC 3720 iSCSI April 2004 + + +9.6. Considerations for State-dependent Devices and Long-lasting SCSI + Operations + + Sequential access devices operate on the principle that the position + of the device is based on the last command processed. As such, + command processing order and knowledge of whether or not the previous + command was processed is of the utmost importance to maintain data + integrity. For example, inadvertent retries of SCSI commands when it + is not known if the previous SCSI command was processed is a + potential data integrity risk. + + For a sequential access device, consider the scenario in which a SCSI + SPACE command to backspace one filemark is issued and then re-issued + due to no status received for the command. If the first SPACE + command was actually processed, the re-issued SPACE command, if + processed, will cause the position to change. Thus, a subsequent + write operation will write data to the wrong position and any + previous data at that position will be overwritten. + + For a medium changer device, consider the scenario in which an + EXCHANGE MEDIUM command (the SOURCE ADDRESS and DESTINATION ADDRESS + are the same thus performing a swap) is issued and then re-issued due + to no status received for the command. If the first EXCHANGE MEDIUM + command was actually processed, the re-issued EXCHANGE MEDIUM + command, if processed, will perform the swap again. The net effect + is that a swap was not performed thus leaving a data integrity + exposure. + + All commands that change the state of the device (as in SPACE + commands for sequential access devices, and EXCHANGE MEDIUM for + medium changer device), MUST be issued as non-immediate commands for + deterministic and in order delivery to iSCSI targets. + + For many of those state changing commands, the execution model also + assumes that the command is executed exactly once. Devices + implementing READ POSITION and LOCATE provide a means for SCSI level + command recovery and new tape-class devices should support those + commands. In their absence a retry at SCSI level is difficult and + error recovery at iSCSI level is advisable. + + Devices operating on long latency delivery subsystems and performing + long lasting SCSI operations may need mechanisms that enable + connection replacement while commands are running (e.g., during an + extended copy operation). + + + + + + + +Satran, et al. Standards Track [Page 111] + +RFC 3720 iSCSI April 2004 + + +9.6.1. Determining the Proper ErrorRecoveryLevel + + The implementation and use of a specific ErrorRecoveryLevel should be + determined based on the deployment scenarios of a given iSCSI + implementation. Generally, the following factors must be considered + before deciding on the proper level of recovery: + + a) Application resilience to I/O failures. + b) Required level of availability in the face of transport + connection failures. + c) Probability of transport layer "checksum escape". This in + turn decides the iSCSI digest failure frequency, and thus the + criticality of iSCSI-level error recovery. The details of + estimating this probability are outside the scope of this + document. + + + A consideration of the above factors for SCSI tape devices as an + example suggests that implementations SHOULD use ErrorRecoveryLevel=1 + when transport connection failure is not a concern and SCSI level + recovery is unavailable, and ErrorRecoveryLevel=2 when the connection + failure is also of high likelihood during a backup/retrieval. + + For extended copy operations, implementations SHOULD use + ErrorRecoveryLevel=2 whenever there is a relatively high likelihood + of connection failure. + +10. iSCSI PDU Formats + + All multi-byte integers that are specified in formats defined in this + document are to be represented in network byte order (i.e., big + endian). Any field that appears in this document assumes that the + most significant byte is the lowest numbered byte and the most + significant bit (within byte or field) is the lowest numbered bit + unless specified otherwise. + + Any compliant sender MUST set all bits not defined and all reserved + fields to zero unless specified otherwise. Any compliant receiver + MUST ignore any bit not defined and all reserved fields unless + specified otherwise. Receipt of reserved code values in defined + fields MUST be reported as a protocol error. + + Reserved fields are marked by the word "reserved", some abbreviation + of "reserved", or by "." for individual bits when no other form of + marking is technically feasible. + + + + + + +Satran, et al. Standards Track [Page 112] + +RFC 3720 iSCSI April 2004 + + +10.1. iSCSI PDU Length and Padding + + iSCSI PDUs are padded to the closest integer number of four byte + words. The padding bytes SHOULD be sent as 0. + +10.2. PDU Template, Header, and Opcodes + + All iSCSI PDUs have one or more header segments and, optionally, a + data segment. After the entire header segment group a header-digest + MAY follow. The data segment MAY also be followed by a data-digest. + + The Basic Header Segment (BHS) is the first segment in all of the + iSCSI PDUs. The BHS is a fixed-length 48-byte header segment. It + MAY be followed by Additional Header Segments (AHS), a Header-Digest, + a Data Segment, and/or a Data-Digest. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 113] + +RFC 3720 iSCSI April 2004 + + + The overall structure of an iSCSI PDU is as follows: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0/ Basic Header Segment (BHS) / + +/ / + +---------------+---------------+---------------+---------------+ + 48/ Additional Header Segment 1 (AHS) (optional) / + +/ / + +---------------+---------------+---------------+---------------+ + / Additional Header Segment 2 (AHS) (optional) / + +/ / + +---------------+---------------+---------------+---------------+ + ---- + +---------------+---------------+---------------+---------------+ + / Additional Header Segment n (AHS) (optional) / + +/ / + +---------------+---------------+---------------+---------------+ + ---- + +---------------+---------------+---------------+---------------+ + k/ Header-Digest (optional) / + +/ / + +---------------+---------------+---------------+---------------+ + l/ Data Segment(optional) / + +/ / + +---------------+---------------+---------------+---------------+ + m/ Data-Digest (optional) / + +/ / + +---------------+---------------+---------------+---------------+ + + All PDU segments and digests are padded to the closest integer number + of four byte words. For example, all PDU segments and digests start + at a four byte word boundary and the padding ranges from 0 to 3 + bytes. The padding bytes SHOULD be sent as 0. + + iSCSI response PDUs do not have AH Segments. + +10.2.1. Basic Header Segment (BHS) + + The BHS is 48 bytes long. The Opcode and DataSegmentLength fields + appear in all iSCSI PDUs. In addition, when used, the Initiator Task + Tag and Logical Unit Number always appear in the same location in the + header. + + + + + + +Satran, et al. Standards Track [Page 114] + +RFC 3720 iSCSI April 2004 + + + The format of the BHS is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| Opcode |F| Opcode-specific fields | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Opcode-specific fields | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20/ Opcode-specific fields / + +/ / + +---------------+---------------+---------------+---------------+ + 48 + +10.2.1.1 I + + For request PDUs, the I bit set to 1 is an immediate delivery marker. + +10.2.1.2. Opcode + + The Opcode indicates the type of iSCSI PDU the header encapsulates. + + The Opcodes are divided into two categories: initiator opcodes and + target opcodes. Initiator opcodes are in PDUs sent by the initiator + (request PDUs). Target opcodes are in PDUs sent by the target + (response PDUs). + + Initiators MUST NOT use target opcodes and targets MUST NOT use + initiator opcodes. + + Initiator opcodes defined in this specification are: + + 0x00 NOP-Out + 0x01 SCSI Command (encapsulates a SCSI Command Descriptor Block) + 0x02 SCSI Task Management function request + 0x03 Login Request + 0x04 Text Request + 0x05 SCSI Data-Out (for WRITE operations) + 0x06 Logout Request + 0x10 SNACK Request + 0x1c-0x1e Vendor specific codes + + + +Satran, et al. Standards Track [Page 115] + +RFC 3720 iSCSI April 2004 + + + + Target opcodes are: + + 0x20 NOP-In + 0x21 SCSI Response - contains SCSI status and possibly sense + information or other response information. + 0x22 SCSI Task Management function response + 0x23 Login Response + 0x24 Text Response + 0x25 SCSI Data-In - for READ operations. + 0x26 Logout Response + 0x31 Ready To Transfer (R2T) - sent by target when it is ready + to receive data. + 0x32 Asynchronous Message - sent by target to indicate certain + special conditions. + 0x3c-0x3e Vendor specific codes + 0x3f Reject + + All other opcodes are reserved. + +10.2.1.3. Final (F) bit + + When set to 1 it indicates the final (or only) PDU of a sequence. + +10.2.1.4. Opcode-specific Fields + + These fields have different meanings for different opcode types. + +10.2.1.5. TotalAHSLength + + Total length of all AHS header segments in units of four byte words + including padding, if any. + + The TotalAHSLength is only used in PDUs that have an AHS and MUST be + 0 in all other PDUs. + +10.2.1.6. DataSegmentLength + + This is the data segment payload length in bytes (excluding padding). + The DataSegmentLength MUST be 0 whenever the PDU has no data segment. + +10.2.1.7. LUN + + Some opcodes operate on a specific Logical Unit. The Logical Unit + Number (LUN) field identifies which Logical Unit. If the opcode does + not relate to a Logical Unit, this field is either ignored or may be + used in an opcode specific way. The LUN field is 64-bits and should + + + + +Satran, et al. Standards Track [Page 116] + +RFC 3720 iSCSI April 2004 + + + be formatted in accordance with [SAM2]. For example, LUN[0] from + [SAM2] is BHS byte 8 and so on up to LUN[7] from [SAM2], which is BHS + byte 15. + +10.2.1.8. Initiator Task Tag + + The initiator assigns a Task Tag to each iSCSI task it issues. While + a task exists, this tag MUST uniquely identify the task session-wide. + SCSI may also use the initiator task tag as part of the SCSI task + identifier when the timespan during which an iSCSI initiator task tag + must be unique extends over the timespan during which a SCSI task tag + must be unique. However, the iSCSI Initiator Task Tag must exist and + be unique even for untagged SCSI commands. + +10.2.2. Additional Header Segment (AHS) + + The general format of an AHS is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0| AHSLength | AHSType | AHS-Specific | + +---------------+---------------+---------------+---------------+ + 4/ AHS-Specific / + +/ / + +---------------+---------------+---------------+---------------+ + x + +10.2.2.1. AHSType + + The AHSType field is coded as follows: + + bit 0-1 - Reserved + + bit 2-7 - AHS code + + 0 - Reserved + 1 - Extended CDB + 2 - Expected Bidirectional Read Data Length + 3 - 63 Reserved + +10.2.2.2. AHSLength + + This field contains the effective length in bytes of the AHS + excluding AHSType and AHSLength and padding, if any. The AHS is + padded to the smallest integer number of 4 byte words (i.e., from 0 + up to 3 padding bytes). + + + +Satran, et al. Standards Track [Page 117] + +RFC 3720 iSCSI April 2004 + + +10.2.2.3. Extended CDB AHS + + The format of the Extended CDB AHS is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0| AHSLength (CDBLength-15) | 0x01 | Reserved | + +---------------+---------------+---------------+---------------+ + 4/ ExtendedCDB...+padding / + +/ / + +---------------+---------------+---------------+---------------+ + x + + This type of AHS MUST NOT be used if the CDBLength is less than 17. + The length includes the reserved byte 3. + +10.2.2.4. Bidirectional Expected Read-Data Length AHS + + The format of the Bidirectional Read Expected Data Transfer Length + AHS is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0| AHSLength (0x0005) | 0x02 | Reserved | + +---------------+---------------+---------------+---------------+ + 4| Expected Read-Data Length | + +---------------+---------------+---------------+---------------+ + 8 + +10.2.3. Header Digest and Data Digest + + Optional header and data digests protect the integrity of the header + and data, respectively. The digests, if present, are located, + respectively, after the header and PDU-specific data, and cover + respectively the header and the PDU data, each including the padding + bytes, if any. + + The existence and type of digests are negotiated during the Login + Phase. + + The separation of the header and data digests is useful in iSCSI + routing applications, in which only the header changes when a message + is forwarded. In this case, only the header digest should be + recalculated. + + + +Satran, et al. Standards Track [Page 118] + +RFC 3720 iSCSI April 2004 + + + Digests are not included in data or header length fields. + + A zero-length Data Segment also implies a zero-length data-digest. + +10.2.4. Data Segment + + The (optional) Data Segment contains PDU associated data. Its + payload effective length is provided in the BHS field - + DataSegmentLength. The Data Segment is also padded to an integer + number of 4 byte words. + +10.3. SCSI Command + + The format of the SCSI Command PDU is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| 0x01 |F|R|W|. .|ATTR | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| Logical Unit Number (LUN) | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Expected Data Transfer Length | + +---------------+---------------+---------------+---------------+ + 24| CmdSN | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32/ SCSI Command Descriptor Block (CDB) / + +/ / + +---------------+---------------+---------------+---------------+ + 48/ AHS (Optional) / + +---------------+---------------+---------------+---------------+ + x/ Header Digest (Optional) / + +---------------+---------------+---------------+---------------+ + y/ (DataSegment, Command Data) (Optional) / + +/ / + +---------------+---------------+---------------+---------------+ + z/ Data Digest (Optional) / + +---------------+---------------+---------------+---------------+ + + + + +Satran, et al. Standards Track [Page 119] + +RFC 3720 iSCSI April 2004 + + +10.3.1. Flags and Task Attributes (byte 1) + + The flags for a SCSI Command are: + + bit 0 (F) is set to 1 when no unsolicited SCSI Data-Out PDUs follow + this PDU. When F=1 for a write and if Expected Data + Transfer Length is larger than the DataSegmentLength, the + target may solicit additional data through R2T. + + bit 1 (R) is set to 1 when the command is expected to input data. + + bit 2 (W) is set to 1 when the command is expected to output data. + + bit 3-4 Reserved. + + bit 5-7 contains Task Attributes. + + Task Attributes (ATTR) have one of the following integer values (see + [SAM2] for details): + + 0 - Untagged + 1 - Simple + 2 - Ordered + 3 - Head of Queue + 4 - ACA + 5-7 - Reserved + + Setting both the W and the F bit to 0 is an error. Either or both of + R and W MAY be 1 when either the Expected Data Transfer Length and/or + Bidirectional Read Expected Data Transfer Length are 0, but they MUST + NOT both be 0 when the Expected Data Transfer Length and/or + Bidirectional Read Expected Data Transfer Length are not 0 (i.e., + when some data transfer is expected the transfer direction is + indicated by the R and/or W bit). + +10.3.2. CmdSN - Command Sequence Number + + Enables ordered delivery across multiple connections in a single + session. + +10.3.3. ExpStatSN + + Command responses up to ExpStatSN-1 (mod 2**32) have been received + (acknowledges status) on the connection. + + + + + + + +Satran, et al. Standards Track [Page 120] + +RFC 3720 iSCSI April 2004 + + +10.3.4. Expected Data Transfer Length + + For unidirectional operations, the Expected Data Transfer Length + field contains the number of bytes of data involved in this SCSI + operation. For a unidirectional write operation (W flag set to 1 and + R flag set to 0), the initiator uses this field to specify the number + of bytes of data it expects to transfer for this operation. For a + unidirectional read operation (W flag set to 0 and R flag set to 1), + the initiator uses this field to specify the number of bytes of data + it expects the target to transfer to the initiator. It corresponds + to the SAM2 byte count. + + For bidirectional operations (both R and W flags are set to 1), this + field contains the number of data bytes involved in the write + transfer. For bidirectional operations, an additional header segment + MUST be present in the header sequence that indicates the + Bidirectional Read Expected Data Transfer Length. The Expected Data + Transfer Length field and the Bidirectional Read Expected Data + Transfer Length field correspond to the SAM2 byte count + + If the Expected Data Transfer Length for a write and the length of + the immediate data part that follows the command (if any) are the + same, then no more data PDUs are expected to follow. In this case, + the F bit MUST be set to 1. + + If the Expected Data Transfer Length is higher than the + FirstBurstLength (the negotiated maximum amount of unsolicited data + the target will accept), the initiator MUST send the maximum amount + of unsolicited data OR ONLY the immediate data, if any. + + Upon completion of a data transfer, the target informs the initiator + (through residual counts) of how many bytes were actually processed + (sent and/or received) by the target. + +10.3.5. CDB - SCSI Command Descriptor Block + + There are 16 bytes in the CDB field to accommodate the commonly used + CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS + MUST be used to contain the CDB spillover. + +10.3.6. Data Segment - Command Data + + Some SCSI commands require additional parameter data to accompany the + SCSI command. This data may be placed beyond the boundary of the + iSCSI header in a data segment. Alternatively, user data (e.g., from + a WRITE operation) can be placed in the data segment (both cases are + + + + + +Satran, et al. Standards Track [Page 121] + +RFC 3720 iSCSI April 2004 + + + referred to as immediate data). These data are governed by the rules + for solicited vs. unsolicited data outlined in Section 3.2.4.2 Data + Transfer Overview. + +10.4. SCSI Response + + The format of the SCSI Response PDU is: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x21 |1|. .|o|u|O|U|.| Response | Status | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| SNACK Tag or Reserved | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| ExpDataSN or Reserved | + +---------------+---------------+---------------+---------------+ + 40| Bidirectional Read Residual Count or Reserved | + +---------------+---------------+---------------+---------------+ + 44| Residual Count or Reserved | + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / Data Segment (Optional) / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + + + + + + + +Satran, et al. Standards Track [Page 122] + +RFC 3720 iSCSI April 2004 + + +10.4.1. Flags (byte 1) + + bit 1-2 Reserved. + + bit 3 - (o) set for Bidirectional Read Residual Overflow. In this + case, the Bidirectional Read Residual Count indicates the number + of bytes that were not transferred to the initiator because the + initiator's Expected Bidirectional Read Data Transfer Length was + not sufficient. + + bit 4 - (u) set for Bidirectional Read Residual Underflow. In this + case, the Bidirectional Read Residual Count indicates the number + of bytes that were not transferred to the initiator out of the + number of bytes expected to be transferred. + + bit 5 - (O) set for Residual Overflow. In this case, the Residual + Count indicates the number of bytes that were not transferred + because the initiator's Expected Data Transfer Length was not + sufficient. For a bidirectional operation, the Residual Count + contains the residual for the write operation. + + bit 6 - (U) set for Residual Underflow. In this case, the Residual + Count indicates the number of bytes that were not transferred out + of the number of bytes that were expected to be transferred. For + a bidirectional operation, the Residual Count contains the + residual for the write operation. + + bit 7 - (0) Reserved. + + Bits O and U and bits o and u are mutually exclusive (i.e., having + both o and u or O and U set to 1 is a protocol error). For a + response other than "Command Completed at Target", bits 3-6 MUST be + 0. + +10.4.2. Status + + The Status field is used to report the SCSI status of the command (as + specified in [SAM2]) and is only valid if the Response Code is + Command Completed at target. + + + + + + + + + + + + +Satran, et al. Standards Track [Page 123] + +RFC 3720 iSCSI April 2004 + + + Some of the status codes defined in [SAM2] are: + + 0x00 GOOD + 0x02 CHECK CONDITION + 0x08 BUSY + 0x18 RESERVATION CONFLICT + 0x28 TASK SET FULL + 0x30 ACA ACTIVE + 0x40 TASK ABORTED + + See [SAM2] for the complete list and definitions. + + If a SCSI device error is detected while data from the initiator is + still expected (the command PDU did not contain all the data and the + target has not received a Data PDU with the final bit Set), the + target MUST wait until it receives a Data PDU with the F bit set in + the last expected sequence before sending the Response PDU. + +10.4.3. Response + + This field contains the iSCSI service response. + + iSCSI service response codes defined in this specification are: + + 0x00 - Command Completed at Target + 0x01 - Target Failure + 0x80-0xff - Vendor specific + + All other response codes are reserved. + + The Response is used to report a Service Response. The mapping of + the response code into a SCSI service response code value, if needed, + is outside the scope of this document. However, in symbolic terms + response value 0x00 maps to the SCSI service response (see [SAM2] and + [SPC3]) of TASK COMPLETE or LINKED COMMAND COMPLETE. All other + Response values map to the SCSI service response of SERVICE DELIVERY + OR TARGET FAILURE. + + If a PDU that includes SCSI status (Response PDU or Data-In PDU + including status) does not arrive before the session is terminated, + the SCSI service response is SERVICE DELIVERY OR TARGET FAILURE. + + A non-zero Response field indicates a failure to execute the command + in which case the Status and Flag fields are undefined. + + + + + + + +Satran, et al. Standards Track [Page 124] + +RFC 3720 iSCSI April 2004 + + +10.4.4. SNACK Tag + + This field contains a copy of the SNACK Tag of the last SNACK Tag + accepted by the target on the same connection and for the command for + which the response is issued. Otherwise it is reserved and should be + set to 0. + + After issuing a R-Data SNACK the initiator must discard any SCSI + status unless contained in an SCSI Response PDU carrying the same + SNACK Tag as the last issued R-Data SNACK for the SCSI command on the + current connection. + + For a detailed discussion on R-Data SNACK see Section 10.16 SNACK + Request. + +10.4.5. Residual Count + + The Residual Count field MUST be valid in the case where either the U + bit or the O bit is set. If neither bit is set, the Residual Count + field is reserved. Targets may set the residual count and initiators + may use it when the response code is "completed at target" (even if + the status returned is not GOOD). If the O bit is set, the Residual + Count indicates the number of bytes that were not transferred because + the initiator's Expected Data Transfer Length was not sufficient. If + the U bit is set, the Residual Count indicates the number of bytes + that were not transferred out of the number of bytes expected to be + transferred. + +10.4.6. Bidirectional Read Residual Count + + The Bidirectional Read Residual Count field MUST be valid in the case + where either the u bit or the o bit is set. If neither bit is set, + the Bidirectional Read Residual Count field is reserved. Targets may + set the Bidirectional Read Residual Count and initiators may use it + when the response code is "completed at target". If the o bit is + set, the Bidirectional Read Residual Count indicates the number of + bytes that were not transferred to the initiator because the + initiator's Expected Bidirectional Read Transfer Length was not + sufficient. If the u bit is set, the Bidirectional Read Residual + Count indicates the number of bytes that were not transferred to the + initiator out of the number of bytes expected to be transferred. + +10.4.7. Data Segment - Sense and Response Data Segment + + iSCSI targets MUST support and enable autosense. If Status is CHECK + CONDITION (0x02), then the Data Segment MUST contain sense data for + the failed command. + + + + +Satran, et al. Standards Track [Page 125] + +RFC 3720 iSCSI April 2004 + + + For some iSCSI responses, the response data segment MAY contain some + response related information, (e.g., for a target failure, it may + contain a vendor specific detailed description of the failure). + + If the DataSegmentLength is not 0, the format of the Data Segment is + as follows: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|SenseLength | Sense Data | + +---------------+---------------+---------------+---------------+ + x/ Sense Data / + +---------------+---------------+---------------+---------------+ + y/ Response Data / + / / + +---------------+---------------+---------------+---------------+ + z| + +10.4.7.1. SenseLength + + Length of Sense Data. + +10.4.7.2. Sense Data + + The Sense Data contains detailed information about a check condition + and [SPC3] specifies the format and content of the Sense Data. + + Certain iSCSI conditions result in the command being terminated at + the target (response Command Completed at Target) with a SCSI Check + Condition Status as outlined in the next table: + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 126] + +RFC 3720 iSCSI April 2004 + + + +--------------------------+----------+---------------------------+ + | iSCSI Condition |Sense | Additional Sense Code & | + | |Key | Qualifier | + +--------------------------+----------+---------------------------+ + | Unexpected unsolicited |Aborted | ASC = 0x0c ASCQ = 0x0c | + | data |Command-0B| Write Error | + +--------------------------+----------+---------------------------+ + | Incorrect amount of data |Aborted | ASC = 0x0c ASCQ = 0x0d | + | |Command-0B| Write Error | + +--------------------------+----------+---------------------------+ + | Protocol Service CRC |Aborted | ASC = 0x47 ASCQ = 0x05 | + | error |Command-0B| CRC Error Detected | + +--------------------------+----------+---------------------------+ + | SNACK rejected |Aborted | ASC = 0x11 ASCQ = 0x13 | + | |Command-0B| Read Error | + +--------------------------+----------+---------------------------+ + + The target reports the "Incorrect amount of data" condition if during + data output the total data length to output is greater than + FirstBurstLength and the initiator sent unsolicited non-immediate + data but the total amount of unsolicited data is different than + FirstBurstLength. The target reports the same error when the amount + of data sent as a reply to an R2T does not match the amount + requested. + +10.4.8. ExpDataSN + + The number of R2T and Data-In (read) PDUs the target has sent for the + command. + + This field MUST be 0 if the response code is not Command Completed at + Target or the target sent no Data-In PDUs for the command. + +10.4.9. StatSN - Status Sequence Number + + StatSN is a Sequence Number that the target iSCSI layer generates per + connection and that in turn, enables the initiator to acknowledge + status reception. StatSN is incremented by 1 for every + response/status sent on a connection except for responses sent as a + result of a retry or SNACK. In the case of responses sent due to a + retransmission request, the StatSN MUST be the same as the first time + the PDU was sent unless the connection has since been restarted. + + + + + + + + + +Satran, et al. Standards Track [Page 127] + +RFC 3720 iSCSI April 2004 + + +10.4.10. ExpCmdSN - Next Expected CmdSN from this Initiator + + ExpCmdSN is a Sequence Number that the target iSCSI returns to the + initiator to acknowledge command reception. It is used to update a + local variable with the same name. An ExpCmdSN equal to MaxCmdSN+1 + indicates that the target cannot accept new commands. + +10.4.11. MaxCmdSN - Maximum CmdSN from this Initiator + + MaxCmdSN is a Sequence Number that the target iSCSI returns to the + initiator to indicate the maximum CmdSN the initiator can send. It + is used to update a local variable with the same name. If MaxCmdSN + is equal to ExpCmdSN-1, this indicates to the initiator that the + target cannot receive any additional commands. When MaxCmdSN changes + at the target while the target has no pending PDUs to convey this + information to the initiator, it MUST generate a NOP-IN to carry the + new MaxCmdSN. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 128] + +RFC 3720 iSCSI April 2004 + + +10.5. Task Management Function Request + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| 0x02 |1| Function | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| Logical Unit Number (LUN) or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Referenced Task Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| CmdSN | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32| RefCmdSN or Reserved | + +---------------+---------------+---------------+---------------+ + 36| ExpDataSN or Reserved | + +---------------+---------------+---------------+---------------+ + 40/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + +10.5.1. Function + + The Task Management functions provide an initiator with a way to + explicitly control the execution of one or more Tasks (SCSI and iSCSI + tasks). The Task Management function codes are listed below. For a + more detailed description of SCSI task management, see [SAM2]. + + 1 - ABORT TASK - aborts the task identified by the Referenced Task + Tag field. + + 2 - ABORT TASK SET - aborts all Tasks issued via this session on the + logical unit. + + 3 - CLEAR ACA - clears the Auto Contingent Allegiance condition. + + + + + +Satran, et al. Standards Track [Page 129] + +RFC 3720 iSCSI April 2004 + + + 4 - CLEAR TASK SET - aborts all Tasks in the appropriate task set as + defined by the TST field in the Control mode page (see [SPC3]). + + 5 - LOGICAL UNIT RESET + + 6 - TARGET WARM RESET + + 7 - TARGET COLD RESET + + 8 - TASK REASSIGN - reassigns connection allegiance for the task + identified by the Referenced Task Tag field to this connection, + thus resuming the iSCSI exchanges for the task. + + For all these functions, the Task Management function response MUST + be returned as detailed in Section 10.6 Task Management Function + Response. All these functions apply to the referenced tasks + regardless of whether they are proper SCSI tasks or tagged iSCSI + operations. Task management requests must act on all the commands + from the same session having a CmdSN lower than the task management + CmdSN. LOGICAL UNIT RESET, TARGET WARM RESET and TARGET COLD RESET + may affect commands from other sessions or commands from the same + session with CmdSN equal or exceeding CmdSN. + + If the task management request is marked for immediate delivery, it + must be considered immediately for execution, but the operations + involved (all or part of them) may be postponed to allow the target + to receive all relevant tasks. According to [SAM2], for all the + tasks covered by the Task Management response (i.e., with CmdSN lower + than the task management command CmdSN) but except the Task + Management response to a TASK REASSIGN, additional responses MUST NOT + be delivered to the SCSI layer after the Task Management response. + The iSCSI initiator MAY deliver to the SCSI layer all responses + received before the Task Management response (i.e., it is a matter of + implementation if the SCSI responses, received before the Task + Management response but after the task management request was issued, + are delivered to the SCSI layer by the iSCSI layer in the initiator). + The iSCSI target MUST ensure that no responses for the tasks covered + by a task management function are delivered to the iSCSI initiator + after the Task Management response except for a task covered by a + TASK REASSIGN. + + For ABORT TASK SET and CLEAR TASK SET, the issuing initiator MUST + continue to respond to all valid target transfer tags (received via + R2T, Text Response, NOP-In, or SCSI Data-In PDUs) related to the + affected task set, even after issuing the task management request. + The issuing initiator SHOULD however terminate (i.e., by setting the + F-bit to 1) these response sequences as quickly as possible. The + target on its part MUST wait for responses on all affected target + + + +Satran, et al. Standards Track [Page 130] + +RFC 3720 iSCSI April 2004 + + + transfer tags before acting on either of these two task management + requests. In case all or part of the response sequence is not + received (due to digest errors) for a valid TTT, the target MAY treat + it as a case of within-command error recovery class (see Section + 6.1.4.1 Recovery Within-command) if it is supporting + ErrorRecoveryLevel >= 1, or alternatively may drop the connection to + complete the requested task set function. + + If an ABORT TASK is issued for a task created by an immediate command + then RefCmdSN MUST be that of the Task Management request itself + (i.e., CmdSN and RefCmdSN are equal); otherwise RefCmdSN MUST be set + to the CmdSN of the task to be aborted (lower than CmdSN). + + If the connection is still active (it is not undergoing an implicit + or explicit logout), ABORT TASK MUST be issued on the same connection + to which the task to be aborted is allegiant at the time the Task + Management Request is issued. If the connection is implicitly or + explicitly logged out (i.e., no other request will be issued on the + failing connection and no other response will be received on the + failing connection), then an ABORT TASK function request may be + issued on another connection. This Task Management request will then + establish a new allegiance for the command to be aborted as well as + abort it (i.e., the task to be aborted will not have to be retried or + reassigned, and its status, if issued but not acknowledged, will be + reissued followed by the Task Management response). + + At the target an ABORT TASK function MUST NOT be executed on a Task + Management request; such a request MUST result in Task Management + response of "Function rejected". + + For the LOGICAL UNIT RESET function, the target MUST behave as + dictated by the Logical Unit Reset function in [SAM2]. + + The implementation of the TARGET WARM RESET function and the TARGET + COLD RESET function is OPTIONAL and when implemented, should act as + described below. The TARGET WARM RESET is also subject to SCSI + access controls on the requesting initiator as defined in [SPC3]. + When authorization fails at the target, the appropriate response as + described in Section 10.6 Task Management Function Response MUST be + returned by the target. The TARGET COLD RESET function is not + subject to SCSI access controls, but its execution privileges may be + managed by iSCSI mechanisms such as login authentication. + + When executing the TARGET WARM RESET and TARGET COLD RESET functions, + the target cancels all pending operations on all Logical Units known + by the issuing initiator. Both functions are equivalent to the + Target Reset function specified by [SAM2]. They can affect many + other initiators logged in with the servicing SCSI target port. + + + +Satran, et al. Standards Track [Page 131] + +RFC 3720 iSCSI April 2004 + + + The target MUST treat the TARGET COLD RESET function additionally as + a power on event, thus terminating all of its TCP connections to all + initiators (all sessions are terminated). For this reason, the + Service Response (defined by [SAM2]) for this SCSI task management + function may not be reliably delivered to the issuing initiator port. + + For the TASK REASSIGN function, the target should reassign the + connection allegiance to this new connection (and thus resume iSCSI + exchanges for the task). TASK REASSIGN MUST ONLY be received by the + target after the connection on which the command was previously + executing has been successfully logged-out. The Task Management + response MUST be issued before the reassignment becomes effective. + For additional usage semantics see Section 6.2 Retry and Reassign in + Recovery. + + At the target a TASK REASSIGN function request MUST NOT be executed + to reassign the connection allegiance of a Task Management function + request, an active text negotiation task, or a Logout task; such a + request MUST result in Task Management response of "Function + rejected". + + TASK REASSIGN MUST be issued as an immediate command. + +10.5.2. TotalAHSLength and DataSegmentLength + + For this PDU TotalAHSLength and DataSegmentLength MUST be 0. + +10.5.3. LUN + + This field is required for functions that address a specific LU + (ABORT TASK, CLEAR TASK SET, ABORT TASK SET, CLEAR ACA, LOGICAL UNIT + RESET) and is reserved in all others. + +10.5.4. Referenced Task Tag + + The Initiator Task Tag of the task to be aborted for the ABORT TASK + function or reassigned for the TASK REASSIGN function. For all the + other functions this field MUST be set to the reserved value + 0xffffffff. + +10.5.5. RefCmdSN + + If an ABORT TASK is issued for a task created by an immediate command + then RefCmdSN MUST be that of the Task Management request itself + (i.e., CmdSN and RefCmdSN are equal). + + + + + + +Satran, et al. Standards Track [Page 132] + +RFC 3720 iSCSI April 2004 + + + For an ABORT TASK of a task created by non-immediate command RefCmdSN + MUST be set to the CmdSN of the task identified by the Referenced + Task Tag field. Targets must use this field as described in section + 10.6.1 when the task identified by the Referenced Task Tag field is + not with the target. + + Otherwise, this field is reserved. + +10.5.6. ExpDataSN + + For recovery purposes, the iSCSI target and initiator maintain a data + acknowledgement reference number - the first input DataSN number + unacknowledged by the initiator. When issuing a new command, this + number is set to 0. If the function is TASK REASSIGN, which + establishes a new connection allegiance for a previously issued Read + or Bidirectional command, ExpDataSN will contain an updated data + acknowledgement reference number or the value 0; the latter + indicating that the data acknowledgement reference number is + unchanged. The initiator MUST discard any data PDUs from the + previous execution that it did not acknowledge and the target MUST + transmit all Data-In PDUs (if any) starting with the data + acknowledgement reference number. The number of retransmitted PDUs + may or may not be the same as the original transmission depending on + if there was a change in MaxRecvDataSegmentLength in the + reassignment. The target MAY also send no more Data-In PDUs if all + data has been acknowledged. + + The value of ExpDataSN MUST be 0 or higher than the DataSN of the + last acknowledged Data-In PDU, but not larger than DataSN+1 of the + last Data-In PDU sent by the target. Any other value MUST be ignored + by the target. + + For other functions this field is reserved. + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 133] + +RFC 3720 iSCSI April 2004 + + +10.6. Task Management Function Response + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x22 |1| Reserved | Response | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------------------------------------------------------+ + 8/ Reserved / + / / + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Reserved | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + For the functions ABORT TASK, ABORT TASK SET, CLEAR ACA, CLEAR TASK + SET, LOGICAL UNIT RESET, TARGET COLD RESET, TARGET WARM RESET and + TASK REASSIGN, the target performs the requested Task Management + function and sends a Task Management response back to the initiator. + For TASK REASSIGN, the new connection allegiance MUST ONLY become + effective at the target after the target issues the Task Management + Response. + +10.6.1. Response + + The target provides a Response, which may take on the following + values: + + a) 0 - Function complete. + b) 1 - Task does not exist. + c) 2 - LUN does not exist. + d) 3 - Task still allegiant. + e) 4 - Task allegiance reassignment not supported. + + + + +Satran, et al. Standards Track [Page 134] + +RFC 3720 iSCSI April 2004 + + + f) 5 - Task management function not supported. + g) 6 - Function authorization failed. + h) 255 - Function rejected. + + All other values are reserved. + + For a discussion on usage of response codes 3 and 4, see Section + 6.2.2 Allegiance Reassignment. + + For the TARGET COLD RESET and TARGET WARM RESET functions, the target + cancels all pending operations across all Logical Units known to the + issuing initiator. For the TARGET COLD RESET function, the target + MUST then close all of its TCP connections to all initiators + (terminates all sessions). + + The mapping of the response code into a SCSI service response code + value, if needed, is outside the scope of this document. However, in + symbolic terms Response values 0 and 1 map to the SCSI service + response of FUNCTION COMPLETE. All other Response values map to the + SCSI service response of FUNCTION REJECTED. If a Task Management + function response PDU does not arrive before the session is + terminated, the SCSI service response is SERVICE DELIVERY OR TARGET + FAILURE. + + The response to ABORT TASK SET and CLEAR TASK SET MUST only be issued + by the target after all of the commands affected have been received + by the target, the corresponding task management functions have been + executed by the SCSI target, and the delivery of all responses + delivered until the task management function completion have been + confirmed (acknowledged through ExpStatSN) by the initiator on all + connections of this session. For the exact timeline of events, refer + to Section 10.6.2 Task Management Actions on Task Sets. + + For the ABORT TASK function, + + a) If the Referenced Task Tag identifies a valid task leading to + a successful termination, then targets must return the + "Function complete" response. + b) If the Referenced Task Tag does not identify an existing task, + but if the CmdSN indicated by the RefCmdSN field in the Task + Management function request is within the valid CmdSN window + and less than the CmdSN of the Task Management function + request itself, then targets must consider the CmdSN received + and return the "Function complete" response. + + + + + + + +Satran, et al. Standards Track [Page 135] + +RFC 3720 iSCSI April 2004 + + + c) If the Referenced Task Tag does not identify an existing task + and if the CmdSN indicated by the RefCmdSN field in the Task + Management function request is outside the valid CmdSN window, + then targets must return the "Task does not exist" response. + +10.6.2. Task Management Actions on Task Sets + + The execution of ABORT TASK SET and CLEAR TASK SET Task Management + function requests consists of the following sequence of events in the + specified order on each of the entities. + + The initiator: + + a) Issues ABORT TASK SET/CLEAR TASK SET request. + b) Continues to respond to each target transfer tag received + for the affected task set. + c) Receives any responses for the tasks in the affected task + set (may process them as usual because they are guaranteed + to be valid). + d) Receives the task set management response, thus concluding + all the tasks in the affected task set. + + The target: + + a) Receives the ABORT TASK SET/CLEAR TASK SET request. + b) Waits for all target transfer tags to be responded to and + for all affected tasks in the task set to be received. + c) Propagates the command to and receives the response from the + target SCSI layer. + d) Takes note of last-sent StatSN on each of the connections in + the iSCSI sessions (one or more) sharing the affected task + set, and waits for acknowledgement of each StatSN (may + solicit for acknowledgement by way of a NOP-In). If some + tasks originate from non-iSCSI I_T_L nexi then the means by + which the target insures that all affected tasks have + returned their status to the initiator are defined by the + specific protocol. + + e) Sends the task set management response to the issuing + initiator. + + + + + + + + + + + +Satran, et al. Standards Track [Page 136] + +RFC 3720 iSCSI April 2004 + + +10.6.3. TotalAHSLength and DataSegmentLength + + For this PDU TotalAHSLength and DataSegmentLength MUST be 0. + +10.7. SCSI Data-Out & SCSI Data-In + + The SCSI Data-Out PDU for WRITE operations has the following format: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x05 |F| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| Reserved | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32| Reserved | + +---------------+---------------+---------------+---------------+ + 36| DataSN | + +---------------+---------------+---------------+---------------+ + 40| Buffer Offset | + +---------------+---------------+---------------+---------------+ + 44| Reserved | + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + + + + + + + +Satran, et al. Standards Track [Page 137] + +RFC 3720 iSCSI April 2004 + + + The SCSI Data-In PDU for READ operations has the following format: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x25 |F|A|0 0 0|O|U|S| Reserved |Status or Rsvd | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| StatSN or Reserved | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| DataSN | + +---------------+---------------+---------------+---------------+ + 40| Buffer Offset | + +---------------+---------------+---------------+---------------+ + 44| Residual Count | + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + Status can accompany the last Data-In PDU if the command did not end + with an exception (i.e., the status is "good status" - GOOD, + CONDITION MET or INTERMEDIATE CONDITION MET). The presence of status + (and of a residual count) is signaled though the S flag bit. + Although targets MAY choose to send even non-exception status in + separate responses, initiators MUST support non-exception status in + Data-In PDUs. + + + + + + +Satran, et al. Standards Track [Page 138] + +RFC 3720 iSCSI April 2004 + + +10.7.1. F (Final) Bit + + For outgoing data, this bit is 1 for the last PDU of unsolicited data + or the last PDU of a sequence that answers an R2T. + + For incoming data, this bit is 1 for the last input (read) data PDU + of a sequence. Input can be split into several sequences, each + having its own F bit. Splitting the data stream into sequences does + not affect DataSN counting on Data-In PDUs. It MAY be used as a + "change direction" indication for Bidirectional operations that need + such a change. + + DataSegmentLength MUST not exceed MaxRecvDataSegmentLength for the + direction it is sent and the total of all the DataSegmentLength of + all PDUs in a sequence MUST not exceed MaxBurstLength (or + FirstBurstLength for unsolicited data). However the number of + individual PDUs in a sequence (or in total) may be higher than the + MaxBurstLength (or FirstBurstLength) to MaxRecvDataSegmentLength + ratio (as PDUs may be limited in length by the sender capabilities). + Using DataSegmentLength of 0 may increase beyond what is reasonable + for the number of PDUs and should therefore be avoided. + + For Bidirectional operations, the F bit is 1 for both the end of the + input sequences and the end of the output sequences. + +10.7.2. A (Acknowledge) Bit + + For sessions with ErrorRecoveryLevel 1 or higher, the target sets + this bit to 1 to indicate that it requests a positive acknowledgement + from the initiator for the data received. The target should use the + A bit moderately; it MAY only set the A bit to 1 once every + MaxBurstLength bytes, or on the last Data-In PDU that concludes the + entire requested read data transfer for the task from the target's + perspective, and it MUST NOT do so more frequently. The target MUST + NOT set to 1 the A bit for sessions with ErrorRecoveryLevel=0. The + initiator MUST ignore the A bit set to 1 for sessions with + ErrorRecoveryLevel=0. + + On receiving a Data-In PDU with the A bit set to 1 on a session with + ErrorRecoveryLevel greater than 0, if there are no holes in the read + data until that Data-In PDU, the initiator MUST issue a SNACK of type + DataACK except when it is able to acknowledge the status for the task + immediately via ExpStatSN on other outbound PDUs if the status for + the task is also received. In the latter case (acknowledgement + through ExpStatSN), sending a SNACK of type DataACK in response to + the A bit is OPTIONAL, but if it is done, it must not be sent after + the status acknowledgement through ExpStatSN. If the initiator has + detected holes in the read data prior to that Data-In PDU, it MUST + + + +Satran, et al. Standards Track [Page 139] + +RFC 3720 iSCSI April 2004 + + + postpone issuing the SNACK of type DataACK until the holes are + filled. An initiator also MUST NOT acknowledge the status for the + task before those holes are filled. A status acknowledgement for a + task that generated the Data-In PDUs is considered by the target as + an implicit acknowledgement of the Data-In PDUs if such an + acknowledgement was requested by the target. + +10.7.3. Flags (byte 1) + + The last SCSI Data packet sent from a target to an initiator for a + SCSI command that completed successfully (with a status of GOOD, + CONDITION MET, INTERMEDIATE or INTERMEDIATE CONDITION MET) may also + optionally contain the Status for the data transfer. As Sense Data + cannot be sent together with the Command Status, if the command is + completed with an error, then the response and sense data MUST be + sent in a SCSI Response PDU (i.e., MUST NOT be sent in a SCSI Data + packet). If Status is sent with the data, then a SCSI Response PDU + MUST NOT be sent as this would violate SCSI rules (a single status). + For Bidirectional commands, the status MUST be sent in a SCSI + Response PDU. + + bit 2-4 - Reserved. + + bit 5-6 - used the same as in a SCSI Response. These bits are + only valid when S is set to 1. For details see Section + 10.4.1 Flags (byte 1). + + bit 7 S (status)- set to indicate that the Command Status field + contains status. If this bit is set to 1, the F bit + MUST also be set to 1. + + The fields StatSN, Status, and Residual Count only have meaningful + content if the S bit is set to 1 and their values are defined in + Section 10.4 SCSI Response. + +10.7.4. Target Transfer Tag and LUN + + On outgoing data, the Target Transfer Tag is provided to the target + if the transfer is honoring an R2T. In this case, the Target + Transfer Tag field is a replica of the Target Transfer Tag provided + with the R2T. + + On incoming data, the Target Transfer Tag and LUN MUST be provided by + the target if the A bit is set to 1; otherwise they are reserved. + The Target Transfer Tag and LUN are copied by the initiator into the + SNACK of type DataACK that it issues as a result of receiving a SCSI + Data-In PDU with the A bit set to 1. + + + + +Satran, et al. Standards Track [Page 140] + +RFC 3720 iSCSI April 2004 + + + The Target Transfer Tag values are not specified by this protocol + except that the value 0xffffffff is reserved and means that the + Target Transfer Tag is not supplied. If the Target Transfer Tag is + provided, then the LUN field MUST hold a valid value and be + consistent with whatever was specified with the command; otherwise, + the LUN field is reserved. + +10.7.5. DataSN + + For input (read) or bidirectional Data-In PDUs, the DataSN is the + input PDU number within the data transfer for the command identified + by the Initiator Task Tag. + + R2T and Data-In PDUs, in the context of bidirectional commands, share + the numbering sequence (see Section 3.2.2.3 Data Sequencing). + + For output (write) data PDUs, the DataSN is the Data-Out PDU number + within the current output sequence. The current output sequence is + either identified by the Initiator Task Tag (for unsolicited data) or + is a data sequence generated for one R2T (for data solicited through + R2T). + +10.7.6. Buffer Offset + + The Buffer Offset field contains the offset of this PDU payload data + within the complete data transfer. The sum of the buffer offset and + length should not exceed the expected transfer length for the + command. + + The order of data PDUs within a sequence is determined by + DataPDUInOrder. When set to Yes, it means that PDUs have to be in + increasing Buffer Offset order and overlays are forbidden. + + The ordering between sequences is determined by DataSequenceInOrder. + When set to Yes, it means that sequences have to be in increasing + Buffer Offset order and overlays are forbidden. + +10.7.7. DataSegmentLength + + This is the data payload length of a SCSI Data-In or SCSI Data-Out + PDU. The sending of 0 length data segments should be avoided, but + initiators and targets MUST be able to properly receive 0 length data + segments. + + The Data Segments of Data-In and Data-Out PDUs SHOULD be filled to + the integer number of 4 byte words (real payload) unless the F bit is + set to 1. + + + + +Satran, et al. Standards Track [Page 141] + +RFC 3720 iSCSI April 2004 + + +10.8. Ready To Transfer (R2T) + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x31 |1| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| R2TSN | + +---------------+---------------+---------------+---------------+ + 40| Buffer Offset | + +---------------+---------------+---------------+---------------+ + 44| Desired Data Transfer Length | + +---------------------------------------------------------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + When an initiator has submitted a SCSI Command with data that passes + from the initiator to the target (WRITE), the target may specify + which blocks of data it is ready to receive. The target may request + that the data blocks be delivered in whichever order is convenient + for the target at that particular instant. This information is + passed from the target to the initiator in the Ready To Transfer + (R2T) PDU. + + In order to allow write operations without an explicit initial R2T, + the initiator and target MUST have negotiated the key InitialR2T to + No during Login. + + An R2T MAY be answered with one or more SCSI Data-Out PDUs with a + matching Target Transfer Tag. If an R2T is answered with a single + Data-Out PDU, the Buffer Offset in the Data PDU MUST be the same as + + + +Satran, et al. Standards Track [Page 142] + +RFC 3720 iSCSI April 2004 + + + the one specified by the R2T, and the data length of the Data PDU + MUST be the same as the Desired Data Transfer Length specified in the + R2T. If the R2T is answered with a sequence of Data PDUs, the Buffer + Offset and Length MUST be within the range of those specified by R2T, + and the last PDU MUST have the F bit set to 1. If the last PDU + (marked with the F bit) is received before the Desired Data Transfer + Length is transferred, a target MAY choose to Reject that + + PDU with "Protocol error" reason code. DataPDUInOrder governs the + Data-Out PDU ordering. If DataPDUInOrder is set to Yes, the Buffer + Offsets and Lengths for consecutive PDUs MUST form a continuous + non-overlapping range and the PDUs MUST be sent in increasing offset + order. + + The target may send several R2T PDUs. It, therefore, can have a + number of pending data transfers. The number of outstanding R2T PDUs + are limited by the value of the negotiated key MaxOutstandingR2T. + Within a connection, outstanding R2Ts MUST be fulfilled by the + initiator in the order in which they were received. + + R2T PDUs MAY also be used to recover Data Out PDUs. Such an R2T + (Recovery-R2T) is generated by a target upon detecting the loss of + one or more Data-Out PDUs due to: + + - Digest error + - Sequence error + - Sequence reception timeout + + A Recovery-R2T carries the next unused R2TSN, but requests part of or + the entire data burst that an earlier R2T (with a lower R2TSN) had + already requested. + + DataSequenceInOrder governs the buffer offset ordering in consecutive + R2Ts. If DataSequenceInOrder is Yes, then consecutive R2Ts MUST + refer to continuous non-overlapping ranges except for Recovery-R2Ts. + +10.8.1. TotalAHSLength and DataSegmentLength + + For this PDU TotalAHSLength and DataSegmentLength MUST be 0. + +10.8.2. R2TSN + + R2TSN is the R2T PDU input PDU number within the command identified + by the Initiator Task Tag. + + For bidirectional commands R2T and Data-In PDUs share the input PDU + numbering sequence (see Section 3.2.2.3 Data Sequencing). + + + + +Satran, et al. Standards Track [Page 143] + +RFC 3720 iSCSI April 2004 + + +10.8.3. StatSN + + The StatSN field will contain the next StatSN. The StatSN for this + connection is not advanced after this PDU is sent. + +10.8.4. Desired Data Transfer Length and Buffer Offset + + The target specifies how many bytes it wants the initiator to send + because of this R2T PDU. The target may request the data from the + initiator in several chunks, not necessarily in the original order of + the data. The target, therefore, also specifies a Buffer Offset that + indicates the point at which the data transfer should begin, relative + to the beginning of the total data transfer. The Desired Data + Transfer Length MUST NOT be 0 and MUST not exceed MaxBurstLength. + +10.8.5. Target Transfer Tag + + The target assigns its own tag to each R2T request that it sends to + the initiator. This tag can be used by the target to easily identify + the data it receives. The Target Transfer Tag and LUN are copied in + the outgoing data PDUs and are only used by the target. There is no + protocol rule about the Target Transfer Tag except that the value + 0xffffffff is reserved and MUST NOT be sent by a target in an R2T. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 144] + +RFC 3720 iSCSI April 2004 + + +10.9. Asynchronous Message + + An Asynchronous Message may be sent from the target to the initiator + without correspondence to a particular command. The target specifies + the reason for the event and sense data. + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x32 |1| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| 0xffffffff | + +---------------+---------------+---------------+---------------+ + 20| Reserved | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| AsyncEvent | AsyncVCode | Parameter1 or Reserved | + +---------------+---------------+---------------+---------------+ + 40| Parameter2 or Reserved | Parameter3 or Reserved | + +---------------+---------------+---------------+---------------+ + 44| Reserved | + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment - Sense Data and iSCSI Event Data / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + Some Asynchronous Messages are strictly related to iSCSI while others + are related to SCSI [SAM2]. + + StatSN counts this PDU as an acknowledgeable event (StatSN is + advanced), which allows for initiator and target state + synchronization. + + + +Satran, et al. Standards Track [Page 145] + +RFC 3720 iSCSI April 2004 + + +10.9.1. AsyncEvent + + The codes used for iSCSI Asynchronous Messages (events) are: + + 0 - a SCSI Asynchronous Event is reported in the sense data. + Sense Data that accompanies the report, in the data segment, + identifies the condition. The sending of a SCSI Event + (Asynchronous Event Reporting in SCSI terminology) is + dependent on the target support for SCSI asynchronous event + reporting (see [SAM2]) as indicated in the standard INQUIRY + data (see [SPC3]). Its use may be enabled by parameters in + the SCSI Control mode page (see [SPC3]). + + 1 - target requests Logout. This Async Message MUST be sent on + the same connection as the one requesting to be logged out. + The initiator MUST honor this request by issuing a Logout as + early as possible, but no later than Parameter3 seconds. + Initiator MUST send a Logout with a reason code of "Close the + connection" OR "Close the session" to close all the + connections. Once this message is received, the initiator + SHOULD NOT issue new iSCSI commands on the connection to be + logged out. The target MAY reject any new I/O requests that + it receives after this Message with the reason code "Waiting + for Logout". If the initiator does not Logout in Parameter3 + seconds, the target should send an Async PDU with iSCSI event + code "Dropped the connection" if possible, or simply terminate + the transport connection. Parameter1 and Parameter2 are + reserved. + + 2 - target indicates it will drop the connection. The Parameter1 + field indicates the CID of the connection that is going to be + dropped. + + The Parameter2 field (Time2Wait) indicates, in seconds, the + minimum time to wait before attempting to reconnect or + reassign. + + The Parameter3 field (Time2Retain) indicates the maximum time + allowed to reassign commands after the initial wait (in + Parameter2). + + If the initiator does not attempt to reconnect and/or reassign + the outstanding commands within the time specified by + Parameter3, or if Parameter3 is 0, the target will terminate + all outstanding commands on this connection. In this case, no + other responses should be expected from the target for the + outstanding commands on this connection. + + + + +Satran, et al. Standards Track [Page 146] + +RFC 3720 iSCSI April 2004 + + + A value of 0 for Parameter2 indicates that reconnect can be + attempted immediately. + + 3 - target indicates it will drop all the connections of this + session. + + Parameter1 field is reserved. + + The Parameter2 field (Time2Wait) indicates, in seconds, the + minimum time to wait before attempting to reconnect. The + Parameter3 field (Time2Retain) indicates the maximum time + allowed to reassign commands after the initial wait (in + Parameter2). + + If the initiator does not attempt to reconnect and/or reassign + the outstanding commands within the time specified by + Parameter3, or if Parameter3 is 0, the session is terminated. + + In this case, the target will terminate all outstanding + commands in this session; no other responses should be + expected from the target for the outstanding commands in this + session. A value of 0 for Parameter2 indicates that reconnect + can be attempted immediately. + + 4 - target requests parameter negotiation on this connection. The + initiator MUST honor this request by issuing a Text Request + (that can be empty) on the same connection as early as + possible, but no later than Parameter3 seconds, unless a Text + Request is already pending on the connection, or by issuing a + Logout Request. If the initiator does not issue a Text + Request the target may reissue the Asynchronous Message + requesting parameter negotiation. + + 255 - vendor specific iSCSI Event. The AsyncVCode details the + vendor code, and data MAY accompany the report. + + All other event codes are reserved. + +10.9.2. AsyncVCode + + AsyncVCode is a vendor specific detail code that is only valid if the + AsyncEvent field indicates a vendor specific event. Otherwise, it is + reserved. + +10.9.3. LUN + + The LUN field MUST be valid if AsyncEvent is 0. Otherwise, this + field is reserved. + + + +Satran, et al. Standards Track [Page 147] + +RFC 3720 iSCSI April 2004 + + +10.9.4. Sense Data and iSCSI Event Data + + For a SCSI event, this data accompanies the report in the data + segment and identifies the condition. + + For an iSCSI event, additional vendor-unique data MAY accompany the + Async event. Initiators MAY ignore the data when not understood + while processing the rest of the PDU. + + If the DataSegmentLength is not 0, the format of the DataSegment is + as follows: + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|SenseLength | Sense Data | + +---------------+---------------+---------------+---------------+ + x/ Sense Data / + +---------------+---------------+---------------+---------------+ + y/ iSCSI Event Data / + / / + +---------------+---------------+---------------+---------------+ + z| + +10.9.4.1. SenseLength + + This is the length of Sense Data. When the Sense Data field is empty + (e.g., the event is not a SCSI event) SenseLength is 0. + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 148] + +RFC 3720 iSCSI April 2004 + + +10.10. Text Request + + The Text Request is provided to allow for the exchange of information + and for future extensions. It permits the initiator to inform a + target of its capabilities or to request some special operations. + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| 0x04 |F|C| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| CmdSN | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment (Text) / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + An initiator MUST have at most one outstanding Text Request on a + connection at any given time. + + On a connection failure, an initiator must either explicitly abort + any active allegiant text negotiation task or must cause such a task + to be implicitly terminated by the target. + + + + + + + + +Satran, et al. Standards Track [Page 149] + +RFC 3720 iSCSI April 2004 + + +10.10.1. F (Final) Bit + + When set to 1, indicates that this is the last or only text request + in a sequence of Text Requests; otherwise, it indicates that more + Text Requests will follow. + +10.10.2. C (Continue) Bit + + When set to 1, indicates that the text (set of key=value pairs) in + this Text Request is not complete (it will be continued on subsequent + Text Requests); otherwise, it indicates that this Text Request ends a + set of key=value pairs. A Text Request with the C bit set to 1 MUST + have the F bit set to 0. + +10.10.3. Initiator Task Tag + + The initiator assigned identifier for this Text Request. If the + command is sent as part of a sequence of text requests and responses, + the Initiator Task Tag MUST be the same for all the requests within + the sequence (similar to linked SCSI commands). The I bit for all + requests in a sequence also MUST be the same. + +10.10.4. Target Transfer Tag + + When the Target Transfer Tag is set to the reserved value 0xffffffff, + it tells the target that this is a new request and the target resets + any internal state associated with the Initiator Task Tag (resets the + current negotiation state). + + The target sets the Target Transfer Tag in a text response to a value + other than the reserved value 0xffffffff whenever it indicates that + it has more data to send or more operations to perform that are + associated with the specified Initiator Task Tag. It MUST do so + whenever it sets the F bit to 0 in the response. By copying the + Target Transfer Tag from the response to the next Text Request, the + initiator tells the target to continue the operation for the specific + Initiator Task Tag. The initiator MUST ignore the Target Transfer + Tag in the Text Response when the F bit is set to 1. + + This mechanism allows the initiator and target to transfer a large + amount of textual data over a sequence of text-command/text-response + exchanges, or to perform extended negotiation sequences. + + If the Target Transfer Tag is not 0xffffffff, the LUN field MUST be + sent by the target in the Text Response. + + + + + + +Satran, et al. Standards Track [Page 150] + +RFC 3720 iSCSI April 2004 + + + A target MAY reset its internal negotiation state if an exchange is + stalled by the initiator for a long time or if it is running out of + resources. + + Long text responses are handled as in the following example: + + I->T Text SendTargets=All (F=1,TTT=0xffffffff) + T->I Text <part 1> (F=0,TTT=0x12345678) + I->T Text <empty> (F=1, TTT=0x12345678) + T->I Text <part 2> (F=0, TTT=0x12345678) + I->T Text <empty> (F=1, TTT=0x12345678) + ... + T->I Text <part n> (F=1, TTT=0xffffffff) + +10.10.5. Text + + The data lengths of a text request MUST NOT exceed the iSCSI target + MaxRecvDataSegmentLength (a per connection and per direction + negotiated parameter). The text format is specified in Section 5.2 + Text Mode Negotiation. + + Chapter 11 and Chapter 12 list some basic Text key=value pairs, some + of which can be used in Login Request/Response and some in Text + Request/Response. + + A key=value pair can span Text request or response boundaries. A + key=value pair can start in one PDU and continue on the next. In + other words the end of a PDU does not necessarily signal the end of a + key=value pair. + + The target responds by sending its response back to the initiator. + The response text format is similar to the request text format. The + text response MAY refer to key=value pairs presented in an earlier + text request and the text in the request may refer to earlier + responses. + + Chapter 5 details the rules for the Text Requests and Responses. + + Text operations are usually meant for parameter setting/ + negotiations, but can also be used to perform some long lasting + operations. + + Text operations that take a long time should be placed in their own + Text request. + + + + + + + +Satran, et al. Standards Track [Page 151] + +RFC 3720 iSCSI April 2004 + + +10.11. Text Response + + The Text Response PDU contains the target's responses to the + initiator's Text request. The format of the Text field matches that + of the Text request. + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x24 |F|C| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment (Text) / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + +10.11.1. F (Final) Bit + + When set to 1, in response to a Text Request with the Final bit set + to 1, the F bit indicates that the target has finished the whole + operation. Otherwise, if set to 0 in response to a Text Request with + the Final Bit set to 1, it indicates that the target has more work to + do (invites a follow-on text request). A Text Response with the F + bit set to 1 in response to a Text Request with the F bit set to 0 is + a protocol error. + + + +Satran, et al. Standards Track [Page 152] + +RFC 3720 iSCSI April 2004 + + + A Text Response with the F bit set to 1 MUST NOT contain key=value + pairs that may require additional answers from the initiator. + + A Text Response with the F bit set to 1 MUST have a Target Transfer + Tag field set to the reserved value of 0xffffffff. + + A Text Response with the F bit set to 0 MUST have a Target Transfer + Tag field set to a value other than the reserved 0xffffffff. + +10.11.2. C (Continue) Bit + + When set to 1, indicates that the text (set of key=value pairs) in + this Text Response is not complete (it will be continued on + subsequent Text Responses); otherwise, it indicates that this Text + Response ends a set of key=value pairs. A Text Response with the C + bit set to 1 MUST have the F bit set to 0. + +10.11.3. Initiator Task Tag + + The Initiator Task Tag matches the tag used in the initial Text + Request. + +10.11.4. Target Transfer Tag + + When a target has more work to do (e.g., cannot transfer all the + remaining text data in a single Text Response or has to continue the + negotiation) and has enough resources to proceed, it MUST set the + Target Transfer Tag to a value other than the reserved value of + 0xffffffff. Otherwise, the Target Transfer Tag MUST be set to + 0xffffffff. + + When the Target Transfer Tag is not 0xffffffff, the LUN field may be + significant. + + The initiator MUST copy the Target Transfer Tag and LUN in its next + request to indicate that it wants the rest of the data. + + When the target receives a Text Request with the Target Transfer Tag + set to the reserved value of 0xffffffff, it resets its internal + information (resets state) associated with the given Initiator Task + Tag (restarts the negotiation). + + When a target cannot finish the operation in a single Text Response, + and does not have enough resources to continue, it rejects the Text + Request with the appropriate Reject code. + + + + + + +Satran, et al. Standards Track [Page 153] + +RFC 3720 iSCSI April 2004 + + + A target may reset its internal state associated with an Initiator + Task Tag (the current negotiation state), state expressed through the + Target Transfer Tag if the initiator fails to continue the exchange + for some time. The target may reject subsequent Text Requests with + the Target Transfer Tag set to the "stale" value. + +10.11.5. StatSN + + The target StatSN variable is advanced by each Text Response sent. + +10.11.6. Text Response Data + + The data lengths of a text response MUST NOT exceed the iSCSI + initiator MaxRecvDataSegmentLength (a per connection and per + direction negotiated parameter). + + The text in the Text Response Data is governed by the same rules as + the text in the Text Request Data (see Section 10.10.5 Text). + + Although the initiator is the requesting party and controls the + request-response initiation and termination, the target can offer + key=value pairs of its own as part of a sequence and not only in + response to the initiator. + +10.12. Login Request + + After establishing a TCP connection between an initiator and a + target, the initiator MUST start a Login Phase to gain further access + to the target's resources. + + The Login Phase (see Chapter 5) consists of a sequence of Login + Requests and Responses that carry the same Initiator Task Tag. + + Login Requests are always considered as immediate. + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 154] + +RFC 3720 iSCSI April 2004 + + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|1| 0x03 |T|C|.|.|CSG|NSG| Version-max | Version-min | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| ISID | + + +---------------+---------------+ + 12| | TSIH | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| CID | Reserved | + +---------------+---------------+---------------+---------------+ + 24| CmdSN | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN or Reserved | + +---------------+---------------+---------------+---------------+ + 32| Reserved | + +---------------+---------------+---------------+---------------+ + 36| Reserved | + +---------------+---------------+---------------+---------------+ + 40/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48/ DataSegment - Login Parameters in Text request Format / + +/ / + +---------------+---------------+---------------+---------------+ + +10.12.1. T (Transit) Bit + + If set to 1, indicates that the initiator is ready to transit to the + next stage. + + If the T bit is set to 1 and NSG is FullFeaturePhase, then this also + indicates that the initiator is ready for the Final Login Response + (see Chapter 5). + +10.12.2. C (Continue) Bit + + When set to 1, indicates that the text (set of key=value pairs) in + this Login Request is not complete (it will be continued on + subsequent Login Requests); otherwise, it indicates that this Login + Request ends a set of key=value pairs. A Login Request with the C + bit set to 1 MUST have the T bit set to 0. + + + + +Satran, et al. Standards Track [Page 155] + +RFC 3720 iSCSI April 2004 + + +10.12.3. CSG and NSG + + Through these fields, Current Stage (CSG) and Next Stage (NSG), the + Login negotiation requests and responses are associated with a + specific stage in the session (SecurityNegotiation, + LoginOperationalNegotiation, FullFeaturePhase) and may indicate the + next stage to which they want to move (see Chapter 5). The next + stage value is only valid when the T bit is 1; otherwise, it is + reserved. + + The stage codes are: + + - 0 - SecurityNegotiation + - 1 - LoginOperationalNegotiation + - 3 - FullFeaturePhase + + All other codes are reserved. + +10.12.4. Version + + The version number of the current draft is 0x00. As such, all + devices MUST carry version 0x00 for both Version-min and Version-max. + +10.12.4.1. Version-max + + Maximum Version number supported. + + All Login Requests within the Login Phase MUST carry the same + Version-max. + + The target MUST use the value presented with the first Login Request. + +10.12.4.2. Version-min + + All Login Requests within the Login Phase MUST carry the same + Version-min. The target MUST use the value presented with the first + Login Request. + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 156] + +RFC 3720 iSCSI April 2004 + + +10.12.5. ISID + + This is an initiator-defined component of the session identifier and + is structured as follows (see [RFC3721] and Section 9.1.1 + Conservative Reuse of ISIDs for details): + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 8| T | A | B | C | + +---------------+---------------+---------------+---------------+ + 12| D | + +---------------+---------------+ + + The T field identifies the format and usage of A, B, C, and D as + indicated below: + + T + + 00b OUI-Format + A&B are a 22 bit OUI + (the I/G & U/L bits are omitted) + C&D 24 bit qualifier + 01b EN - Format (IANA Enterprise Number) + A - Reserved + B&C EN (IANA Enterprise Number) + D - Qualifier + 10b "Random" + A - Reserved + B&C Random + D - Qualifier + 11b A,B,C&D Reserved + + For the T field values 00b and 01b, a combination of A and B (for + 00b) or B and C (for 01b) identifies the vendor or organization whose + component (software or hardware) generates this ISID. A vendor or + organization with one or more OUIs, or one or more Enterprise + Numbers, MUST use at least one of these numbers and select the + appropriate value for the T field when its components generate ISIDs. + An OUI or EN MUST be set in the corresponding fields in network byte + order (byte big-endian). + + If the T field is 10b, B and C are set to a random 24-bit unsigned + integer value in network byte order (byte big-endian). See [RFC3721] + for how this affects the principle of "conservative reuse". + + + + + +Satran, et al. Standards Track [Page 157] + +RFC 3720 iSCSI April 2004 + + + The Qualifier field is a 16 or 24-bit unsigned integer value that + provides a range of possible values for the ISID within the selected + namespace. It may be set to any value within the constraints + specified in the iSCSI protocol (see Section 3.4.3 Consequences of + the Model and Section 9.1.1 Conservative Reuse of ISIDs). + + The T field value of 11b is reserved. + + If the ISID is derived from something assigned to a hardware adapter + or interface by a vendor, as a preset default value, it MUST be + configurable to a value assigned according to the SCSI port behavior + desired by the system in which it is installed (see Section 9.1.1 + Conservative Reuse of ISIDs and Section 9.1.2 iSCSI Name, ISID, and + TPGT Use). The resultant ISID MUST also be persistent over power + cycles, reboot, card swap, etc. + +10.12.6. TSIH + + TSIH must be set in the first Login Request. The reserved value 0 + MUST be used on the first connection for a new session. Otherwise, + the TSIH sent by the target at the conclusion of the successful login + of the first connection for this session MUST be used. The TSIH + identifies to the target the associated existing session for this new + connection. + + All Login Requests within a Login Phase MUST carry the same TSIH. + + The target MUST check the value presented with the first Login + Request and act as specified in Section 5.3.1 Login Phase Start. + +10.12.7. Connection ID - CID + + A unique ID for this connection within the session. + + All Login Requests within the Login Phase MUST carry the same CID. + + The target MUST use the value presented with the first Login Request. + + A Login Request with a non-zero TSIH and a CID equal to that of an + existing connection implies a logout of the connection followed by a + Login (see Section 5.3.4 Connection Reinstatement). For the details + of the implicit Logout Request, see Section 10.14 Logout Request. + + + + + + + + + +Satran, et al. Standards Track [Page 158] + +RFC 3720 iSCSI April 2004 + + +10.12.8. CmdSN + + CmdSN is either the initial command sequence number of a session (for + the first Login Request of a session - the "leading" login), or the + command sequence number in the command stream if the login is for a + new connection in an existing session. + + Examples: + + - Login on a leading connection - if the leading login carries + the CmdSN 123, all other Login Requests in the same Login Phase + carry the CmdSN 123 and the first non-immediate command in + FullFeaturePhase also carries the CmdSN 123. + + - Login on other than a leading connection - if the current CmdSN + at the time the first login on the connection is issued is 500, + then that PDU carries CmdSN=500. Subsequent Login Requests + that are needed to complete this Login Phase may carry a CmdSN + higher than 500 if non-immediate requests that were issued on + other connections in the same session advance CmdSN. + + If the Login Request is a leading Login Request, the target MUST use + the value presented in CmdSN as the target value for ExpCmdSN. + +10.12.9. ExpStatSN + + For the first Login Request on a connection this is ExpStatSN for the + old connection and this field is only valid if the Login Request + restarts a connection (see Section 5.3.4 Connection Reinstatement). + + For subsequent Login Requests it is used to acknowledge the Login + Responses with their increasing StatSN values. + +10.12.10. Login Parameters + + The initiator MUST provide some basic parameters in order to enable + the target to determine if the initiator may use the target's + resources and the initial text parameters for the security exchange. + + All the rules specified in Section 10.10.5 Text for text requests + also hold for Login Requests. Keys and their explanations are listed + in Chapter 11 (security negotiation keys) and Chapter 12 (operational + parameter negotiation keys). All keys in Chapter 12, except for the + X extension formats, MUST be supported by iSCSI initiators and + targets. Keys in Chapter 11 only need to be supported when the + function to which they refer is mandatory to implement. + + + + + +Satran, et al. Standards Track [Page 159] + +RFC 3720 iSCSI April 2004 + + +10.13. Login Response + + The Login Response indicates the progress and/or end of the Login + Phase. + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x23 |T|C|.|.|CSG|NSG| Version-max | Version-active| + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| ISID | + + +---------------+---------------+ + 12| | TSIH | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Reserved | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| Status-Class | Status-Detail | Reserved | + +---------------+---------------+---------------+---------------+ + 40/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48/ DataSegment - Login Parameters in Text request Format / + +/ / + +---------------+---------------+---------------+---------------+ + +10.13.1. Version-max + + This is the highest version number supported by the target. + + All Login Responses within the Login Phase MUST carry the same + Version-max. + + The initiator MUST use the value presented as a response to the first + Login Request. + + + + + + +Satran, et al. Standards Track [Page 160] + +RFC 3720 iSCSI April 2004 + + +10.13.2. Version-active + + Indicates the highest version supported by the target and initiator. + If the target does not support a version within the range specified + by the initiator, the target rejects the login and this field + indicates the lowest version supported by the target. + + All Login Responses within the Login Phase MUST carry the same + Version-active. + + The initiator MUST use the value presented as a response to the first + Login Request. + +10.13.3. TSIH + + The TSIH is the target assigned session identifying handle. Its + internal format and content are not defined by this protocol except + for the value 0 that is reserved. With the exception of the Login + Final-Response in a new session, this field should be set to the TSIH + provided by the initiator in the Login Request. For a new session, + the target MUST generate a non-zero TSIH and ONLY return it in the + Login Final-Response (see Section 5.3 Login Phase). + +10.13.4. StatSN + + For the first Login Response (the response to the first Login + Request), this is the starting status Sequence Number for the + connection. The next response of any kind, including the next Login + Response, if any, in the same Login Phase, will carry this number + + 1. This field is only valid if the Status-Class is 0. + +10.13.5. Status-Class and Status-Detail + + The Status returned in a Login Response indicates the execution + status of the Login Phase. The status includes: + + Status-Class + Status-Detail + + 0 Status-Class indicates success. + + A non-zero Status-Class indicates an exception. In this case, + Status-Class is sufficient for a simple initiator to use when + handling exceptions, without having to look at the Status-Detail. + The Status-Detail allows finer-grained exception handling for more + sophisticated initiators and for better information for logging. + + + + + +Satran, et al. Standards Track [Page 161] + +RFC 3720 iSCSI April 2004 + + + The status classes are as follows: + + 0 - Success - indicates that the iSCSI target successfully + received, understood, and accepted the request. The numbering + fields (StatSN, ExpCmdSN, MaxCmdSN) are only valid if + Status-Class is 0. + + 1 - Redirection - indicates that the initiator must take further + action to complete the request. This is usually due to the + target moving to a different address. All of the redirection + status class responses MUST return one or more text key + parameters of the type "TargetAddress", which indicates the + target's new address. A redirection response MAY be issued by + a target prior or after completing a security negotiation if a + security negotiation is required. A redirection SHOULD be + accepted by an initiator even without having the target + complete a security negotiation if any security negotiation is + required, and MUST be accepted by the initiator after the + completion of the security negotiation if any security + negotiation is required. + + 2 - Initiator Error (not a format error) - indicates that the + initiator most likely caused the error. This MAY be due to a + request for a resource for which the initiator does not have + permission. The request should not be tried again. + + 3 - Target Error - indicates that the target sees no errors in the + initiator's Login Request, but is currently incapable of + fulfilling the request. The initiator may re-try the same + Login Request later. + + The table below shows all of the currently allocated status codes. + The codes are in hexadecimal; the first byte is the status class and + the second byte is the status detail. + + ----------------------------------------------------------------- + Status | Code | Description + |(hex) | + ----------------------------------------------------------------- + Success | 0000 | Login is proceeding OK (*1). + ----------------------------------------------------------------- + Target moved | 0101 | The requested iSCSI Target Name (ITN) + temporarily | | has temporarily moved + | | to the address provided. + ----------------------------------------------------------------- + Target moved | 0102 | The requested ITN has permanently moved + permanently | | to the address provided. + ----------------------------------------------------------------- + + + +Satran, et al. Standards Track [Page 162] + +RFC 3720 iSCSI April 2004 + + + Initiator | 0200 | Miscellaneous iSCSI initiator + error | | errors. + ---------------------------------------------------------------- + Authentication| 0201 | The initiator could not be + failure | | successfully authenticated or target + | | authentication is not supported. + ----------------------------------------------------------------- + Authorization | 0202 | The initiator is not allowed access + failure | | to the given target. + ----------------------------------------------------------------- + Not found | 0203 | The requested ITN does not + | | exist at this address. + ----------------------------------------------------------------- + Target removed| 0204 | The requested ITN has been removed and + | |no forwarding address is provided. + ----------------------------------------------------------------- + Unsupported | 0205 | The requested iSCSI version range is + version | | not supported by the target. + ----------------------------------------------------------------- + Too many | 0206 | Too many connections on this SSID. + connections | | + ----------------------------------------------------------------- + Missing | 0207 | Missing parameters (e.g., iSCSI + parameter | | Initiator and/or Target Name). + ----------------------------------------------------------------- + Can't include | 0208 | Target does not support session + in session | | spanning to this connection (address). + ----------------------------------------------------------------- + Session type | 0209 | Target does not support this type of + not supported | | of session or not from this Initiator. + ----------------------------------------------------------------- + Session does | 020a | Attempt to add a connection + not exist | | to a non-existent session. + ----------------------------------------------------------------- + Invalid during| 020b | Invalid Request type during Login. + login | | + ----------------------------------------------------------------- + Target error | 0300 | Target hardware or software error. + ----------------------------------------------------------------- + Service | 0301 | The iSCSI service or target is not + unavailable | | currently operational. + ----------------------------------------------------------------- + Out of | 0302 | The target has insufficient session, + resources | | connection, or other resources. + ----------------------------------------------------------------- + + + + + + +Satran, et al. Standards Track [Page 163] + +RFC 3720 iSCSI April 2004 + + + (*1) If the response T bit is 1 in both the request and the matching + response, and the NSG is FullFeaturePhase in both the request and the + matching response, the Login Phase is finished and the initiator may + proceed to issue SCSI commands. + + If the Status Class is not 0, the initiator and target MUST close the + TCP connection. + + If the target wishes to reject the Login Request for more than one + reason, it should return the primary reason for the rejection. + +10.13.6. T (Transit) bit + + The T bit is set to 1 as an indicator of the end of the stage. If + the T bit is set to 1 and NSG is FullFeaturePhase, then this is also + the Final Login Response (see Chapter 5). A T bit of 0 indicates a + "partial" response, which means "more negotiation needed". + + A Login Response with a T bit set to 1 MUST NOT contain key=value + pairs that may require additional answers from the initiator within + the same stage. + + If the status class is 0, the T bit MUST NOT be set to 1 if the T bit + in the request was set to 0. + +10.13.7. C (Continue) Bit + + When set to 1, indicates that the text (set of key=value pairs) in + this Login Response is not complete (it will be continued on + subsequent Login Responses); otherwise, it indicates that this Login + Response ends a set of key=value pairs. A Login Response with the C + bit set to 1 MUST have the T bit set to 0. + +10.13.8. Login Parameters + + The target MUST provide some basic parameters in order to enable the + initiator to determine if it is connected to the correct port and the + initial text parameters for the security exchange. + + All the rules specified in Section 10.11.6 Text Response Data for + text responses also hold for Login Responses. Keys and their + explanations are listed in Chapter 11 (security negotiation keys) and + Chapter 12 (operational parameter negotiation keys). All keys in + Chapter 12, except for the X extension formats, MUST be supported by + iSCSI initiators and targets. Keys in Chapter 11, only need to be + supported when the function to which they refer is mandatory to + implement. + + + + +Satran, et al. Standards Track [Page 164] + +RFC 3720 iSCSI April 2004 + + +10.14. Logout Request + + The Logout Request is used to perform a controlled closing of a + connection. + + An initiator MAY use a Logout Request to remove a connection from a + session or to close an entire session. + + After sending the Logout Request PDU, an initiator MUST NOT send any + new iSCSI requests on the closing connection. If the Logout Request + is intended to close the session, new iSCSI requests MUST NOT be sent + on any of the connections participating in the session. + + When receiving a Logout Request with the reason code of "close the + connection" or "close the session", the target MUST terminate all + pending commands, whether acknowledged via ExpCmdSN or not, on that + connection or session respectively. + + When receiving a Logout Request with the reason code "remove + connection for recovery", the target MUST discard all requests not + yet acknowledged via ExpCmdSN that were issued on the specified + connection, and suspend all data/status/R2T transfers on behalf of + pending commands on the specified connection. + + The target then issues the Logout Response and half-closes the TCP + connection (sends FIN). After receiving the Logout Response and + attempting to receive the FIN (if still possible), the initiator MUST + completely close the logging-out connection. For the terminated + commands, no additional responses should be expected. + + A Logout for a CID may be performed on a different transport + connection when the TCP connection for the CID has already been + terminated. In such a case, only a logical "closing" of the iSCSI + connection for the CID is implied with a Logout. + + All commands that were not terminated or not completed (with status) + and acknowledged when the connection is closed completely can be + reassigned to a new connection if the target supports connection + recovery. + + If an initiator intends to start recovery for a failing connection, + it MUST use the Logout Request to "clean-up" the target end of a + failing connection and enable recovery to start, or the Login Request + with a non-zero TSIH and the same CID on a new connection for the + same effect (see Section 10.14.3 CID). In sessions with a single + connection, the connection can be closed and then a new connection + reopened. A connection reinstatement login can be used for recovery + (see Section 5.3.4 Connection Reinstatement). + + + +Satran, et al. Standards Track [Page 165] + +RFC 3720 iSCSI April 2004 + + + A successful completion of a Logout Request with the reason code of + "close the connection" or "remove the connection for recovery" + results at the target in the discarding of unacknowledged commands + received on the connection being logged out. These are commands that + have arrived on the connection being logged out, but have not been + delivered to SCSI because one or more commands with a smaller CmdSN + has not been received by iSCSI. See Section 3.2.2.1 Command + Numbering and Acknowledging. The resulting holes the in command + sequence numbers will have to be handled by appropriate recovery (see + Chapter 6) unless the session is also closed. + + The entire logout discussion in this section is also applicable for + an implicit Logout realized via a connection reinstatement or session + reinstatement. When a Login Request performs an implicit Logout, the + implicit Logout is performed as if having the reason codes specified + below: + + Reason code Type of implicit Logout + ------------------------------------------- + 0 session reinstatement + 1 connection reinstatement when + the operational ErrorRecoveryLevel < 2 + 2 connection reinstatement when + the operational ErrorRecoveryLevel = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 166] + +RFC 3720 iSCSI April 2004 + + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| 0x06 |1| Reason Code | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------------------------------------------------------+ + 8/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| CID or Reserved | Reserved | + +---------------+---------------+---------------+---------------+ + 24| CmdSN | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + +10.14.1. Reason Code + + Reason Code indicates the reason for Logout as follows: + + 0 - close the session. All commands associated with the session + (if any) are terminated. + + 1 - close the connection. All commands associated with connection + (if any) are terminated. + + 2 - remove the connection for recovery. Connection is closed and + all commands associated with it, if any, are to be prepared + for a new allegiance. + + All other values are reserved. + + + + + + + + + + + +Satran, et al. Standards Track [Page 167] + +RFC 3720 iSCSI April 2004 + + +10.14.2. TotalAHSLength and DataSegmentLength + + For this PDU TotalAHSLength and DataSegmentLength MUST be 0. + +10.14.3. CID + + This is the connection ID of the connection to be closed (including + closing the TCP stream). This field is only valid if the reason code + is not "close the session". + +10.14.4. ExpStatSN + + This is the last ExpStatSN value for the connection to be closed. + +10.14.5. Implicit termination of tasks + + A target implicitly terminates the active tasks due to the iSCSI + protocol in the following cases: + + a) When a connection is implicitly or explicitly logged out with + the reason code of "Close the connection" and there are active + tasks allegiant to that connection. + + b) When a connection fails and eventually the connection state + times out (state transition M1 in Section 7.2.2 State + Transition Descriptions for Initiators and Targets) and there + are active tasks allegiant to that connection. + + c) When a successful recovery Logout is performed while there are + active tasks allegiant to that connection, and those tasks + eventually time out after the Time2Wait and Time2Retain + periods without allegiance reassignment. + + d) When a connection is implicitly or explicitly logged out with + the reason code of "Close the session" and there are active + tasks in that session. + + If the tasks terminated in any of the above cases are SCSI tasks, + they must be internally terminated as if with CHECK CONDITION status. + This status is only meaningful for appropriately handling the + internal SCSI state and SCSI side effects with respect to ordering + because this status is never communicated back as a terminating + status to the initiator. However additional actions may have to be + taken at SCSI level depending on the SCSI context as defined by the + SCSI standards (e.g., queued commands and ACA, in cases a), b), and + c), after the tasks are terminated, the target MUST report a Unit + Attention condition on the next command processed on any connection + for each affected I_T_L nexus with the status of CHECK CONDITION, and + + + +Satran, et al. Standards Track [Page 168] + +RFC 3720 iSCSI April 2004 + + + the ASC/ASCQ value of 47h/7Fh - "SOME COMMANDS CLEARED BY ISCSI + PROTOCOL EVENT" - etc. - see [SAM2] and [SPC3]). + +10.15. Logout Response + + The Logout Response is used by the target to indicate if the cleanup + operation for the connection(s) has completed. + + After Logout, the TCP connection referred by the CID MUST be closed + at both ends (or all connections must be closed if the logout reason + was session close). + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x26 |1| Reserved | Response | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------------------------------------------------------+ + 8/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag | + +---------------+---------------+---------------+---------------+ + 20| Reserved | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| Reserved | + +---------------------------------------------------------------+ + 40| Time2Wait | Time2Retain | + +---------------+---------------+---------------+---------------+ + 44| Reserved | + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + + + + + + + + + +Satran, et al. Standards Track [Page 169] + +RFC 3720 iSCSI April 2004 + + +10.15.1. Response + + Logout Response: + + 0 - connection or session closed successfully. + + 1 - CID not found. + + 2 - connection recovery is not supported. If Logout reason code + was recovery and target does not support it as indicated by the + ErrorRecoveryLevel. + + 3 - cleanup failed for various reasons. + +10.15.2. TotalAHSLength and DataSegmentLength + + For this PDU TotalAHSLength and DataSegmentLength MUST be 0. + +10.15.3. Time2Wait + + If the Logout Response code is 0 and if the operational + ErrorRecoveryLevel is 2, this is the minimum amount of time, in + seconds, to wait before attempting task reassignment. If the Logout + Response code is 0 and if the operational ErrorRecoveryLevel is less + than 2, this field is to be ignored. + + This field is invalid if the Logout Response code is 1. + + If the Logout response code is 2 or 3, this field specifies the + minimum time to wait before attempting a new implicit or explicit + logout. + + If Time2Wait is 0, the reassignment or a new Logout may be attempted + immediately. + +10.15.4. Time2Retain + + If the Logout response code is 0 and if the operational + ErrorRecoveryLevel is 2, this is the maximum amount of time, in + seconds, after the initial wait (Time2Wait), the target waits for the + allegiance reassignment for any active task after which the task + state is discarded. If the Logout response code is 0 and if the + operational ErrorRecoveryLevel is less than 2, this field is to be + ignored. + + This field is invalid if the Logout response code is 1. + + + + + +Satran, et al. Standards Track [Page 170] + +RFC 3720 iSCSI April 2004 + + + If the Logout response code is 2 or 3, this field specifies the + maximum amount of time, in seconds, after the initial wait + (Time2Wait), the target waits for a new implicit or explicit logout. + + If it is the last connection of a session, the whole session state is + discarded after Time2Retain. + + If Time2Retain is 0, the target has already discarded the connection + (and possibly the session) state along with the task states. No + reassignment or Logout is required in this case. + +10.16. SNACK Request + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x10 |1|.|.|.| Type | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or SNACK Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| Reserved | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 40| BegRun | + +---------------------------------------------------------------+ + 44| RunLength | + +---------------------------------------------------------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + If the implementation supports ErrorRecoveryLevel greater than zero, + it MUST support all SNACK types. + + + + + + +Satran, et al. Standards Track [Page 171] + +RFC 3720 iSCSI April 2004 + + + The SNACK is used by the initiator to request the retransmission of + numbered-responses, data, or R2T PDUs from the target. The SNACK + request indicates the numbered-responses or data "runs" whose + retransmission is requested by the target, where the run starts with + the first StatSN, DataSN, or R2TSN whose retransmission is requested + and indicates the number of Status, Data, or R2T PDUs requested + including the first. 0 has special meaning when used as a starting + number and length: + + - When used in RunLength, it means all PDUs starting with the + initial. + - When used in both BegRun and RunLength, it means all + unacknowledged PDUs. + + The numbered-response(s) or R2T(s), requested by a SNACK, MUST be + delivered as exact replicas of the ones that the target transmitted + originally except for the fields ExpCmdSN, MaxCmdSN, and ExpDataSN, + which MUST carry the current values. R2T(s)requested by SNACK MUST + also carry the current value of StatSN. + + The numbered Data-In PDUs, requested by a Data SNACK MUST be + delivered as exact replicas of the ones that the target transmitted + originally except for the fields ExpCmdSN and MaxCmdSN, which MUST + carry the current values and except for resegmentation (see Section + 10.16.3 Resegmentation). + + Any SNACK that requests a numbered-response, Data, or R2T that was + not sent by the target or was already acknowledged by the initiator, + MUST be rejected with a reason code of "Protocol error". + +10.16.1. Type + + This field encodes the SNACK function as follows: + + 0-Data/R2T SNACK - requesting retransmission of one or more Data- + In or R2T PDUs. + + 1-Status SNACK - requesting retransmission of one or more numbered + responses. + + 2-DataACK - positively acknowledges Data-In PDUs. + + 3-R-Data SNACK - requesting retransmission of Data-In PDUs with + possible resegmentation and status tagging. + + + + + + + +Satran, et al. Standards Track [Page 172] + +RFC 3720 iSCSI April 2004 + + + All other values are reserved. + + Data/R2T SNACK, Status SNACK, or R-Data SNACK for a command MUST + precede status acknowledgement for the given command. + +10.16.2. Data Acknowledgement + + If an initiator operates at ErrorRecoveryLevel 1 or higher, it MUST + issue a SNACK of type DataACK after receiving a Data-In PDU with the + A bit set to 1. However, if the initiator has detected holes in the + input sequence, it MUST postpone issuing the SNACK of type DataACK + until the holes are filled. An initiator MAY ignore the A bit if it + deems that the bit is being set aggressively by the target (i.e., + before the MaxBurstLength limit is reached). + + The DataACK is used to free resources at the target and not to + request or imply data retransmission. + + An initiator MUST NOT request retransmission for any data it had + already acknowledged. + +10.16.3. Resegmentation + + If the initiator MaxRecvDataSegmentLength changed between the + original transmission and the time the initiator requests + retransmission, the initiator MUST issue a R-Data SNACK (see Section + 10.16.1 Type). With R-Data SNACK, the initiator indicates that it + discards all the unacknowledged data and expects the target to resend + it. It also expects resegmentation. In this case, the retransmitted + Data-In PDUs MAY be different from the ones originally sent in order + to reflect changes in MaxRecvDataSegmentLength. Their DataSN starts + with the BegRun of the last DataACK received by the target if any was + received; otherwise it starts with 0 and is increased by 1 for each + resent Data-In PDU. + + A target that has received a R-Data SNACK MUST return a SCSI Response + that contains a copy of the SNACK Tag field from the R-Data SNACK in + the SCSI Response SNACK Tag field as its last or only Response. For + example, if it has already sent a response containing another value + in the SNACK Tag field or had the status included in the last Data-In + PDU, it must send a new SCSI Response PDU. If a target sends more + than one SCSI Response PDU due to this rule, all SCSI responses must + carry the same StatSN (see Section 10.4.4 SNACK Tag). If an + initiator attempts to recover a lost SCSI Response (with a + Status SNACK, see Section 10.16.1 Type) when more than one response + has been sent, the target will send the SCSI Response with the latest + content known to the target, including the last SNACK Tag for the + command. + + + +Satran, et al. Standards Track [Page 173] + +RFC 3720 iSCSI April 2004 + + + For considerations in allegiance reassignment of a task to a + connection with a different MaxRecvDataSegmentLength, refer to + Section 6.2.2 Allegiance Reassignment. + +10.16.4. Initiator Task Tag + + For Status SNACK and DataACK, the Initiator Task Tag MUST be set to + the reserved value 0xffffffff. In all other cases, the Initiator + Task Tag field MUST be set to the Initiator Task Tag of the + referenced command. + +10.16.5. Target Transfer Tag or SNACK Tag + + For an R-Data SNACK, this field MUST contain a value that is + different from 0 or 0xffffffff and is unique for the task (identified + by the Initiator Task Tag). This value MUST be copied by the iSCSI + target in the last or only SCSI Response PDU it issues for the + command. + + For DataACK, the Target Transfer Tag MUST contain a copy of the + Target Transfer Tag and LUN provided with the SCSI Data-In PDU with + the A bit set to 1. + + In all other cases, the Target Transfer Tag field MUST be set to the + reserved value of 0xffffffff. + +10.16.6. BegRun + + The DataSN, R2TSN, or StatSN of the first PDU whose retransmission is + requested (Data/R2T and Status SNACK), or the next expected DataSN + (DataACK SNACK). + + BegRun 0 when used in conjunction with RunLength 0 means resend all + unacknowledged Data-In, R2T or Response PDUs. + + BegRun MUST be 0 for a R-Data SNACK. + +10.16.7. RunLength + + The number of PDUs whose retransmission is requested. + + RunLength 0 signals that all Data-In, R2T, or Response PDUs carrying + the numbers equal to or greater than BegRun have to be resent. + + The RunLength MUST also be 0 for a DataACK SNACK in addition to + R-Data SNACK. + + + + + +Satran, et al. Standards Track [Page 174] + +RFC 3720 iSCSI April 2004 + + +10.17. Reject + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x3f |1| Reserved | Reason | Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 16| 0xffffffff | + +---------------+---------------+---------------+---------------+ + 20| Reserved | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36| DataSN/R2TSN or Reserved | + +---------------+---------------+---------------+---------------+ + 40| Reserved | + +---------------+---------------+---------------+---------------+ + 44| Reserved | + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + xx/ Complete Header of Bad PDU / + +/ / + +---------------+---------------+---------------+---------------+ + yy/Vendor specific data (if any) / + / / + +---------------+---------------+---------------+---------------+ + zz| Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + Reject is used to indicate an iSCSI error condition (protocol, + unsupported option, etc.). + + + + + + + + + +Satran, et al. Standards Track [Page 175] + +RFC 3720 iSCSI April 2004 + + +10.17.1. Reason + + The reject Reason is coded as follows: + + +------+----------------------------------------+------------------+ + | Code | Explanation | Can the original | + | (hex)| | PDU be re-sent? | + +------+----------------------------------------+------------------+ + | 0x01 | Reserved | no | + | | | | + | 0x02 | Data (payload) Digest Error | yes (Note 1) | + | | | | + | 0x03 | SNACK Reject | yes | + | | | | + | 0x04 | Protocol Error (e.g., SNACK request for| no | + | | a status that was already acknowledged)| | + | | | | + | 0x05 | Command not supported | no | + | | | | + | 0x06 | Immediate Command Reject - too many | yes | + | | immediate commands | | + | | | | + | 0x07 | Task in progress | no | + | | | | + | 0x08 | Invalid Data ACK | no | + | | | | + | 0x09 | Invalid PDU field | no (Note 2) | + | | | | + | 0x0a | Long Operation Reject - Can't generate | yes | + | | Target Transfer Tag - out of resources | | + | | | | + | 0x0b | Negotiation Reset | no | + | | | | + | 0x0c | Waiting for Logout | no | + +------+----------------------------------------+------------------+ + + Note 1: For iSCSI, Data-Out PDU retransmission is only done if the + target requests retransmission with a recovery R2T. However, if this + is the data digest error on immediate data, the initiator may choose + to retransmit the whole PDU including the immediate data. + + Note 2: A target should use this reason code for all invalid values + of PDU fields that are meant to describe a task, a response, or a + data transfer. Some examples are invalid TTT/ITT, buffer offset, LUN + qualifying a TTT, and an invalid sequence number in a SNACK. + + All other values for Reason are reserved. + + + + +Satran, et al. Standards Track [Page 176] + +RFC 3720 iSCSI April 2004 + + + In all the cases in which a pre-instantiated SCSI task is terminated + because of the reject, the target MUST issue a proper SCSI command + response with CHECK CONDITION as described in Section 10.4.3 + Response. In these cases in which a status for the SCSI task was + already sent before the reject, no additional status is required. If + the error is detected while data from the initiator is still expected + (i.e., the command PDU did not contain all the data and the target + has not received a Data-Out PDU with the Final bit set to 1 for the + unsolicited data, if any, and all outstanding R2Ts, if any), the + target MUST wait until it receives the last expected Data-Out PDUs + with the F bit set to 1 before sending the Response PDU. + + For additional usage semantics of Reject PDU, see Section 6.3 Usage + Of Reject PDU in Recovery. + +10.17.2. DataSN/R2TSN + + This field is only valid if the rejected PDU is a Data/R2T SNACK and + the Reject reason code is "Protocol error" (see Section 10.16 SNACK + Request). The DataSN/R2TSN is the next Data/R2T sequence number that + the target would send for the task, if any. + +10.17.3. StatSN, ExpCmdSN and MaxCmdSN + + These fields carry their usual values and are not related to the + rejected command. StatSN is advanced after a Reject. + +10.17.4. Complete Header of Bad PDU + + The target returns the header (not including digest) of the PDU in + error as the data of the response. + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 177] + +RFC 3720 iSCSI April 2004 + + +10.18. NOP-Out + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|I| 0x00 |1| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| CmdSN | + +---------------+---------------+---------------+---------------+ + 28| ExpStatSN | + +---------------+---------------+---------------+---------------+ + 32/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment - Ping Data (optional) / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + A NOP-Out may be used by an initiator as a "ping request" to verify + that a connection/session is still active and all its components are + operational. The NOP-In response is the "ping echo". + + A NOP-Out is also sent by an initiator in response to a NOP-In. + + A NOP-Out may also be used to confirm a changed ExpStatSN if another + PDU will not be available for a long time. + + Upon receipt of a NOP-In with the Target Transfer Tag set to a valid + value (not the reserved 0xffffffff), the initiator MUST respond with + a NOP-Out. In this case, the NOP-Out Target Transfer Tag MUST + contain a copy of the NOP-In Target Transfer Tag. + + + + + +Satran, et al. Standards Track [Page 178] + +RFC 3720 iSCSI April 2004 + + +10.18.1. Initiator Task Tag + + The NOP-Out MUST have the Initiator Task Tag set to a valid value + only if a response in the form of NOP-In is requested (i.e., the + NOP-Out is used as a ping request). Otherwise, the Initiator Task + Tag MUST be set to 0xffffffff. + + When a target receives the NOP-Out with a valid Initiator Task Tag, + it MUST respond with a Nop-In Response (see Section 10.19 NOP-In). + + If the Initiator Task Tag contains 0xffffffff, the I bit MUST be set + to 1 and the CmdSN is not advanced after this PDU is sent. + +10.18.2. Target Transfer Tag + + A target assigned identifier for the operation. + + The NOP-Out MUST only have the Target Transfer Tag set if it is + issued in response to a NOP-In with a valid Target Transfer Tag. In + this case, it copies the Target Transfer Tag from the NOP-In PDU. + Otherwise, the Target Transfer Tag MUST be set to 0xffffffff. + + When the Target Transfer Tag is set to a value other than 0xffffffff, + the LUN field MUST also be copied from the NOP-In. + +10.18.3. Ping Data + + Ping data are reflected in the NOP-In Response. The length of the + reflected data are limited to MaxRecvDataSegmentLength. The length + of ping data are indicated by the DataSegmentLength. 0 is a valid + value for the DataSegmentLength and indicates the absence of ping + data. + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 179] + +RFC 3720 iSCSI April 2004 + + +10.19. NOP-In + + Byte/ 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0|.|.| 0x20 |1| Reserved | + +---------------+---------------+---------------+---------------+ + 4|TotalAHSLength | DataSegmentLength | + +---------------+---------------+---------------+---------------+ + 8| LUN or Reserved | + + + + 12| | + +---------------+---------------+---------------+---------------+ + 16| Initiator Task Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 20| Target Transfer Tag or 0xffffffff | + +---------------+---------------+---------------+---------------+ + 24| StatSN | + +---------------+---------------+---------------+---------------+ + 28| ExpCmdSN | + +---------------+---------------+---------------+---------------+ + 32| MaxCmdSN | + +---------------+---------------+---------------+---------------+ + 36/ Reserved / + +/ / + +---------------+---------------+---------------+---------------+ + 48| Header-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + / DataSegment - Return Ping Data / + +/ / + +---------------+---------------+---------------+---------------+ + | Data-Digest (Optional) | + +---------------+---------------+---------------+---------------+ + + NOP-In is either sent by a target as a response to a NOP-Out, as a + "ping" to an initiator, or as a means to carry a changed ExpCmdSN + and/or MaxCmdSN if another PDU will not be available for a long time + (as determined by the target). + + When a target receives the NOP-Out with a valid Initiator Task Tag + (not the reserved value 0xffffffff), it MUST respond with a NOP-In + with the same Initiator Task Tag that was provided in the NOP-Out + request. It MUST also duplicate up to the first + MaxRecvDataSegmentLength bytes of the initiator provided Ping Data. + For such a response, the Target Transfer Tag MUST be 0xffffffff. + + + + + +Satran, et al. Standards Track [Page 180] + +RFC 3720 iSCSI April 2004 + + + Otherwise, when a target sends a NOP-In that is not a response to a + Nop-Out received from the initiator, the Initiator Task Tag MUST be + set to 0xffffffff and the Data Segment MUST NOT contain any data + (DataSegmentLength MUST be 0). + +10.19.1. Target Transfer Tag + + If the target is responding to a NOP-Out, this is set to the reserved + value 0xffffffff. + + If the target is sending a NOP-In as a Ping (intending to receive a + corresponding NOP-Out), this field is set to a valid value (not the + reserved 0xffffffff). + + If the target is initiating a NOP-In without wanting to receive a + corresponding NOP-Out, this field MUST hold the reserved value of + 0xffffffff. + +10.19.2. StatSN + + The StatSN field will always contain the next StatSN. However, when + the Initiator Task Tag is set to 0xffffffff, StatSN for the + connection is not advanced after this PDU is sent. + +10.19.3. LUN + + A LUN MUST be set to a correct value when the Target Transfer Tag is + valid (not the reserved value 0xffffffff). + +11. iSCSI Security Text Keys and Authentication Methods + + Only the following keys are used during the SecurityNegotiation stage + of the Login Phase: + + SessionType + InitiatorName + TargetName + TargetAddress + InitiatorAlias + TargetAlias + TargetPortalGroupTag + AuthMethod and the keys used by the authentication methods + specified under Section 11.1 AuthMethod along with all of + their associated keys as well as Vendor Specific + Authentication Methods. + + + + + + +Satran, et al. Standards Track [Page 181] + +RFC 3720 iSCSI April 2004 + + + Other keys MUST NOT be used. + + SessionType, InitiatorName, TargetName, InitiatorAlias, TargetAlias, + and TargetPortalGroupTag are described in Chapter 12 as they can be + used also in the OperationalNegotiation stage. + + All security keys have connection-wide applicability. + +11.1. AuthMethod + + Use: During Login - Security Negotiation Senders: Initiator and + Target Scope: connection + + AuthMethod = <list-of-values> + + The main item of security negotiation is the authentication method + (AuthMethod). + + The authentication methods that can be used (appear in the + list-of-values) are either those listed in the following table or are + vendor-unique methods: + + +------------------------------------------------------------+ + | Name | Description | + +------------------------------------------------------------+ + | KRB5 | Kerberos V5 - defined in [RFC1510] | + +------------------------------------------------------------+ + | SPKM1 | Simple Public-Key GSS-API Mechanism | + | | defined in [RFC2025] | + +------------------------------------------------------------+ + | SPKM2 | Simple Public-Key GSS-API Mechanism | + | | defined in [RFC2025] | + +------------------------------------------------------------+ + | SRP | Secure Remote Password | + | | defined in [RFC2945] | + +------------------------------------------------------------+ + | CHAP | Challenge Handshake Authentication Protocol| + | | defined in [RFC1994] | + +------------------------------------------------------------+ + | None | No authentication | + +------------------------------------------------------------+ + + The AuthMethod selection is followed by an "authentication exchange" + specific to the authentication method selected. + + The authentication method proposal may be made by either the + initiator or the target. However the initiator MUST make the first + step specific to the selected authentication method as soon as it is + + + +Satran, et al. Standards Track [Page 182] + +RFC 3720 iSCSI April 2004 + + + selected. It follows that if the target makes the authentication + method proposal the initiator sends the first keys(s) of the exchange + together with its authentication method selection. + + The authentication exchange authenticates the initiator to the + target, and optionally, the target to the initiator. Authentication + is OPTIONAL to use but MUST be supported by the target and initiator. + + The initiator and target MUST implement CHAP. All other + authentication methods are OPTIONAL. + + Private or public extension algorithms MAY also be negotiated for + authentication methods. Whenever a private or public extension + algorithm is part of the default offer (the offer made in absence of + explicit administrative action) the implementer MUST ensure that CHAP + is listed as an alternative in the default offer and "None" is not + part of the default offer. + + Extension authentication methods MUST be named using one of the + following two formats: + + a) Z-reversed.vendor.dns_name.do_something= + b) Z<#><IANA-registered-string>= + + Authentication methods named using the Z- format are used as private + extensions. Authentication methods named using the Z# format are + used as public extensions that must be registered with IANA and MUST + be described by an informational RFC. + + For all of the public or private extension authentication methods, + the method specific keys MUST conform to the format specified in + Section 5.1 Text Format for standard-label. + + To identify the vendor for private extension authentication methods, + we suggest you use the reversed DNS-name as a prefix to the proper + digest names. + + The part of digest-name following Z- and Z# MUST conform to the + format for standard-label specified in Section 5.1 Text Format. + + Support for public or private extension authentication methods is + OPTIONAL. + + The following subsections define the specific exchanges for each of + the standardized authentication methods. As mentioned earlier the + first step is always done by the initiator. + + + + + +Satran, et al. Standards Track [Page 183] + +RFC 3720 iSCSI April 2004 + + +11.1.1. Kerberos + + For KRB5 (Kerberos V5) [RFC1510] and [RFC1964], the initiator MUST + use: + + KRB_AP_REQ=<KRB_AP_REQ> + + where KRB_AP_REQ is the client message as defined in [RFC1510]. + + The default principal name assumed by an iSCSI initiator or target + (prior to any administrative configuration action) MUST be the iSCSI + Initiator Name or iSCSI Target Name respectively, prefixed by the + string "iscsi/". + + If the initiator authentication fails, the target MUST respond with a + Login reject with "Authentication Failure" status. Otherwise, if the + initiator has selected the mutual authentication option (by setting + MUTUAL-REQUIRED in the ap-options field of the KRB_AP_REQ), the + target MUST reply with: + + KRB_AP_REP=<KRB_AP_REP> + + where KRB_AP_REP is the server's response message as defined in + [RFC1510]. + + If mutual authentication was selected and target authentication + fails, the initiator MUST close the connection. + + KRB_AP_REQ and KRB_AP_REP are binary-values and their binary length + (not the length of the character string that represents them in + encoded form) MUST not exceed 65536 bytes. + +11.1.2. Simple Public-Key Mechanism (SPKM) + + For SPKM1 and SPKM2 [RFC2025], the initiator MUST use: + + SPKM_REQ=<SPKM-REQ> + + where SPKM-REQ is the first initiator token as defined in [RFC2025]. + + [RFC2025] defines situations where each side may send an error token + that may cause the peer to re-generate and resend its last token. + This scheme is followed in iSCSI, and the error token syntax is: + + SPKM_ERROR=<SPKM-ERROR> + + + + + + +Satran, et al. Standards Track [Page 184] + +RFC 3720 iSCSI April 2004 + + + However, SPKM-DEL tokens that are defined by [RFC2025] for fatal + errors will not be used by iSCSI. If the target needs to send a + SPKM-DEL token, it will, instead, send a Login "login reject" message + with the "Authentication Failure" status and terminate the + connection. If the initiator needs to send a SPKM-DEL token, it will + close the connection. + + In the following sections, we assume that no SPKM-ERROR tokens are + required. + + If the initiator authentication fails, the target MUST return an + error. Otherwise, if the AuthMethod is SPKM1 or if the initiator has + selected the mutual authentication option (by setting mutual-state + bit in the options field of the REQ-TOKEN in the SPKM-REQ), the + target MUST reply with: + + SPKM_REP_TI=<SPKM-REP-TI> + + where SPKM-REP-TI is the target token as defined in [RFC2025]. + + If mutual authentication was selected and target authentication + fails, the initiator MUST close the connection. Otherwise, if the + AuthMethod is SPKM1, the initiator MUST continue with: + + SPKM_REP_IT=<SPKM-REP-IT> + + where SPKM-REP-IT is the second initiator token as defined in + [RFC2025]. If the initiator authentication fails, the target MUST + answer with a Login reject with "Authentication Failure" status. + + SPKM requires support for very long authentication items. + + All the SPKM-* tokens are binary-values and their binary length (not + the length of the character string that represents them in encoded + form) MUST not exceed 65536 bytes. + +11.1.3. Secure Remote Password (SRP) + + For SRP [RFC2945], the initiator MUST use: + + SRP_U=<U> TargetAuth=Yes /* or TargetAuth=No */ + + The target MUST answer with a Login reject with the "Authorization + Failure" status or reply with: + + SRP_GROUP=<G1,G2...> SRP_s=<s> + + Where G1,G2... are proposed groups, in order of preference. + + + +Satran, et al. Standards Track [Page 185] + +RFC 3720 iSCSI April 2004 + + + The initiator MUST either close the connection or continue with: + + SRP_A=<A> SRP_GROUP=<G> + + Where G is one of G1,G2... that were proposed by the target. + + The target MUST answer with a Login reject with the "Authentication + Failure" status or reply with: + + SRP_B=<B> + + The initiator MUST close the connection or continue with: + + SRP_M=<M> + + If the initiator authentication fails, the target MUST answer with a + Login reject with "Authentication Failure" status. Otherwise, if the + initiator sent TargetAuth=Yes in the first message (requiring target + authentication), the target MUST reply with: + + SRP_HM=<H(A | M | K)> + + If the target authentication fails, the initiator MUST close the + connection. + + Where U, s, A, B, M, and H(A | M | K) are defined in [RFC2945] (using + the SHA1 hash function, such as SRP-SHA1) and G,Gn (Gn stands for + G1,G2...) are identifiers of SRP groups specified in [RFC3723]. G, + Gn, and U are text strings, s,A,B,M, and H(A | M | K) are + binary-values. The length of s,A,B,M and H(A | M | K) in binary form + (not the length of the character string that represents them in + encoded form) MUST not exceed 1024 bytes. + + For the SRP_GROUP, all the groups specified in [RFC3723] up to 1536 + bits (i.e., SRP-768, SRP-1024, SRP-1280, SRP-1536) must be supported + by initiators and targets. To guarantee interoperability, targets + MUST always offer "SRP-1536" as one of the proposed groups. + +11.1.4. Challenge Handshake Authentication Protocol (CHAP) + + For CHAP [RFC1994], in the first step, the initiator MUST send: + + CHAP_A=<A1,A2...> + + Where A1,A2... are proposed algorithms, in order of preference. + + + + + + +Satran, et al. Standards Track [Page 186] + +RFC 3720 iSCSI April 2004 + + + In the second step, the target MUST answer with a Login reject with + the "Authentication Failure" status or reply with: + + CHAP_A=<A> CHAP_I=<I> CHAP_C=<C> + + Where A is one of A1,A2... that were proposed by the initiator. + + In the third step, the initiator MUST continue with: + + CHAP_N=<N> CHAP_R=<R> + + or, if it requires target authentication, with: + + CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> + + If the initiator authentication fails, the target MUST answer with a + Login reject with "Authentication Failure" status. Otherwise, if the + initiator required target authentication, the target MUST either + answer with a Login reject with "Authentication Failure" or reply + with: + + CHAP_N=<N> CHAP_R=<R> + + If target authentication fails, the initiator MUST close the + connection. + + Where N, (A,A1,A2), I, C, and R are (correspondingly) the Name, + Algorithm, Identifier, Challenge, and Response as defined in + [RFC1994], N is a text string, A,A1,A2, and I are numbers, and C and + R are large-binary-values and their binary length (not the length of + the character string that represents them in encoded form) MUST not + exceed 1024 bytes. + + For the Algorithm, as stated in [RFC1994], one value is required to + be implemented: + + 5 (CHAP with MD5) + + To guarantee interoperability, initiators MUST always offer it as one + of the proposed algorithms. + +12. Login/Text Operational Text Keys + + Some session specific parameters MUST only be carried on the leading + connection and cannot be changed after the leading connection login + (e.g., MaxConnections, the maximum number of connections). This + + + + + +Satran, et al. Standards Track [Page 187] + +RFC 3720 iSCSI April 2004 + + + holds for a single connection session with regard to connection + restart. The keys that fall into this category have the use: LO + (Leading Only). + + Keys that can only be used during login have the use: IO (initialize + only), while those that can be used in both the Login Phase and Full + Feature Phase have the use: ALL. + + Keys that can only be used during Full Feature Phase use FFPO (Full + Feature Phase only). + + Keys marked as Any-Stage may also appear in the SecurityNegotiation + stage while all other keys described in this chapter are operational + keys. + + Keys that do not require an answer are marked as Declarative. + + Key scope is indicated as session-wide (SW) or connection-only (CO). + + Result function, wherever mentioned, states the function that can be + applied to check the validity of the responder selection. Minimum + means that the selected value cannot exceed the offered value. + Maximum means that the selected value cannot be lower than the + offered value. AND means that the selected value must be a possible + result of a Boolean "and" function with an arbitrary Boolean value + (e.g., if the offered value is No the selected value must be No). OR + means that the selected value must be a possible result of a Boolean + "or" function with an arbitrary Boolean value (e.g., if the offered + value is Yes the selected value must be Yes). + +12.1. HeaderDigest and DataDigest + + Use: IO + Senders: Initiator and Target + Scope: CO + + HeaderDigest = <list-of-values> + DataDigest = <list-of-values> + + Default is None for both HeaderDigest and DataDigest. + + Digests enable the checking of end-to-end, non-cryptographic data + integrity beyond the integrity checks provided by the link layers and + the covering of the whole communication path including all elements + that may change the network level PDUs such as routers, switches, and + proxies. + + + + + +Satran, et al. Standards Track [Page 188] + +RFC 3720 iSCSI April 2004 + + + The following table lists cyclic integrity checksums that can be + negotiated for the digests and that MUST be implemented by every + iSCSI initiator and target. These digest options only have error + detection significance. + + +---------------------------------------------+ + | Name | Description | Generator | + +---------------------------------------------+ + | CRC32C | 32 bit CRC |0x11edc6f41| + +---------------------------------------------+ + | None | no digest | + +---------------------------------------------+ + + The generator polynomial for this digest is given in + hex-notation (e.g., 0x3b stands for 0011 1011 and the polynomial is + x**5+X**4+x**3+x+1). + + When the Initiator and Target agree on a digest, this digest MUST be + used for every PDU in Full Feature Phase. + + Padding bytes, when present in a segment covered by a CRC, SHOULD be + set to 0 and are included in the CRC. + + The CRC MUST be calculated by a method that produces the same + results as the following process: + + - The PDU bits are considered as the coefficients of a + polynomial M(x) of degree n-1; bit 7 of the lowest numbered + byte is considered the most significant bit (x^n-1), followed + by bit 6 of the lowest numbered byte through bit 0 of the + highest numbered byte (x^0). + + - The most significant 32 bits are complemented. + + - The polynomial is multiplied by x^32 then divided by G(x). The + generator polynomial produces a remainder R(x) of degree <= 31. + + - The coefficients of R(x) are considered a 32 bit sequence. + + - The bit sequence is complemented and the result is the CRC. + + - The CRC bits are mapped into the digest word. The x^31 + coefficient in bit 7 of the lowest numbered byte of the digest + continuing through to the byte up to the x^24 coefficient in + bit 0 of the lowest numbered byte, continuing with the x^23 + coefficient in bit 7 of next byte through x^0 in bit 0 of the + highest numbered byte. + + + + +Satran, et al. Standards Track [Page 189] + +RFC 3720 iSCSI April 2004 + + + - Computing the CRC over any segment (data or header) extended + to include the CRC built using the generator 0x11edc6f41 will + always get the value 0x1c2d19ed as its final remainder (R(x)). + This value is given here in its polynomial form (i.e., not + mapped as the digest word). + + For a discussion about selection criteria for the CRC, see + [RFC3385]. For a detailed analysis of the iSCSI polynomial, see + [Castagnoli93]. + + Private or public extension algorithms MAY also be negotiated for + digests. Whenever a private or public digest extension algorithm is + part of the default offer (the offer made in absence of explicit + administrative action) the implementer MUST ensure that CRC32C is + listed as an alternative in the default offer and "None" is not + part of the default offer. + + Extension digest algorithms MUST be named using one of the following + two formats: + + a) Y-reversed.vendor.dns_name.do_something= + b) Y<#><IANA-registered-string>= + + Digests named using the Y- format are used for private purposes + (unregistered). Digests named using the Y# format (public extension) + must be registered with IANA and MUST be described by an + informational RFC. + + For private extension digests, to identify the vendor, we suggest + you use the reversed DNS-name as a prefix to the proper digest + names. + + The part of digest-name following Y- and Y# MUST conform to the + format for standard-label specified in Section 5.1 Text Format. + + Support for public or private extension digests is OPTIONAL. + +12.2. MaxConnections + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + + MaxConnections=<numerical-value-from-1-to-65535> + + Default is 1. + Result function is Minimum. + + + +Satran, et al. Standards Track [Page 190] + +RFC 3720 iSCSI April 2004 + + + + Initiator and target negotiate the maximum number of connections + requested/acceptable. + +12.3. SendTargets + + Use: FFPO + Senders: Initiator + Scope: SW + + For a complete description, see Appendix D. - SendTargets + Operation -. + +12.4. TargetName + + Use: IO by initiator, FFPO by target - only as response to a + SendTargets, Declarative, Any-Stage + + Senders: Initiator and Target + Scope: SW + + TargetName=<iSCSI-name-value> + + Examples: + + TargetName=iqn.1993-11.com.disk-vendor:diskarrays.sn.45678 + TargetName=eui.020000023B040506 + + The initiator of the TCP connection MUST provide this key to the + remote endpoint in the first login request if the initiator is not + establishing a discovery session. The iSCSI Target Name specifies + the worldwide unique name of the target. + + The TargetName key may also be returned by the "SendTargets" text + request (which is its only use when issued by a target). + + TargetName MUST not be redeclared within the login phase. + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 191] + +RFC 3720 iSCSI April 2004 + + +12.5. InitiatorName + + Use: IO, Declarative, Any-Stage + Senders: Initiator + Scope: SW + + InitiatorName=<iSCSI-name-value> + + Examples: + + InitiatorName=iqn.1992-04.com.os-vendor.plan9:cdrom.12345 + InitiatorName=iqn.2001-02.com.ssp.users:customer235.host90 + + The initiator of the TCP connection MUST provide this key to the + remote endpoint at the first Login of the Login Phase for every + connection. The InitiatorName key enables the initiator to identify + itself to the remote endpoint. + + InitiatorName MUST not be redeclared within the login phase. + +12.6. TargetAlias + + Use: ALL, Declarative, Any-Stage + Senders: Target + Scope: SW + + TargetAlias=<iSCSI-local-name-value> + + Examples: + + TargetAlias=Bob-s Disk + TargetAlias=Database Server 1 Log Disk + TargetAlias=Web Server 3 Disk 20 + + If a target has been configured with a human-readable name or + description, this name SHOULD be communicated to the initiator during + a Login Response PDU if SessionType=Normal (see Section 12.21 + SessionType). This string is not used as an identifier, nor is it + meant to be used for authentication or authorization decisions. It + can be displayed by the initiator's user interface in a list of + targets to which it is connected. + + + + + + + + + + +Satran, et al. Standards Track [Page 192] + +RFC 3720 iSCSI April 2004 + + +12.7. InitiatorAlias + + Use: ALL, Declarative, Any-Stage + Senders: Initiator + Scope: SW + + InitiatorAlias=<iSCSI-local-name-value> + + Examples: + + InitiatorAlias=Web Server 4 + InitiatorAlias=spyalley.nsa.gov + InitiatorAlias=Exchange Server + + If an initiator has been configured with a human-readable name or + description, it SHOULD be communicated to the target during a Login + Request PDU. If not, the host name can be used instead. This string + is not used as an identifier, nor is meant to be used for + authentication or authorization decisions. It can be displayed by + the target's user interface in a list of initiators to which it is + connected. + +12.8. TargetAddress + + Use: ALL, Declarative, Any-Stage + Senders: Target + Scope: SW + + TargetAddress=domainname[:port][,portal-group-tag] + + The domainname can be specified as either a DNS host name, a + dotted-decimal IPv4 address, or a bracketed IPv6 address as specified + in [RFC2732]. + + If the TCP port is not specified, it is assumed to be the + IANA-assigned default port for iSCSI (see Section 13 IANA + Considerations). + + If the TargetAddress is returned as the result of a redirect status + in a login response, the comma and portal group tag MUST be omitted. + + If the TargetAddress is returned within a SendTargets response, the + portal group tag MUST be included. + + + + + + + + +Satran, et al. Standards Track [Page 193] + +RFC 3720 iSCSI April 2004 + + + Examples: + + TargetAddress=10.0.0.1:5003,1 + TargetAddress=[1080:0:0:0:8:800:200C:417A],65 + TargetAddress=[1080::8:800:200C:417A]:5003,1 + TargetAddress=computingcenter.example.com,23 + + Use of the portal-group-tag is described in Appendix D. + - SendTargets Operation -. The formats for the port and + portal-group-tag are the same as the one specified in Section 12.9 + TargetPortalGroupTag. + +12.9. TargetPortalGroupTag + + Use: IO by target, Declarative, Any-Stage + Senders: Target + Scope: SW + + TargetPortalGroupTag=<16-bit-binary-value> + + Examples: + TargetPortalGroupTag=1 + + The target portal group tag is a 16-bit binary-value that uniquely + identifies a portal group within an iSCSI target node. This key + carries the value of the tag of the portal group that is servicing + the Login request. The iSCSI target returns this key to the + initiator in the Login Response PDU to the first Login Request PDU + that has the C bit set to 0 when TargetName is given by the + initiator. + + For the complete usage expectations of this key see Section 5.3 Login + Phase. + +12.10. InitialR2T + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + + InitialR2T=<boolean-value> + + Examples: + + I->InitialR2T=No + T->InitialR2T=No + + + + +Satran, et al. Standards Track [Page 194] + +RFC 3720 iSCSI April 2004 + + + Default is Yes. + Result function is OR. + + The InitialR2T key is used to turn off the default use of R2T for + unidirectional and the output part of bidirectional commands, thus + allowing an initiator to start sending data to a target as if it has + received an initial R2T with Buffer Offset=Immediate Data Length and + Desired Data Transfer Length=(min(FirstBurstLength, Expected Data + Transfer Length) - Received Immediate Data Length). + + The default action is that R2T is required, unless both the initiator + and the target send this key-pair attribute specifying InitialR2T=No. + Only the first outgoing data burst (immediate data and/or separate + PDUs) can be sent unsolicited (i.e., not requiring an explicit R2T). + +12.11. ImmediateData + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + + ImmediateData=<boolean-value> + + Default is Yes. + Result function is AND. + + The initiator and target negotiate support for immediate data. To + turn immediate data off, the initiator or target must state its + desire to do so. ImmediateData can be turned on if both the + initiator and target have ImmediateData=Yes. + + If ImmediateData is set to Yes and InitialR2T is set to Yes + (default), then only immediate data are accepted in the first burst. + + If ImmediateData is set to No and InitialR2T is set to Yes, then the + initiator MUST NOT send unsolicited data and the target MUST reject + unsolicited data with the corresponding response code. + + If ImmediateData is set to No and InitialR2T is set to No, then the + initiator MUST NOT send unsolicited immediate data, but MAY send one + unsolicited burst of Data-Out PDUs. + + If ImmediateData is set to Yes and InitialR2T is set to No, then the + initiator MAY send unsolicited immediate data and/or one unsolicited + burst of Data-Out PDUs. + + + + + +Satran, et al. Standards Track [Page 195] + +RFC 3720 iSCSI April 2004 + + + The following table is a summary of unsolicited data options: + + +----------+-------------+------------------+--------------+ + |InitialR2T|ImmediateData| Unsolicited |Immediate Data| + | | | Data Out PDUs | | + +----------+-------------+------------------+--------------+ + | No | No | Yes | No | + +----------+-------------+------------------+--------------+ + | No | Yes | Yes | Yes | + +----------+-------------+------------------+--------------+ + | Yes | No | No | No | + +----------+-------------+------------------+--------------+ + | Yes | Yes | No | Yes | + +----------+-------------+------------------+--------------+ + +12.12. MaxRecvDataSegmentLength + + Use: ALL, Declarative + Senders: Initiator and Target + Scope: CO + + MaxRecvDataSegmentLength=<numerical-value-512-to-(2**24-1)> + + Default is 8192 bytes. + + The initiator or target declares the maximum data segment length in + bytes it can receive in an iSCSI PDU. + + The transmitter (initiator or target) is required to send PDUs with a + data segment that does not exceed MaxRecvDataSegmentLength of the + receiver. + + A target receiver is additionally limited by MaxBurstLength for + solicited data and FirstBurstLength for unsolicited data. An + initiator MUST NOT send solicited PDUs exceeding MaxBurstLength nor + unsolicited PDUs exceeding FirstBurstLength (or + FirstBurstLength-Immediate Data Length if immediate data were sent). + +12.13. MaxBurstLength + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + + MaxBurstLength=<numerical-value-512-to-(2**24-1)> + + + + + +Satran, et al. Standards Track [Page 196] + +RFC 3720 iSCSI April 2004 + + + Default is 262144 (256 Kbytes). + Result function is Minimum. + + The initiator and target negotiate maximum SCSI data payload in bytes + in a Data-In or a solicited Data-Out iSCSI sequence. A sequence + consists of one or more consecutive Data-In or Data-Out PDUs that end + with a Data-In or Data-Out PDU with the F bit set to one. + +12.14. FirstBurstLength + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + Irrelevant when: ( InitialR2T=Yes and ImmediateData=No ) + + FirstBurstLength=<numerical-value-512-to-(2**24-1)> + + Default is 65536 (64 Kbytes). + Result function is Minimum. + + The initiator and target negotiate the maximum amount in bytes of + unsolicited data an iSCSI initiator may send to the target during the + execution of a single SCSI command. This covers the immediate data + (if any) and the sequence of unsolicited Data-Out PDUs (if any) that + follow the command. + + FirstBurstLength MUST NOT exceed MaxBurstLength. + +12.15. DefaultTime2Wait + + Use: LO + Senders: Initiator and Target + Scope: SW + + DefaultTime2Wait=<numerical-value-0-to-3600> + + Default is 2. + Result function is Maximum. + + The initiator and target negotiate the minimum time, in seconds, to + wait before attempting an explicit/implicit logout or an active task + reassignment after an unexpected connection termination or a + connection reset. + + A value of 0 indicates that logout or active task reassignment can be + attempted immediately. + + + + +Satran, et al. Standards Track [Page 197] + +RFC 3720 iSCSI April 2004 + + +12.16. DefaultTime2Retain + + Use: LO Senders: Initiator and Target Scope: SW + + DefaultTime2Retain=<numerical-value-0-to-3600> + + Default is 20. Result function is Minimum. + + The initiator and target negotiate the maximum time, in seconds after + an initial wait (Time2Wait), before which an active task reassignment + is still possible after an unexpected connection termination or a + connection reset. + + This value is also the session state timeout if the connection in + question is the last LOGGED_IN connection in the session. + + A value of 0 indicates that connection/task state is immediately + discarded by the target. + +12.17. MaxOutstandingR2T + + Use: LO + Senders: Initiator and Target + Scope: SW + + MaxOutstandingR2T=<numerical-value-from-1-to-65535> + Irrelevant when: SessionType=Discovery + + Default is 1. + Result function is Minimum. + + Initiator and target negotiate the maximum number of outstanding R2Ts + per task, excluding any implied initial R2T that might be part of + that task. An R2T is considered outstanding until the last data PDU + (with the F bit set to 1) is transferred, or a sequence reception + timeout (Section 6.1.4.1 Recovery Within-command) is encountered for + that data sequence. + +12.18. DataPDUInOrder + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + + DataPDUInOrder=<boolean-value> + + + + + +Satran, et al. Standards Track [Page 198] + +RFC 3720 iSCSI April 2004 + + + Default is Yes. + Result function is OR. + + No is used by iSCSI to indicate that the data PDUs within sequences + can be in any order. Yes is used to indicate that data PDUs within + sequences have to be at continuously increasing addresses and + overlays are forbidden. + +12.19. DataSequenceInOrder + + Use: LO + Senders: Initiator and Target + Scope: SW + Irrelevant when: SessionType=Discovery + + DataSequenceInOrder=<boolean-value> + + Default is Yes. + Result function is OR. + + A Data Sequence is a sequence of Data-In or Data-Out PDUs that end + with a Data-In or Data-Out PDU with the F bit set to one. A Data-Out + sequence is sent either unsolicited or in response to an R2T. + Sequences cover an offset-range. + + If DataSequenceInOrder is set to No, Data PDU sequences may be + transferred in any order. + + If DataSequenceInOrder is set to Yes, Data Sequences MUST be + transferred using continuously non-decreasing sequence offsets (R2T + buffer offset for writes, or the smallest SCSI Data-In buffer offset + within a read data sequence). + + If DataSequenceInOrder is set to Yes, a target may retry at most the + last R2T, and an initiator may at most request retransmission for the + last read data sequence. For this reason, if ErrorRecoveryLevel is + not 0 and DataSequenceInOrder is set to Yes then MaxOustandingR2T + MUST be set to 1. + +12.20. ErrorRecoveryLevel + + Use: LO + Senders: Initiator and Target + Scope: SW + + ErrorRecoveryLevel=<numerical-value-0-to-2> + + + + + +Satran, et al. Standards Track [Page 199] + +RFC 3720 iSCSI April 2004 + + + Default is 0. + Result function is Minimum. + + The initiator and target negotiate the recovery level supported. + + Recovery levels represent a combination of recovery capabilities. + Each recovery level includes all the capabilities of the lower + recovery levels and adds some new ones to them. + + In the description of recovery mechanisms, certain recovery classes + are specified. Section 6.1.5 Error Recovery Hierarchy describes the + mapping between the classes and the levels. + +12.21. SessionType + + Use: LO, Declarative, Any-Stage + Senders: Initiator + Scope: SW + + SessionType= <Discovery|Normal> + + Default is Normal. + + The initiator indicates the type of session it wants to create. The + target can either accept it or reject it. + + A discovery session indicates to the Target that the only purpose of + this Session is discovery. The only requests a target accepts in + this type of session are a text request with a SendTargets key and a + logout request with reason "close the session". + + The discovery session implies MaxConnections = 1 and overrides both + the default and an explicit setting. + +12.22. The Private or Public Extension Key Format + + Use: ALL + Senders: Initiator and Target + Scope: specific key dependent + + X-reversed.vendor.dns_name.do_something= + + or + + X<#><IANA-registered-string>= + + + + + + +Satran, et al. Standards Track [Page 200] + +RFC 3720 iSCSI April 2004 + + + Keys with this format are used for public or private extension + purposes. These keys always start with X- if unregistered with IANA + (private) or X# if registered with IANA (public). + + For unregistered keys, to identify the vendor, we suggest you use the + reversed DNS-name as a prefix to the key-proper. + + The part of key-name following X- and X# MUST conform to the format + for key-name specified in Section 5.1 Text Format. + + For IANA registered keys the string following X# must be registered + with IANA and the use of the key MUST be described by an + informational RFC. + + Vendor specific keys MUST ONLY be used in normal sessions. + + Support for public or private extension keys is OPTIONAL. + +13. IANA Considerations + + This section conforms to [RFC2434]. + + The well-known user TCP port number for iSCSI connections assigned by + IANA is 3260 and this is the default iSCSI port. Implementations + needing a system TCP port number may use port 860, the port assigned + by IANA as the iSCSI system port; however in order to use port 860, + it MUST be explicitly specified - implementations MUST NOT default to + use of port 860, as 3260 is the only allowed default. + + Extension keys, authentication methods, or digest types for which a + vendor or group of vendors intend to provide publicly available + descriptions MUST be described by an RFC and MUST be registered with + IANA. + + The IANA has set up the following three registries: + + a) iSCSI extended key registry + b) iSCSI authentication methods registry + c) iSCSI digests registry + + [RFC3723] also instructs IANA to maintain a registry for the values + of the SRP_GROUP key. The format of these values must conform to the + one specified for iSCSI extension item-label in Section 13.5.4 + Standard iSCSI extension item-label format. + + + + + + + +Satran, et al. Standards Track [Page 201] + +RFC 3720 iSCSI April 2004 + + + For the iSCSI authentication methods registry and the iSCSI digests + registry, IANA MUST also assign a 16-bit unsigned integer number (the + method number for the authentication method and the digest number for + the digest). + + The following initial values for the registry for authentication + methods are specified by the standards action of this document: + + Authentication Method | Number | + +----------------------------------------+--------+ + | CHAP | 1 | + +----------------------------------------+--------+ + | SRP | 2 | + +----------------------------------------+--------+ + | KRB5 | 3 | + +----------------------------------------+--------+ + | SPKM1 | 4 | + +----------------------------------------+--------+ + | SPKM2 | 5 | + +----------------------------------------+--------+ + + All other record numbers from 0 to 255 are reserved. IANA will + register numbers above 255. + + Authentication methods with numbers above 255 MUST be unique within + the registry and MUST be used with the prefix Z#. + + + The following initial values for the registry for digests are + specified by the standards action of this document: + + Digest | Number | + +----------------------------------------+--------+ + | CRC32C | 1 | + +----------------------------------------+--------+ + + All other record numbers from 0 to 255 are reserved. IANA will + register numbers above 255. + + Digests with numbers above 255 MUST be unique within the registry and + MUST be used with the prefix Y#. + + The RFC that describes the item to be registered MUST indicate in the + IANA Considerations section the string and iSCSI registry to which it + should be recorded. + + Extension Keys, Authentication Methods, and digests (iSCSI extension + items) must conform to a number of requirements as described below. + + + +Satran, et al. Standards Track [Page 202] + +RFC 3720 iSCSI April 2004 + + +13.1. Naming Requirements + + Each iSCSI extension item must have a unique name in its category. + This name will be used as a standard-label for the key, access + method, or digest and must conform to the syntax specified in Section + 13.5.4 Standard iSCSI extension item-label format for iSCSI extension + item-labels. + +13.2. Mechanism Specification Requirements + + For iSCSI extension items all of the protocols and procedures used by + a given iSCSI extension item must be described, either in the + specification of the iSCSI extension item itself or in some other + publicly available specification, in sufficient detail for the iSCSI + extension item to be implemented by any competent implementor. Use + of secret and/or proprietary methods in iSCSI extension items are + expressly prohibited. In addition, the restrictions imposed by + [RFC1602] on the standardization of patented algorithms must be + respected. + +13.3. Publication Requirements + + All iSCSI extension items must be described by an RFC. The RFC may + be informational rather than Standards-Track, although Standards + Track review and approval are encouraged for all iSCSI extension + items. + +13.4. Security Requirements + + Any known security issues that arise from the use of the iSCSI + extension item must be completely and fully described. It is not + required that the iSCSI extension item be secure or that it be free + from risks, but that the known risks be identified. Publication of a + new iSCSI extension item does not require an exhaustive security + review, and the security considerations section is subject to + continuing evaluation. + + Additional security considerations should be addressed by publishing + revised versions of the iSCSI extension item specification. + + For each of these registries, IANA must record the registered string, + which MUST conform to the format rules described in Section 13.5.4 + Standard iSCSI extension item-label format for iSCSI extension + item-labels, and the RFC number that describes it. The key prefix + (X#, Y# or Z#) is not part of the recorded string. + + + + + + +Satran, et al. Standards Track [Page 203] + +RFC 3720 iSCSI April 2004 + + +13.5. Registration Procedure + + Registration of a new iSCSI extension item starts with the + construction of an Internet Draft to become an RFC. + +13.5.1. Present the iSCSI extension item to the Community + + Send a proposed access type specification to the IPS WG mailing list, + or if the IPS WG is disbanded at the registration time, to a mailing + list designated by the IETF Transport Area Director for a review + period of a month. The intent of the public posting is to solicit + comments and feedback on the iSCSI extension item specification and a + review of any security considerations. + +13.5.2. iSCSI extension item review and IESG approval + + When the one month period has passed, the IPS WG chair or a person + nominated by the IETF Transport Area Director (the iSCSI extension + item reviewer) forwards the Internet Draft to the IESG for + publication as an informational RFC or rejects it. If the + specification is a standards track document, the usual IETF + procedures for such documents are followed. + + Decisions made by the iSCSI extension item reviewer must be published + within two weeks after the month-long review period. Decisions made + by the iSCSI extension item reviewer can be appealed through the IESG + appeal process. + +13.5.3. IANA Registration + + Provided that the iSCSI extension item has either passed review or + has been successfully appealed to the IESG, and the specification is + published as an RFC, then IANA will register the iSCSI extension item + and make the registration available to the community. + +13.5.4. Standard iSCSI extension item-label format + + The following character symbols are used iSCSI extension item-labels + (the hexadecimal values represent Unicode code points): + + (a-z, A-Z) - letters + (0-9) - digits + "." (0x2e) - dot + "-" (0x2d) - minus + "+" (0x2b) - plus + "@" (0x40) - commercial at + "_" (0x5f) - underscore + + + + +Satran, et al. Standards Track [Page 204] + +RFC 3720 iSCSI April 2004 + + + An iSCSI extension item-label is a string of one or more characters + that consist of letters, digits, dot, minus, plus, commercial at, or + underscore. An iSCSI extension item-label MUST begin with a capital + letter and must not exceed 63 characters. + +13.6. IANA Procedures for Registering iSCSI extension items + + The identity of the iSCSI extension item reviewer is communicated to + the IANA by the IESG. Then, the IANA only acts in response to iSCSI + extension item definitions that are approved by the iSCSI extension + item reviewer and forwarded by the reviewer to the IANA for + registration, or in response to a communication from the IESG that an + iSCSI extension item definition appeal has overturned the iSCSI + extension item reviewer's ruling. + +References + +Normative References + + [CAM] ANSI X3.232-199X, Common Access Method-3. + + [EUI] "Guidelines for 64-bit Global Identifier (EUI-64)", + http: + //standards.ieee.org/regauth/oui/tutorials/EUI64.html + + [OUI] "IEEE OUI and Company_Id Assignments", + http://standards.ieee.org/regauth/oui + + [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC793] Postel, J., "Transmission Control Protocol", STD 7, + RFC 793, September 1981. + + [RFC1035] Mockapetris, P., "Domain Names - Implementation and + Specification", STD 13, RFC 1035, November 1987. + + [RFC1122] Braden, R., Ed., "Requirements for Internet Hosts- + Communication Layer", STD 3, RFC 1122, October 1989. + + [RFC1510] Kohl, J. and C. Neuman, "The Kerberos Network + Authentication Service (V5)", RFC 1510, September + 1993. + + [RFC1737] Sollins, K. and L. Masinter "Functional Requirements + for Uniform Resource Names"RFC 1737, December 1994. + + + + + +Satran, et al. Standards Track [Page 205] + +RFC 3720 iSCSI April 2004 + + + [RFC1964] Linn, J., "The Kerberos Version 5 GSS-API Mechanism", + RFC 1964, June 1996. + + [RFC1982] Elz, R. and R. Bush, "Serial Number Arithmetic", RFC + 1982, August 1996. + + [RFC1994] Simpson, W., "PPP Challenge Handshake Authentication + Protocol (CHAP)", RFC 1994, August 1996. + + [RFC2025] Adams, C., "The Simple Public-Key GSS-API Mechanism + (SPKM)", RFC 2025, October 1996. + + [RFC2045] Borenstein, N. and N. Freed, "MIME (Multipurpose + Internet Mail Extensions) Part One: Mechanisms for + Specifying and Describing the Format of Internet + Message Bodies", RFC 2045, November 1996. + + [RFC2119] Bradner, S. "Key Words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2279] Yergeau, F., "UTF-8, a Transformation Format of ISO + 10646", RFC 2279 October 1996. + + [RFC2373] Hinden, R. and S. Deering, "IP Version 6 Addressing + Architecture", RFC 2373, July 1998. + + [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter "Uniform + Resource Identifiers", RFC 2396, August 1998. + + [RFC2401] Kent, S. and R. Atkinson, "Security Architecture for + the Internet Protocol", RFC 2401, November 1998. + + [RFC2404] Madson, C. and R. Glenn, "The Use of HMAC-SHA-1-96 + within ESP and AH", RFC 2404, November 1998. + + [RFC2406] Kent, S. and R. Atkinson, "IP Encapsulating Security + Payload (ESP)", RFC 2406, November 1998. + + [RFC2407] Piper, D., "The Internet IP Security Domain of + Interpretation of ISAKMP", RFC 2407, November 1998. + + [RFC2409] Harkins, D. and D. Carrel, "The Internet Key Exchange + (IKE)", RFC2409, November 1998. + + [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing + an IANA Considerations Section in RFCs.", BCP 26, RFC + 2434, October 1998. + + + + +Satran, et al. Standards Track [Page 206] + +RFC 3720 iSCSI April 2004 + + + [RFC2451] Pereira, R. and R. Adams " The ESP CBC-Mode Cipher + Algorithms", RFC 2451, November 1998. + + [RFC2732] Hinden, R., Carpenter, B. and L. Masinter, "Format for + Literal IPv6 Addresses in URL's", RFC 2451, December + 1999. + + [RFC2945] Wu, T., "The SRP Authentication and Key Exchange + System", RFC 2945, September 2000. + + [RFC3066] Alvestrand, H., "Tags for the Identification of + Languages", STD 47, RFC 3066, January 2001. + + [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of + Internationalized Strings ("stringprep")", RFC 3454, + December 2002. + + [RFC3566] Frankel, S. and H. Herbert, "The AES-XCBC-MAC-96 + Algorithm and Its Use With IPsec", RFC 3566, September + 2003. + + [RFC3686] Housley, R., "Using Advanced Encryption Standard (AES) + Counter Mode with IPsec Encapsulating Security Payload + (ESP)", RFC 3686, January 2004. + + [RFC3722] Bakke, M., "String Profile for Internet Small Computer + Systems Interface (iSCSI) Names", RFC 3722, March + 2004. + + [RFC3723] Aboba, B., Tseng, J., Walker, J., Rangan, V. and F. + Travostino, "Securing Block Storage Protocols over + IP", RFC 3723, March 2004. + + [SAM2] T10/1157D, SCSI Architecture Model - 2 (SAM-2). + + [SBC] NCITS.306-1998, SCSI-3 Block Commands (SBC). + + [SPC3] T10/1416-D, SCSI Primary Commands-3. + + [UNICODE] Unicode Standard Annex #15, "Unicode Normalization + Forms", http://www.unicode.org/unicode/reports/tr15 + + + + + + + + + + +Satran, et al. Standards Track [Page 207] + +RFC 3720 iSCSI April 2004 + + +Informative References + + [BOOT] P. Sarkar, et al., "Bootstrapping Clients using the + iSCSI Protocol", Work in Progress, July 2003. + + [Castagnoli93] G. Castagnoli, S. Braeuer and M. Herrman "Optimization + of Cyclic Redundancy-Check Codes with 24 and 32 Parity + Bits", IEEE Transact. on Communications, Vol. 41, No. + 6, June 1993. + + [CORD] Chadalapaka, M. and R. Elliott, "SCSI Command + Ordering Considerations with iSCSI", Work in Progress. + + [RFC3347] Krueger, M., Haagens, R., Sapuntzakis, C. and M. + Bakke, "Small Computer Systems Interface protocol over + the Internet (iSCSI) Requirements and Design + Considerations", RFC 3347, July 2002. + + [RFC3385] Sheinwald, D., Staran, J., Thaler, P. and V. Cavanna, + "Internet Protocol Small Computer System Interface + (iSCSI) Cyclic Redundancy Check (CRC)/Checksum + Considerations", RFC 3385, September 2002. + + [RFC3721] Bakke M., Hafner, J., Hufferd, J., Voruganti, K. and + M. Krueger, "Internet Small Computer Systems Interface + (iSCSI) Naming and Discovery, RFC 3721, March 2004. + + [SEQ-EXT] Kent, S., "IP Encapsulating Security Payload (ESP)", + Work in Progress, July 2002. + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 208] + +RFC 3720 iSCSI April 2004 + + +Appendix A. Sync and Steering with Fixed Interval Markers + + This appendix presents a simple scheme for synchronization (PDU + boundary retrieval). It uses markers that include synchronization + information placed at fixed intervals in the TCP stream. + + A Marker consists of: + + Byte / 0 | 1 | 2 | 3 | + / | | | | + |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| + +---------------+---------------+---------------+---------------+ + 0| Next-iSCSI-PDU-start pointer - copy #1 | + +---------------+---------------+---------------+---------------+ + 4| Next-iSCSI-PDU-start pointer - copy #2 | + +---------------+---------------+---------------+---------------+ + + The Marker scheme uses payload byte stream counting that includes + every byte placed by iSCSI in the TCP stream except for the markers + themselves. It also excludes any bytes that TCP counts but are not + originated by iSCSI. + + Markers MUST NOT be included in digest calculation. + + The Marker indicates the offset to the next iSCSI PDU header. The + Marker is eight bytes in length and contains two 32-bit offset fields + that indicate how many bytes to skip in the TCP stream in order to + find the next iSCSI PDU header. The marker uses two copies of the + pointer so that a marker that spans a TCP packet boundary should + leave at least one valid copy in one of the packets. + + The structure and semantics of an inserted marker are independent of + the marker interval. + + The use of markers is negotiable. The initiator and target MAY + indicate their readiness to receive and/or send markers during login + separately for each connection. The default is No. + +A.1. Markers At Fixed Intervals + + A marker is inserted at fixed intervals in the TCP byte stream. + During login, each end of the iSCSI session specifies the interval at + which it is willing to receive the marker, or it disables the marker + altogether. If a receiver indicates that it desires a marker, the + sender MAY agree (during negotiation) and provide the marker at the + desired interval. However, in certain environments, a sender that + does not provide markers to a receiver that wants markers may suffer + an appreciable performance degradation. + + + +Satran, et al. Standards Track [Page 209] + +RFC 3720 iSCSI April 2004 + + + The marker interval and the initial marker-less interval are counted + in terms of the bytes placed in the TCP stream data by iSCSI. + + When reduced to iSCSI terms, markers MUST indicate the offset to a + 4-byte word boundary in the stream. The least significant two bits + of each marker word are reserved and are considered 0 for offset + computation. + + Padding iSCSI PDU payloads to 4-byte word boundaries simplifies + marker manipulation. + +A.2. Initial Marker-less Interval + + To enable the connection setup including the Login Phase negotiation, + marking (if any) is only started at the first marker interval after + the end of the Login Phase. However, in order to enable the marker + inclusion and exclusion mechanism to work without knowledge of the + length of the Login Phase, the first marker will be placed in the TCP + stream as if the Marker-less interval had included markers. + + Thus, all markers appear in the stream at locations conforming to the + formula: [(MI + 8) * n - 8] where MI = Marker Interval, n = integer + number. + + For example, if the marker interval is 512 bytes and the login ended + at byte 1003 (first iSCSI placed byte is 0), the first marker will be + inserted after byte 1031 in the stream. + +A.3. Negotiation + + The following operational key=value pairs are used to negotiate the + fixed interval markers. The direction (output or input) is relative + to the initiator. + +A.3.1. OFMarker, IFMarker + + Use: IO + Senders: Initiator and Target + Scope: CO + + OFMarker=<boolean-value> + IFMarker=<boolean-value> + + Default is No. + + Result function is AND. + + + + + +Satran, et al. Standards Track [Page 210] + +RFC 3720 iSCSI April 2004 + + + OFMarker is used to turn on or off the initiator to target markers + on the connection. IFMarker is used to turn on or off the target to + initiator markers on the connection. + + Examples: + + I->OFMarker=Yes,IFMarker=Yes + T->OFMarker=Yes,IFMarker=Yes + + Results in the Marker being used in both directions while: + + I->OFMarker=Yes,IFMarker=Yes + T->OFMarker=Yes,IFMarker=No + + Results in Marker being used from the initiator to the target, but + not from the target to initiator. + +A.3.2. OFMarkInt, IFMarkInt + + Use: IO + Senders: Initiator and Target + Scope: CO + OFMarkInt is Irrelevant when: OFMarker=No + IFMarkInt is Irrelevant when: IFMarker=No + + Offering: + + OFMarkInt=<numeric-range-from-1-to-65535> + IFMarkInt=<numeric-range-from-1-to-65535> + + Responding: + + OFMarkInt=<numeric-value-from-1-to-65535>|Reject + IFMarkInt=<numeric-value-from-1-to-65535>|Reject + + OFMarkInt is used to set the interval for the initiator to target + markers on the connection. IFMarkInt is used to set the interval for + the target to initiator markers on the connection. + + For the offering, the initiator or target indicates the minimum to + maximum interval (in 4-byte words) it wants the markers for one or + both directions. In case it only wants a specific value, only a + single value has to be specified. The responder selects a value + within the minimum and maximum offered or the only value offered or + indicates through the xFMarker key=value its inability to set and/or + receive markers. When the interval is unacceptable the responder + answers with "Reject". Reject is resetting the marker function in + the specified direction (Output or Input) to No. + + + +Satran, et al. Standards Track [Page 211] + +RFC 3720 iSCSI April 2004 + + + The interval is measured from the end of a marker to the beginning of + the next marker. For example, a value of 1024 means 1024 words (4096 + bytes of iSCSI payload between markers). + + The default is 2048. + +Appendix B. Examples + +B.1. Read Operation Example + + +------------------+-----------------------+----------------------+ + |Initiator Function| PDU Type | Target Function | + +------------------+-----------------------+----------------------+ + | Command request |SCSI Command (READ)>>> | | + | (read) | | | + +------------------+-----------------------+----------------------+ + | | |Prepare Data Transfer | + +------------------+-----------------------+----------------------+ + | Receive Data | <<< SCSI Data-In | Send Data | + +------------------+-----------------------+----------------------+ + | Receive Data | <<< SCSI Data-In | Send Data | + +------------------+-----------------------+----------------------+ + | Receive Data | <<< SCSI Data-In | Send Data | + +------------------+-----------------------+----------------------+ + | | <<< SCSI Response |Send Status and Sense | + +------------------+-----------------------+----------------------+ + | Command Complete | | | + +------------------+-----------------------+----------------------+ + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 212] + +RFC 3720 iSCSI April 2004 + + +B.2. Write Operation Example + + +------------------+-----------------------+---------------------+ + |Initiator Function| PDU Type | Target Function | + +------------------+-----------------------+---------------------+ + | Command request |SCSI Command (WRITE)>>>| Receive command | + | (write) | | and queue it | + +------------------+-----------------------+---------------------+ + | | | Process old commands| + +------------------+-----------------------+---------------------+ + | | | Ready to process | + | | <<< R2T | WRITE command | + +------------------+-----------------------+---------------------+ + | Send Data | SCSI Data-Out >>> | Receive Data | + +------------------+-----------------------+---------------------+ + | | <<< R2T | Ready for data | + +------------------+-----------------------+---------------------+ + | | <<< R2T | Ready for data | + +------------------+-----------------------+---------------------+ + | Send Data | SCSI Data-Out >>> | Receive Data | + +------------------+-----------------------+---------------------+ + | Send Data | SCSI Data-Out >>> | Receive Data | + +------------------+-----------------------+---------------------+ + | | <<< SCSI Response |Send Status and Sense| + +------------------+-----------------------+---------------------+ + | Command Complete | | | + +------------------+-----------------------+---------------------+ + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 213] + +RFC 3720 iSCSI April 2004 + + +B.3. R2TSN/DataSN Use Examples + + Output (write) data DataSN/R2TSN Example + + +------------------+-----------------------+----------------------+ + |Initiator Function| PDU Type & Content | Target Function | + +------------------+-----------------------+----------------------+ + | Command request |SCSI Command (WRITE)>>>| Receive command | + | (write) | | and queue it | + +------------------+-----------------------+----------------------+ + | | | Process old commands | + +------------------+-----------------------+----------------------+ + | | <<< R2T | Ready for data | + | | R2TSN = 0 | | + +------------------+-----------------------+----------------------+ + | | <<< R2T | Ready for more data | + | | R2TSN = 1 | | + +------------------+-----------------------+----------------------+ + | Send Data | SCSI Data-Out >>> | Receive Data | + | for R2TSN 0 | DataSN = 0, F=0 | | + +------------------+-----------------------+----------------------+ + | Send Data | SCSI Data-Out >>> | Receive Data | + | for R2TSN 0 | DataSN = 1, F=1 | | + +------------------+-----------------------+----------------------+ + | Send Data | SCSI Data >>> | Receive Data | + | for R2TSN 1 | DataSN = 0, F=1 | | + +------------------+-----------------------+----------------------+ + | | <<< SCSI Response |Send Status and Sense | + | | ExpDataSN = 0 | | + +------------------+-----------------------+----------------------+ + | Command Complete | | | + +------------------+-----------------------+----------------------+ + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 214] + +RFC 3720 iSCSI April 2004 + + + Input (read) data DataSN Example + + +------------------+-----------------------+----------------------+ + |Initiator Function| PDU Type | Target Function | + +------------------+-----------------------+----------------------+ + | Command request |SCSI Command (READ)>>> | | + | (read) | | | + +------------------+-----------------------+----------------------+ + | | | Prepare Data Transfer| + +------------------+-----------------------+----------------------+ + | Receive Data | <<< SCSI Data-In | Send Data | + | | DataSN = 0, F=0 | | + +------------------+-----------------------+----------------------+ + | Receive Data | <<< SCSI Data-In | Send Data | + | | DataSN = 1, F=0 | | + +------------------+-----------------------+----------------------+ + | Receive Data | <<< SCSI Data-In | Send Data | + | | DataSN = 2, F=1 | | + +------------------+-----------------------+----------------------+ + | | <<< SCSI Response |Send Status and Sense | + | | ExpDataSN = 3 | | + +------------------+-----------------------+----------------------+ + | Command Complete | | | + +------------------+-----------------------+----------------------+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 215] + +RFC 3720 iSCSI April 2004 + + + Bidirectional DataSN Example + + +------------------+-----------------------+----------------------+ + |Initiator Function| PDU Type | Target Function | + +------------------+-----------------------+----------------------+ + | Command request |SCSI Command >>> | | + | (Read-Write) | Read-Write | | + +------------------+-----------------------+----------------------+ + | | | Process old commands | + +------------------+-----------------------+----------------------+ + | | <<< R2T | Ready to process | + | | R2TSN = 0 | WRITE command | + +------------------+-----------------------+----------------------+ + | * Receive Data | <<< SCSI Data-In | Send Data | + | | DataSN = 1, F=0 | | + +------------------+-----------------------+----------------------+ + | * Receive Data | <<< SCSI Data-In | Send Data | + | | DataSN = 2, F=1 | | + +------------------+-----------------------+----------------------+ + | * Send Data | SCSI Data-Out >>> | Receive Data | + | for R2TSN 0 | DataSN = 0, F=1 | | + +------------------+-----------------------+----------------------+ + | | <<< SCSI Response |Send Status and Sense | + | | ExpDataSN = 3 | | + +------------------+-----------------------+----------------------+ + | Command Complete | | | + +------------------+-----------------------+----------------------+ + + *) Send data and Receive Data may be transferred simultaneously as in + an atomic Read-Old-Write-New or sequentially as in an atomic + Read-Update-Write (in the latter case the R2T may follow the received + data). + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 216] + +RFC 3720 iSCSI April 2004 + + + Unsolicited and immediate output (write) data with DataSN Example + + +------------------+-----------------------+----------------------+ + |Initiator Function| PDU Type & Content | Target Function | + +------------------+-----------------------+----------------------+ + | Command request |SCSI Command (WRITE)>>>| Receive command | + | (write) |F=0 | and data | + |+ Immediate data | | and queue it | + +------------------+-----------------------+----------------------+ + | Send Unsolicited | SCSI Write Data >>> | Receive more Data | + | Data | DataSN = 0, F=1 | | + +------------------+-----------------------+----------------------+ + | | | Process old commands | + +------------------+-----------------------+----------------------+ + | | <<< R2T | Ready for more data | + | | R2TSN = 0 | | + +------------------+-----------------------+----------------------+ + | Send Data | SCSI Write Data >>> | Receive Data | + | for R2TSN 0 | DataSN = 0, F=1 | | + +------------------+-----------------------+----------------------+ + | | <<< SCSI Response |Send Status and Sense | + | | | | + +------------------+-----------------------+----------------------+ + | Command Complete | | | + +------------------+-----------------------+----------------------+ + +B.4. CRC Examples + + N.B. all Values are Hexadecimal + + 32 bytes of zeroes: + + Byte: 0 1 2 3 + + 0: 00 00 00 00 + ... + 28: 00 00 00 00 + + CRC: aa 36 91 8a + + 32 bytes of ones: + + Byte: 0 1 2 3 + + 0: ff ff ff ff + ... + 28: ff ff ff ff + + + + +Satran, et al. Standards Track [Page 217] + +RFC 3720 iSCSI April 2004 + + + CRC: 43 ab a8 62 + + 32 bytes of incrementing 00..1f: + + Byte: 0 1 2 3 + + 0: 00 01 02 03 + ... + 28: 1c 1d 1e 1f + + CRC: 4e 79 dd 46 + + 32 bytes of decrementing 1f..00: + + Byte: 0 1 2 3 + + 0: 1f 1e 1d 1c + ... + 28: 03 02 01 00 + + CRC: 5c db 3f 11 + + An iSCSI - SCSI Read (10) Command PDU + + Byte: 0 1 2 3 + + 0: 01 c0 00 00 + 4: 00 00 00 00 + 8: 00 00 00 00 + 12: 00 00 00 00 + 16: 14 00 00 00 + 20: 00 00 04 00 + 24: 00 00 00 14 + 28: 00 00 00 18 + 32: 28 00 00 00 + 36: 00 00 00 00 + 40: 02 00 00 00 + 44: 00 00 00 00 + + CRC: 56 3a 96 d9 + + + + + + + + + + + +Satran, et al. Standards Track [Page 218] + +RFC 3720 iSCSI April 2004 + + +Appendix C. Login Phase Examples + + In the first example, the initiator and target authenticate each + other via Kerberos: + + I-> Login (CSG,NSG=0,1 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=KRB5,SRP,None + + T-> Login (CSG,NSG=0,0 T=0) + AuthMethod=KRB5 + + I-> Login (CSG,NSG=0,1 T=1) + KRB_AP_REQ=<krb_ap_req> + + (krb_ap_req contains the Kerberos V5 ticket and authenticator + with MUTUAL-REQUIRED set in the ap-options field) + + If the authentication is successful, the target proceeds with: + + T-> Login (CSG,NSG=0,1 T=1) + KRB_AP_REP=<krb_ap_rep> + + (krb_ap_rep is the Kerberos V5 mutual authentication reply) + + If the authentication is successful, the initiator may proceed + with: + + I-> Login (CSG,NSG=1,0 T=0) FirstBurstLength=8192 + T-> Login (CSG,NSG=1,0 T=0) FirstBurstLength=4096 + MaxBurstLength=8192 + I-> Login (CSG,NSG=1,0 T=0) MaxBurstLength=8192 + ... more iSCSI Operational Parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... more iSCSI Operational Parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + + + + + + +Satran, et al. Standards Track [Page 219] + +RFC 3720 iSCSI April 2004 + + + If the initiator's authentication by the target is not + successful, the target responds with: + + T-> Login "login reject" + + instead of the Login KRB_AP_REP message, and terminates the + connection. + + If the target's authentication by the initiator is not + successful, the initiator terminates the connection (without + responding to the Login KRB_AP_REP message). + + In the next example only the initiator is authenticated by the + target via Kerberos: + + I-> Login (CSG,NSG=0,1 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=SRP,KRB5,None + + T-> Login-PR (CSG,NSG=0,0 T=0) + AuthMethod=KRB5 + + I-> Login (CSG,NSG=0,1 T=1) + KRB_AP_REQ=krb_ap_req + + (MUTUAL-REQUIRED not set in the ap-options field of krb_ap_req) + + If the authentication is successful, the target proceeds with: + + T-> Login (CSG,NSG=0,1 T=1) + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + . . . + + T-> Login (CSG,NSG=1,3 T=1)"login accept" + + + + + + + + + + +Satran, et al. Standards Track [Page 220] + +RFC 3720 iSCSI April 2004 + + + In the next example, the initiator and target authenticate each + other via SPKM1: + + I-> Login (CSG,NSG=0,1 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=SPKM1,KRB5,None + + T-> Login (CSG,NSG=0,0 T=0) + AuthMethod=SPKM1 + + I-> Login (CSG,NSG=0,0 T=0) + SPKM_REQ=<spkm-req> + + (spkm-req is the SPKM-REQ token with the mutual-state bit in the + options field of the REQ-TOKEN set) + + T-> Login (CSG,NSG=0,0 T=0) + SPKM_REP_TI=<spkm-rep-ti> + + If the authentication is successful, the initiator proceeds: + + I-> Login (CSG,NSG=0,1 T=1) + SPKM_REP_IT=<spkm-rep-it> + + If the authentication is successful, the target proceeds with: + + T-> Login (CSG,NSG=0,1 T=1) + + The initiator may proceed: + + I-> Login (CSG,NSG=1,0 T=0) ... iSCSI parameters + T-> Login (CSG,NSG=1,0 T=0) ... iSCSI parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + + If the target's authentication by the initiator is not + successful, the initiator terminates the connection (without + responding to the Login SPKM_REP_TI message). + + + + + + +Satran, et al. Standards Track [Page 221] + +RFC 3720 iSCSI April 2004 + + + If the initiator's authentication by the target is not + successful, the target responds with: + + T-> Login "login reject" + + instead of the Login "proceed and change stage" message, and + terminates the connection. + + + In the next example, the initiator and target authenticate each + other via SPKM2: + + I-> Login (CSG,NSG=0,0 T=0) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=SPKM1,SPKM2 + + T-> Login-PR (CSG,NSG=0,0 T=0) + AuthMethod=SPKM2 + + I-> Login (CSG,NSG=0,1 T=1) + SPKM_REQ=<spkm-req> + + (spkm-req is the SPKM-REQ token with the mutual-state bit in the + options field of the REQ-TOKEN not set) + + If the authentication is successful, the target proceeds with: + + T-> Login (CSG,NSG=0,1 T=1) + + The initiator may proceed: + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + + + + + + +Satran, et al. Standards Track [Page 222] + +RFC 3720 iSCSI April 2004 + + + In the next example, the initiator and target authenticate each + other via SRP: + + I-> Login (CSG,NSG=0,1 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=KRB5,SRP,None + + T-> Login-PR (CSG,NSG=0,0 T=0) + AuthMethod=SRP + + I-> Login (CSG,NSG=0,0 T=0) + SRP_U=<user> + TargetAuth=Yes + + T-> Login (CSG,NSG=0,0 T=0) + SRP_GROUP=SRP-1536,SRP-1024 + SRP_s=<s> + + I-> Login (CSG,NSG=0,0 T=0) + SRP_GROUP=SRP-1536 + SRP_A=<A> + + T-> Login (CSG,NSG=0,0 T=0) + SRP_B=<B> + + I-> Login (CSG,NSG=0,1 T=1) + SRP_M=<M> + + If the initiator authentication is successful, the target + proceeds: + + T-> Login (CSG,NSG=0,1 T=1) + SRP_HM=<H(A | M | K)> + + Where N, g, s, A, B, M, and H(A | M | K) are defined in [RFC2945]. + + If the target authentication is not successful, the initiator + terminates the connection; otherwise, it proceeds. + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + + + + + +Satran, et al. Standards Track [Page 223] + +RFC 3720 iSCSI April 2004 + + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + If the initiator authentication is not successful, the target + responds with: + + T-> Login "login reject" + + Instead of the T-> Login SRP_HM=<H(A | M | K)> message and + terminates the connection. + + In the next example, the initiator and target authenticate each + other via SRP: + + I-> Login (CSG,NSG=0,1 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=KRB5,SRP,None + + T-> Login-PR (CSG,NSG=0,0 T=0) + AuthMethod=SRP + + I-> Login (CSG,NSG=0,0 T=0) + SRP_U=<user> + TargetAuth=No + + T-> Login (CSG,NSG=0,0 T=0) + SRP_GROUP=SRP-1536 + SRP_s=<s> + + I-> Login (CSG,NSG=0,0 T=0) + SRP_GROUP=SRP-1536 + SRP_A=<A> + + T-> Login (CSG,NSG=0,0 T=0) + SRP_B=<B> + + I-> Login (CSG,NSG=0,1 T=1) + SRP_M=<M> + + If the initiator authentication is successful, the target + proceeds: + + T-> Login (CSG,NSG=0,1 T=1) + + + +Satran, et al. Standards Track [Page 224] + +RFC 3720 iSCSI April 2004 + + + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + In the next example the initiator and target authenticate each other + via CHAP: + + I-> Login (CSG,NSG=0,0 T=0) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=KRB5,CHAP,None + + T-> Login-PR (CSG,NSG=0,0 T=0) + AuthMethod=CHAP + + I-> Login (CSG,NSG=0,0 T=0) + CHAP_A=<A1,A2> + + T-> Login (CSG,NSG=0,0 T=0) + CHAP_A=<A1> + CHAP_I=<I> + CHAP_C=<C> + + I-> Login (CSG,NSG=0,1 T=1) + CHAP_N=<N> + CHAP_R=<R> + CHAP_I=<I> + CHAP_C=<C> + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 225] + +RFC 3720 iSCSI April 2004 + + + If the initiator authentication is successful, the target + proceeds: + + T-> Login (CSG,NSG=0,1 T=1) + CHAP_N=<N> + CHAP_R=<R> + + If the target authentication is not successful, the initiator + aborts the connection; otherwise, it proceeds. + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + If the initiator authentication is not successful, the target + responds with: + + T-> Login "login reject" + + Instead of the Login CHAP_R=<response> "proceed and change + stage" message and terminates the connection. + + In the next example, only the initiator is authenticated by the + target via CHAP: + + I-> Login (CSG,NSG=0,1 T=0) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=KRB5,CHAP,None + + T-> Login-PR (CSG,NSG=0,0 T=0) + AuthMethod=CHAP + + I-> Login (CSG,NSG=0,0 T=0) + CHAP_A=<A1,A2> + + + + + + + + +Satran, et al. Standards Track [Page 226] + +RFC 3720 iSCSI April 2004 + + + T-> Login (CSG,NSG=0,0 T=0) + CHAP_A=<A1> + CHAP_I=<I> + CHAP_C=<C> + + I-> Login (CSG,NSG=0,1 T=1) + CHAP_N=<N> + CHAP_R=<R> + + If the initiator authentication is successful, the target + proceeds: + + T-> Login (CSG,NSG=0,1 T=1) + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + In the next example, the initiator does not offer any security + parameters. It therefore may offer iSCSI parameters on the Login PDU + with the T bit set to 1, and the target may respond with a final + Login Response PDU immediately: + + I-> Login (CSG,NSG=1,3 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + ... iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + ... ISCSI parameters + + In the next example, the initiator does offer security + parameters on the Login PDU, but the target does not choose + any (i.e., chooses the "None" values): + + I-> Login (CSG,NSG=0,1 T=1) + InitiatorName=iqn.1999-07.com.os:hostid.77 + TargetName=iqn.1999-07.com.example:diskarray.sn.88 + AuthMethod=KRB5,SRP,None + + + +Satran, et al. Standards Track [Page 227] + +RFC 3720 iSCSI April 2004 + + + + T-> Login-PR (CSG,NSG=0,1 T=1) + AuthMethod=None + + I-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + T-> Login (CSG,NSG=1,0 T=0) + ... iSCSI parameters + + And at the end: + + I-> Login (CSG,NSG=1,3 T=1) + optional iSCSI parameters + + T-> Login (CSG,NSG=1,3 T=1) "login accept" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 228] + +RFC 3720 iSCSI April 2004 + + +Appendix D. SendTargets Operation + + To reduce the amount of configuration required on an initiator, iSCSI + provides the SendTargets text request. The initiator uses the + SendTargets request to get a list of targets to which it may have + access, as well as the list of addresses (IP address and TCP port) on + which these targets may be accessed. + + To make use of SendTargets, an initiator must first establish one of + two types of sessions. If the initiator establishes the session + using the key "SessionType=Discovery", the session is a discovery + session, and a target name does not need to be specified. Otherwise, + the session is a normal, operational session. The SendTargets + command MUST only be sent during the Full Feature Phase of a normal + or discovery session. + + A system that contains targets MUST support discovery sessions on + each of its iSCSI IP address-port pairs, and MUST support the + SendTargets command on the discovery session. In a discovery + session, a target MUST return all path information (target name and + IP address-port pairs and portal group tags) for the targets on the + target network entity which the requesting initiator is authorized to + access. + + A target MUST support the SendTargets command on operational + sessions; these will only return path information about the target to + which the session is connected, and do not need to return information + about other target names that may be defined in the responding + system. + + An initiator MAY make use of the SendTargets as it sees fit. + + A SendTargets command consists of a single Text request PDU. This + PDU contains exactly one text key and value. The text key MUST be + SendTargets. The expected response depends upon the value, as well + as whether the session is a discovery or operational session. + + The value must be one of: + + All + + The initiator is requesting that information on all relevant + targets known to the implementation be returned. This value + MUST be supported on a discovery session, and MUST NOT be + supported on an operational session. + + + + + + +Satran, et al. Standards Track [Page 229] + +RFC 3720 iSCSI April 2004 + + + <iSCSI-target-name> + + If an iSCSI target name is specified, the session should respond + with addresses for only the named target, if possible. This + value MUST be supported on discovery sessions. A discovery + session MUST be capable of returning addresses for those + targets that would have been returned had value=All had been + designated. + + <nothing> + + The session should only respond with addresses for the target to + which the session is logged in. This MUST be supported on + operational sessions, and MUST NOT return targets other than + the one to which the session is logged in. + + The response to this command is a text response that contains a list + of zero or more targets and, optionally, their addresses. Each + target is returned as a target record. A target record begins with + the TargetName text key, followed by a list of TargetAddress text + keys, and bounded by the end of the text response or the next + TargetName key, which begins a new record. No text keys other than + TargetName and TargetAddress are permitted within a SendTargets + response. + + For the format of the TargetName, see Section 12.4 TargetName. + + In a discovery session, a target MAY respond to a SendTargets request + with its complete list of targets, or with a list of targets that is + based on the name of the initiator logged in to the session. + + A SendTargets response MUST NOT contain target names if there are no + targets for the requesting initiator to access. + + Each target record returned includes zero or more TargetAddress + fields. + + Each target record starts with one text key of the form: + + TargetName=<target-name-goes-here> + + Followed by zero or more address keys of the form: + + TargetAddress=<hostname-or-ipaddress>[:<tcp-port>], + <portal-group-tag> + + The hostname-or-ipaddress contains a domain name, IPv4 address, or + IPv6 address, as specified for the TargetAddress key. + + + +Satran, et al. Standards Track [Page 230] + +RFC 3720 iSCSI April 2004 + + + A hostname-or-ipaddress duplicated in TargetAddress responses for a + given node (the port is absent or equal) would probably indicate that + multiple address families are in use at once (IPV6 and IPV4). + + Each TargetAddress belongs to a portal group, identified by its + numeric portal group tag (as in Section 12.9 TargetPortalGroupTag). + The iSCSI target name, together with this tag, constitutes the SCSI + port identifier; the tag only needs to be unique within a given + target's name list of addresses. + + Multiple-connection sessions can span iSCSI addresses that belong to + the same portal group. + + Multiple-connection sessions cannot span iSCSI addresses that belong + to different portal groups. + + If a SendTargets response reports an iSCSI address for a target, it + SHOULD also report all other addresses in its portal group in the + same response. + + A SendTargets text response can be longer than a single Text Response + PDU, and makes use of the long text responses as specified. + + After obtaining a list of targets from the discovery target session, + an iSCSI initiator may initiate new sessions to log in to the + discovered targets for full operation. The initiator MAY keep the + discovery session open, and MAY send subsequent SendTargets commands + to discover new targets. + + Examples: + + This example is the SendTargets response from a single target that + has no other interface ports. + + Initiator sends text request that contains: + + SendTargets=All + + Target sends a text response that contains: + + TargetName=iqn.1993-11.com.example:diskarray.sn.8675309 + + All the target had to return in the simple case was the target name. + It is assumed by the initiator that the IP address and TCP port for + this target are the same as used on the current connection to the + default iSCSI target. + + + + + +Satran, et al. Standards Track [Page 231] + +RFC 3720 iSCSI April 2004 + + + The next example has two internal iSCSI targets, each accessible via + two different ports with different IP addresses. The following is + the text response: + + TargetName=iqn.1993-11.com.example:diskarray.sn.8675309 + TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.45:3000,2 + TargetName=iqn.1993-11.com.example:diskarray.sn.1234567 + TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.45:3000,2 + + Both targets share both addresses; the multiple addresses are likely + used to provide multi-path support. The initiator may connect to + either target name on either address. Each of the addresses has its + own portal group tag; they do not support spanning + multiple-connection sessions with each other. Keep in mind that the + portal group tags for the two named targets are independent of one + another; portal group "1" on the first target is not necessarily the + same as portal group "1" on the second target. + + In the above example, a DNS host name or an IPv6 address could have + been returned instead of an IPv4 address. + + The next text response shows a target that supports spanning sessions + across multiple addresses, and further illustrates the use of the + portal group tags: + + TargetName=iqn.1993-11.com.example:diskarray.sn.8675309 + + TargetAddress=10.1.0.45:3000,1 TargetAddress=10.1.1.46:3000,1 + TargetAddress=10.1.0.47:3000,2 TargetAddress=10.1.1.48:3000,2 + TargetAddress=10.1.1.49:3000,3 + + In this example, any of the target addresses can be used to reach the + same target. A single-connection session can be established to any + of these TCP addresses. A multiple-connection session could span + addresses .45 and .46 or .47 and .48, but cannot span any other + combination. A TargetAddress with its own tag (.49) cannot be + combined with any other address within the same session. + + This SendTargets response does not indicate whether .49 supports + multiple connections per session; it is communicated via the + MaxConnections text key upon login to the target. + + + + + + + + + + +Satran, et al. Standards Track [Page 232] + +RFC 3720 iSCSI April 2004 + + +Appendix E. Algorithmic Presentation of Error Recovery Classes + + This appendix illustrates the error recovery classes using a + pseudo-programming-language. The procedure names are chosen to be + obvious to most implementers. Each of the recovery classes described + has initiator procedures as well as target procedures. These + algorithms focus on outlining the mechanics of error recovery + classes, and do not exhaustively describe all other aspects/cases. + Examples of this approach are: + + + - Handling for only certain Opcode types is shown. + + - Only certain reason codes (e.g., Recovery in Logout command) + are outlined. + + - Resultant cases, such as recovery of Synchronization on a + header digest error are considered out-of-scope in these + algorithms. In this particular example, a header digest error + may lead to connection recovery if some type of sync and + steering layer is not implemented. + + These algorithms strive to convey the iSCSI error recovery concepts + in the simplest terms, and are not designed to be optimal. + +E.1. General Data Structure and Procedure Description + + This section defines the procedures and data structures that are + commonly used by all the error recovery algorithms. The structures + may not be the exhaustive representations of what is required for a + typical implementation. + + Data structure definitions - + struct TransferContext { + int TargetTransferTag; + int ExpectedDataSN; + }; + + struct TCB { /* task control block */ + Boolean SoFarInOrder; + int ExpectedDataSN; /* used for both R2Ts, and Data */ + int MissingDataSNList[MaxMissingDPDU]; + Boolean FbitReceived; + Boolean StatusXferd; + Boolean CurrentlyAllegiant; + int ActiveR2Ts; + int Response; + char *Reason; + + + +Satran, et al. Standards Track [Page 233] + +RFC 3720 iSCSI April 2004 + + + struct TransferContext + TransferContextList[MaxOutStandingR2T]; + int InitiatorTaskTag; + int CmdSN; + + int SNACK_Tag; + + }; + + struct Connection { + struct Session SessionReference; + Boolean SoFarInOrder; + int CID; + int State; + + int CurrentTimeout; + int ExpectedStatSN; + int MissingStatSNList[MaxMissingSPDU]; + Boolean PerformConnectionCleanup; + }; + + struct Session { + int NumConnections; + int CmdSN; + int Maxconnections; + int ErrorRecoveryLevel; + struct iSCSIEndpoint OtherEndInfo; + struct Connection ConnectionList[MaxSupportedConns]; + }; + + Procedure descriptions - + Receive-a-In-PDU(transport connection, inbound PDU); + check-basic-validity(inbound PDU); + Start-Timer(timeout handler, argument, timeout value); + Build-And-Send-Reject(transport connection, bad PDU, reason code); + +E.2. Within-command Error Recovery Algorithms + +E.2.1. Procedure Descriptions + + Recover-Data-if-Possible(last required DataSN, task control + block); + Build-And-Send-DSnack(task control block); + Build-And-Send-RDSnack(task control block); + Build-And-Send-Abort(task control block); + SCSI-Task-Completion(task control block); + Build-And-Send-A-Data-Burst(transport connection, data-descriptor, + task control block); + + + +Satran, et al. Standards Track [Page 234] + +RFC 3720 iSCSI April 2004 + + + Build-And-Send-R2T(transport connection, data-descriptor, + task control block); + Build-And-Send-Status(transport connection, task control block); + Transfer-Context-Timeout-Handler(transfer context); + + + Notes: + + - One procedure used in this section: Handle-Status-SNACK- + request is defined in Within-connection recovery algorithms. + + - The Response processing pseudo-code, shown in the target + algorithms, applies to all solicited PDUs that carry StatSN - + SCSI Response, Text Response etc. + +E.2.2. Initiator Algorithms + +Recover-Data-if-Possible(LastRequiredDataSN, TCB) +{ + if (operational ErrorRecoveryLevel > 0) { + if (# of missing PDUs is trackable) { + Note the missing DataSNs in TCB. + if (the task spanned a change in + MaxRecvDataSegmentLength) { + if (TCB.StatusXferd is TRUE) + drop the status PDU; + Build-And-Send-RDSnack(TCB); + } else { + Build-And-Send-DSnack(TCB); + } + } else { + TCB.Reason = "Protocol service CRC error"; + } + } else { + TCB.Reason = "Protocol service CRC error"; + } + if (TCB.Reason == "Protocol service CRC error") { + Clear the missing PDU list in the TCB. + if (TCB.StatusXferd is not TRUE) + Build-And-Send-Abort(TCB); + } +} + +Receive-a-In-PDU(Connection, CurrentPDU) +{ + check-basic-validity(CurrentPDU); + if (Header-Digest-Bad) discard, return; + Retrieve TCB for CurrentPDU.InitiatorTaskTag. + + + +Satran, et al. Standards Track [Page 235] + +RFC 3720 iSCSI April 2004 + + + if ((CurrentPDU.type == Data) + or (CurrentPDU.type = R2T)) { + if (Data-Digest-Bad for Data) { + send-data-SNACK = TRUE; + LastRequiredDataSN = CurrentPDU.DataSN; + } else { + if (TCB.SoFarInOrder = TRUE) { + if (current DataSN is expected) { + Increment TCB.ExpectedDataSN. + } else { + + TCB.SoFarInOrder = FALSE; + send-data-SNACK = TRUE; + } + } else { + if (current DataSN was considered missing) { + remove current DataSN from missing PDU list. + } else if (current DataSN is higher than expected) +{ + send-data-SNACK = TRUE; + } else { + discard, return; + } + Adjust TCB.ExpectedDataSN if appropriate. + } + LastRequiredDataSN = CurrentPDU.DataSN - 1; + } + if (send-data-SNACK is TRUE and + task is not already considered failed) { + Recover-Data-if-Possible(LastRequiredDataSN, TCB); + } + if (missing data PDU list is empty) { + TCB.SoFarInOrder = TRUE; + } + if (CurrentPDU.type == R2T) { + Increment ActiveR2Ts for this task. + + Create a data-descriptor for the data burst. + Build-And-Send-A-Data-Burst(Connection, data-descriptor, + + TCB); + } + } else if (CurrentPDU.type == Response) { + if (Data-Digest-Bad) { + send-status-SNACK = TRUE; + } else { + TCB.StatusXferd = TRUE; + Store the status information in TCB. + + + +Satran, et al. Standards Track [Page 236] + +RFC 3720 iSCSI April 2004 + + + if (ExpDataSN does not match) { + TCB.SoFarInOrder = FALSE; + Recover-Data-if-Possible(current DataSN, TCB); + } + if (missing data PDU list is empty) { + TCB.SoFarInOrder = TRUE; + } + } + } else { /* REST UNRELATED TO WITHIN-COMMAND-RECOVERY, NOT + SHOWN */ + } + if ((TCB.SoFarInOrder == TRUE) and + (TCB.StatusXferd == TRUE)) { + SCSI-Task-Completion(TCB); + } +} + +E.2.3. Target Algorithms + +Receive-a-In-PDU(Connection, CurrentPDU) +{ + check-basic-validity(CurrentPDU); + if (Header-Digest-Bad) discard, return; + Retrieve TCB for CurrentPDU.InitiatorTaskTag. + if (CurrentPDU.type == Data) { + Retrieve TContext from CurrentPDU.TargetTransferTag; + if (Data-Digest-Bad) { + Build-And-Send-Reject(Connection, CurrentPDU, + Payload-Digest-Error); + Note the missing data PDUs in MissingDataRange[]. + send-recovery-R2T = TRUE; + } else { + if (current DataSN is not expected) { + Note the missing data PDUs in MissingDataRange[]. + send-recovery-R2T = TRUE; + } + if (CurrentPDU.Fbit == TRUE) { + if (current PDU is solicited) { + Decrement TCB.ActiveR2Ts. + } + if ((current PDU is unsolicited and + data received is less than I/O length and + data received is less than FirstBurstLength) + or (current PDU is solicited and the length of + this burst is less than expected)) { + send-recovery-R2T = TRUE; + Note the missing data in MissingDataRange[]. + } + + + +Satran, et al. Standards Track [Page 237] + +RFC 3720 iSCSI April 2004 + + + } + } + Increment TContext.ExpectedDataSN. + if (send-recovery-R2T is TRUE and + task is not already considered failed) { + if (operational ErrorRecoveryLevel > 0) { + Increment TCB.ActiveR2Ts. + Create a data-descriptor for the data burst + from MissingDataRange. + Build-And-Send-R2T(Connection, data-descriptor, TCB); + } else { + if (current PDU is the last unsolicited) + TCB.Reason = "Not enough unsolicited data"; + else + TCB.Reason = "Protocol service CRC error"; + } + } + if (TCB.ActiveR2Ts == 0) { + Build-And-Send-Status(Connection, TCB); + } + } else if (CurrentPDU.type == SNACK) { + snack-failure = FALSE; + if (operational ErrorRecoveryLevel > 0) { + if (CurrentPDU.type == Data/R2T) { + if (the request is satisfiable) { + + if (request for Data) { + Create a data-descriptor for the data burst + from BegRun and RunLength. + Build-And-Send-A-Data-Burst(Connection, + + data-descriptor, TCB); + } else { /* R2T */ + Create a data-descriptor for the data burst + from BegRun and RunLength. + Build-And-Send-R2T(Connection, data-descriptor, + TCB); + } + } else { + snack-failure = TRUE; + } + } else if (CurrentPDU.type == status) { + Handle-Status-SNACK-request(Connection, CurrentPDU); + } else if (CurrentPDU.type == DataACK) { + Consider all data upto CurrentPDU.BegRun as + acknowledged. + Free up the retransmission resources for that data. + } else if (CurrentPDU.type == R-Data SNACK) { + + + +Satran, et al. Standards Track [Page 238] + +RFC 3720 iSCSI April 2004 + + + Create a data descriptor for a data burst covering + all unacknowledged data. + Build-And-Send-A-Data-Burst(Connection, + data-descriptor, TCB); + TCB.SNACK_Tag = CurrentPDU.SNACK_Tag; + if (there's no more data to send) { + Build-And-Send-Status(Connection, TCB); + } + } + } else { /* operational ErrorRecoveryLevel = 0 */ + snack-failure = TRUE; + + } + if (snack-failure == TRUE) { + Build-And-Send-Reject(Connection, CurrentPDU, + SNACK-Reject); + if (TCB.StatusXferd != TRUE) { + TCB.Reason = "SNACK Rejected"; + Build-And-Send-Status(Connection, TCB); + } + } + + } else { /* REST UNRELATED TO WITHIN-COMMAND-RECOVERY, NOT SHOWN */ + } +} + +Transfer-Context-Timeout-Handler(TContext) +{ + Retrieve TCB and Connection from TContext. + Decrement TCB.ActiveR2Ts. + if (operational ErrorRecoveryLevel > 0 and + task is not already considered failed) { + Note the missing data PDUs in MissingDataRange[]. + Create a data-descriptor for the data burst + from MissingDataRange[]. + Build-And-Send-R2T(Connection, data-descriptor, TCB); + } else { + TCB.Reason = "Protocol service CRC error"; + if (TCB.ActiveR2Ts = 0) { + Build-And-Send-Status(Connection, TCB); + } + } +} + + + + + + + + +Satran, et al. Standards Track [Page 239] + +RFC 3720 iSCSI April 2004 + + +E.3. Within-connection Recovery Algorithms + +E.3.1. Procedure Descriptions + +Procedure descriptions: +Recover-Status-if-Possible(transport connection, + currently received PDU); +Evaluate-a-StatSN(transport connection, currently received PDU); +Retransmit-Command-if-Possible(transport connection, CmdSN); +Build-And-Send-SSnack(transport connection); +Build-And-Send-Command(transport connection, task control block); +Command-Acknowledge-Timeout-Handler(task control block); +Status-Expect-Timeout-Handler(transport connection); +Build-And-Send-Nop-Out(transport connection); +Handle-Status-SNACK-request(transport connection, status SNACK +PDU); +Retransmit-Status-Burst(status SNACK, task control block); +Is-Acknowledged(beginning StatSN, run length); + +Implementation-specific tunables: +InitiatorProactiveSNACKEnabled + + Notes: + + - The initiator algorithms only deal with unsolicited Nop-In PDUs + for generating status SNACKs. A solicited Nop-In PDU has an + assigned StatSN, which, when out of order, could trigger the + out of order StatSN handling in Within-command algorithms, + again leading to Recover-Status-if-Possible. + + + - The pseudo-code shown may result in the retransmission of + unacknowledged commands in more cases than necessary. This + will not, however, affect the correctness of the operation + because the target is required to discard the duplicate CmdSNs. + + - The procedure Build-And-Send-Async is defined in the Connection + recovery algorithms. + + - The procedure Status-Expect-Timeout-Handler describes how + initiators may proactively attempt to retrieve the Status if + they so choose. This procedure is assumed to be triggered much + before the standard ULP timeout. + + + + + + + + +Satran, et al. Standards Track [Page 240] + +RFC 3720 iSCSI April 2004 + + +E.3.2. Initiator Algorithms + +Recover-Status-if-Possible(Connection, CurrentPDU) +{ + if ((Connection.state == LOGGED_IN) and + connection is not already considered failed) { + if (operational ErrorRecoveryLevel > 0) { + if (# of missing PDUs is trackable) { + Note the missing StatSNs in Connection + that were not already requested with SNACK; + Build-And-Send-SSnack(Connection); + } else { + Connection.PerformConnectionCleanup = TRUE; + } + } else { + Connection.PerformConnectionCleanup = TRUE; + } + if (Connection.PerformConnectionCleanup == TRUE) { + Start-Timer(Connection-Cleanup-Handler, Connection, 0); + } + } +} + +Retransmit-Command-if-Possible(Connection, CmdSN) +{ + + if (operational ErrorRecoveryLevel > 0) { + Retrieve the InitiatorTaskTag, and thus TCB for the CmdSN. + Build-And-Send-Command(Connection, TCB); + } +} + +Evaluate-a-StatSN(Connection, CurrentPDU) +{ + send-status-SNACK = FALSE; + if (Connection.SoFarInOrder == TRUE) { + if (current StatSN is the expected) { + Increment Connection.ExpectedStatSN. + } else { + Connection.SoFarInOrder = FALSE; + send-status-SNACK = TRUE; + } + } else { + if (current StatSN was considered missing) { + remove current StatSN from the missing list. + } else { + if (current StatSN is higher than expected){ + send-status-SNACK = TRUE; + + + +Satran, et al. Standards Track [Page 241] + +RFC 3720 iSCSI April 2004 + + + } else { + send-status-SNACK = FALSE; + discard the PDU; + } + } + Adjust Connection.ExpectedStatSN if appropriate. + if (missing StatSN list is empty) { + Connection.SoFarInOrder = TRUE; + } + } + return send-status-SNACK; +} + +Receive-a-In-PDU(Connection, CurrentPDU) +{ + check-basic-validity(CurrentPDU); + if (Header-Digest-Bad) discard, return; + Retrieve TCB for CurrentPDU.InitiatorTaskTag. + if (CurrentPDU.type == Nop-In) { + if (the PDU is unsolicited) { + if (current StatSN is not expected) { + Recover-Status-if-Possible(Connection, + CurrentPDU); + } + if (current ExpCmdSN is not Session.CmdSN) { + Retransmit-Command-if-Possible(Connection, + CurrentPDU.ExpCmdSN); + } + } + } else if (CurrentPDU.type == Reject) { + if (it is a data digest error on immediate data) { + Retransmit-Command-if-Possible(Connection, + CurrentPDU.BadPDUHeader.CmdSN); + } + } else if (CurrentPDU.type == Response) { + send-status-SNACK = Evaluate-a-StatSN(Connection, + CurrentPDU); + if (send-status-SNACK == TRUE) + Recover-Status-if-Possible(Connection, CurrentPDU); + } else { /* REST UNRELATED TO WITHIN-CONNECTION-RECOVERY, + * NOT SHOWN */ + } +} + +Command-Acknowledge-Timeout-Handler(TCB) +{ + Retrieve the Connection for TCB. + Retransmit-Command-if-Possible(Connection, TCB.CmdSN); + + + +Satran, et al. Standards Track [Page 242] + +RFC 3720 iSCSI April 2004 + + +} + +Status-Expect-Timeout-Handler(Connection) +{ + if (operational ErrorRecoveryLevel > 0) { + Build-And-Send-Nop-Out(Connection); + } else if (InitiatorProactiveSNACKEnabled){ + if ((Connection.state == LOGGED_IN) and + connection is not already considered failed) { + Build-And-Send-SSnack(Connection); + } + } +} + +E.3.3. Target Algorithms + +Handle-Status-SNACK-request(Connection, CurrentPDU) +{ + if (operational ErrorRecoveryLevel > 0) { + if (request for an acknowledged run) { + Build-And-Send-Reject(Connection, CurrentPDU, + Protocol-Error); + } else if (request for an untransmitted run) { + discard, return; + } else { + Retransmit-Status-Burst(CurrentPDU, TCB); + } else { + Build-And-Send-Async(Connection, DroppedConnection, + DefaultTime2Wait, + DefaultTime2Retain); + } +} + +E.4. Connection Recovery Algorithms + +E.4.1. Procedure Descriptions + +Build-And-Send-Async(transport connection, reason code, + minimum time, maximum time); +Pick-A-Logged-In-Connection(session); +Build-And-Send-Logout(transport connection, logout connection + identifier, reason code); +PerformImplicitLogout(transport connection, logout connection + identifier, target information); +PerformLogin(transport connection, target information); +CreateNewTransportConnection(target information); +Build-And-Send-Command(transport connection, task control block); +Connection-Cleanup-Handler(transport connection); + + + +Satran, et al. Standards Track [Page 243] + +RFC 3720 iSCSI April 2004 + + +Connection-Resource-Timeout-Handler(transport connection); +Quiesce-And-Prepare-for-New-Allegiance(session, task control +block); +Build-And-Send-Logout-Response(transport connection, + CID of connection in recovery, reason +code); +Build-And-Send-TaskMgmt-Response(transport connection, + task mgmt command PDU, response code); +Establish-New-Allegiance(task control block, transport +connection); +Schedule-Command-To-Continue(task control block); + +Notes: + - Transport exception conditions, such as unexpected connection + termination, connection reset, and hung connection while the + connection is in the full-feature phase, are all assumed to be + asynchronously signaled to the iSCSI layer using the + Transport_Exception_Handler procedure. + +E.4.2. Initiator Algorithms + + Receive-a-In-PDU(Connection, CurrentPDU) { + check-basic-validity(CurrentPDU); + if (Header-Digest-Bad) discard, return; + + Retrieve TCB from CurrentPDU.InitiatorTaskTag. + if (CurrentPDU.type == Async) { + if (CurrentPDU.AsyncEvent == ConnectionDropped) { + Retrieve the AffectedConnection for + CurrentPDU.Parameter1. + AffectedConnection.CurrentTimeout = + CurrentPDU.Parameter3; + AffectedConnection.State = CLEANUP_WAIT; + Start-Timer(Connection-Cleanup-Handler, + AffectedConnection, + CurrentPDU.Parameter2); + } else if (CurrentPDU.AsyncEvent == LogoutRequest)) { + AffectedConnection = Connection; + AffectedConnection.State = LOGOUT_REQUESTED; + AffectedConnection.PerformConnectionCleanup = TRUE; + AffectedConnection.CurrentTimeout = + CurrentPDU.Parameter3; + Start-Timer(Connection-Cleanup-Handler, + AffectedConnection, 0); + } else if (CurrentPDU.AsyncEvent == SessionDropped)) { + for (each Connection) { + Connection.State = CLEANUP_WAIT; + Connection.CurrentTimeout = CurrentPDU.Parameter3; + + + +Satran, et al. Standards Track [Page 244] + +RFC 3720 iSCSI April 2004 + + + Start-Timer(Connection-Cleanup-Handler, + Connection, CurrentPDU.Parameter2); + } + Session.state = FAILED; + } + + } else if (CurrentPDU.type == LogoutResponse) { + Retrieve the CleanupConnection for CurrentPDU.CID. + if (CurrentPDU.Response = failure) { + CleanupConnection.State = CLEANUP_WAIT; + } else { + CleanupConnection.State = FREE; + } + } else if (CurrentPDU.type == LoginResponse) { + if (this is a response to an implicit Logout) { + Retrieve the CleanupConnection. + if (successful) { + CleanupConnection.State = FREE; + Connection.State = LOGGED_IN; + } else { + CleanupConnection.State = CLEANUP_WAIT; + DestroyTransportConnection(Connection); + } + } + } else { /* REST UNRELATED TO CONNECTION-RECOVERY, + + * NOT SHOWN */ + } + if (CleanupConnection.State == FREE) { + for (each command that was active on CleanupConnection) { + /* Establish new connection allegiance */ + NewConnection = Pick-A-Logged-In-Connection(Session); + Build-And-Send-Command(NewConnection, TCB); + } + } } + + Connection-Cleanup-Handler(Connection) { + Retrieve Session from Connection. + if (Connection can still exchange iSCSI PDUs) { + NewConnection = Connection; + } else { + Start-Timer(Connection-Resource-Timeout-Handler, + Connection, Connection.CurrentTimeout); + if (there are other logged-in connections) { + NewConnection = Pick-A-Logged-In- + Connection(Session); + } else { + NewConnection = + + + +Satran, et al. Standards Track [Page 245] + +RFC 3720 iSCSI April 2004 + + + CreateTransportConnection(Session.OtherEndInfo); + Initiate an implicit Logout on NewConnection for + Connection.CID. + return; + } + } + Build-And-Send-Logout(NewConnection, Connection.CID, + RecoveryRemove); } + + Transport_Exception_Handler(Connection) { + Connection.PerformConnectionCleanup = TRUE; + if (the event is an unexpected transport disconnect) { + Connection.State = CLEANUP_WAIT; + + Connection.CurrentTimeout = DefaultTime2Retain; + Start-Timer(Connection-Cleanup-Handler, Connection, + DefaultTime2Wait); + + } else { + Connection.State = FREE; + } } + +E.4.3. Target Algorithms + + Receive-a-In-PDU(Connection, CurrentPDU) + { + check-basic-validity(CurrentPDU); + if (Header-Digest-Bad) discard, return; + else if (Data-Digest-Bad) { + Build-And-Send-Reject(Connection, CurrentPDU, + Payload-Digest-Error); + discard, return; + } + Retrieve TCB and Session. + if (CurrentPDU.type == Logout) { + if (CurrentPDU.ReasonCode = RecoveryRemove) { + Retrieve the CleanupConnection from CurrentPDU.CID). + for (each command active on CleanupConnection) { + Quiesce-And-Prepare-for-New-Allegiance(Session, + TCB); + TCB.CurrentlyAllegiant = FALSE; + } + Cleanup-Connection-State(CleanupConnection); + if ((quiescing successful) and (cleanup successful)) { + Build-And-Send-Logout-Response(Connection, + CleanupConnection.CID, Success); + } else { + Build-And-Send-Logout-Response(Connection, + + + +Satran, et al. Standards Track [Page 246] + +RFC 3720 iSCSI April 2004 + + + CleanupConnection.CID, Failure); + } + } + } else if ((CurrentPDU.type == Login) and + operational ErrorRecoveryLevel == 2) { + Retrieve the CleanupConnection from CurrentPDU.CID). + for (each command active on CleanupConnection) { + Quiesce-And-Prepare-for-New-Allegiance(Session, TCB); + TCB.CurrentlyAllegiant = FALSE; + } + Cleanup-Connection-State(CleanupConnection); + if ((quiescing successful) and (cleanup successful)) { + Continue with the rest of the Login processing; + } else { + Build-And-Send-Login-Response(Connection, + CleanupConnection.CID, Target Error); + } + } + + } else if (CurrentPDU.type == TaskManagement) { + if (CurrentPDU.function == "TaskReassign") { + if (Session.ErrorRecoveryLevel < 2) { + Build-And-Send-TaskMgmt-Response(Connection, + CurrentPDU, "Allegiance reassignment + not supported"); + } else if (task is not found) { + Build-And-Send-TaskMgmt-Response(Connection, + CurrentPDU, "Task not in task set"); + } else if (task is currently allegiant) { + Build-And-Send-TaskMgmt-Response(Connection, + CurrentPDU, "Task still allegiant"); + } else { + Establish-New-Allegiance(TCB, Connection); + TCB.CurrentlyAllegiant = TRUE; + Schedule-Command-To-Continue(TCB); + } + } + } else { /* REST UNRELATED TO CONNECTION-RECOVERY, + * NOT SHOWN */ + } + } + + Transport_Exception_Handler(Connection) + { + Connection.PerformConnectionCleanup = TRUE; + if (the event is an unexpected transport disconnect) { + Connection.State = CLEANUP_WAIT; + Start-Timer(Connection-Resource-Timeout-Handler, + + + +Satran, et al. Standards Track [Page 247] + +RFC 3720 iSCSI April 2004 + + + Connection, + + (DefaultTime2Wait+DefaultTime2Retain)); + if (this Session has full-feature phase connections + left) + { + DifferentConnection = + Pick-A-Logged-In-Connection(Session); + Build-And-Send-Async(DifferentConnection, + DroppedConnection, DefaultTime2Wait, + DefaultTime2Retain); + } + } else { + Connection.State = FREE; + } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 248] + +RFC 3720 iSCSI April 2004 + + +Appendix F. Clearing Effects of Various Events on Targets + +F.1. Clearing Effects on iSCSI Objects + + The following tables describe the target behavior on receiving the + events specified in the rows of the table. The second table is an + extension of the first table and defines clearing actions for more + objects on the same events. The legend is: + + Y = Yes (cleared/discarded/reset on the event specified in the + row). Unless otherwise noted, the clearing action is only + applicable for the issuing initiator port. + N = No (not affected on the event specified in the row, i.e., + stays at previous value). + NA = Not Applicable or Not Defined. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 249] + +RFC 3720 iSCSI April 2004 + + + +-----+-----+-----+-----+-----+ + |IT(1)|IC(2)|CT(5)|ST(6)|PP(7)| + +---------------------+-----+-----+-----+-----+-----+ + |connection failure(8)|Y |Y |N |N |Y | + +---------------------+-----+-----+-----+-----+-----+ + |connection state |NA |NA |Y |N |NA | + |timeout (9) | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |session timeout/ |Y |Y |Y |Y |Y(14)| + |closure/reinstatement| | | | | | + |(10) | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |session continuation |NA |NA |N(11)|N |NA | + |(12) | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |successful connection|Y |Y |Y |N |Y(13)| + |close logout | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |session failure (18) |Y |Y |N |N |Y | + +---------------------+-----+-----+-----+-----+-----+ + |successful recovery |Y |Y |N |N |Y(13)| + |Logout | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |failed Logout |Y |Y |N |N |Y | + +---------------------+-----+-----+-----+-----+-----+ + |connection Login |NA |NA |NA |Y(15)|NA | + |(leading) | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |connection Login |NA |NA |N(11)|N |Y | + |(non-leading) | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |target cold reset(16)|Y |Y |Y |Y |Y | + +---------------------+-----+-----+-----+-----+-----+ + |target warm reset(16)|Y |Y |Y |Y |Y | + +---------------------+-----+-----+-----+-----+-----+ + |LU reset(19) |Y |Y |Y |Y |Y | + +---------------------+-----+-----+-----+-----+-----+ + |powercycle(16) |Y |Y |Y |Y |Y | + +---------------------+-----+-----+-----+-----+-----+ + + 1. Incomplete TTTs - Target Transfer Tags on which the target is + still expecting PDUs to be received. Examples include TTTs received + via R2T, NOP-IN, etc. + + 2. Immediate Commands - immediate commands, but waiting for + execution on a target. For example, Abort Task Set. + + + + + +Satran, et al. Standards Track [Page 250] + +RFC 3720 iSCSI April 2004 + + + 5. Connection Tasks - tasks that are active on the iSCSI connection + in question. + + 6. Session Tasks - tasks that are active on the entire iSCSI + session. A union of "connection tasks" on all participating + connections. + + 7. Partial PDUs (if any) - PDUs that are partially sent and waiting + for transport window credit to complete the transmission. + + 8. Connection failure is a connection exception condition - one of + the transport connections shutdown, transport connections reset, or + transport connections timed out, which abruptly terminated the iSCSI + full-feature phase connection. A connection failure always takes the + connection state machine to the CLEANUP_WAIT state. + + 9. Connection state timeout happens if a connection spends more time + that agreed upon during Login negotiation in the CLEANUP_WAIT state, + and this takes the connection to the FREE state (M1 transition in + connection cleanup state diagram). + + 10. These are defined in Section 5.3.5 Session Reinstatement, + Closure, and Timeout. + + 11. This clearing effect is "Y" only if it is a connection + reinstatement and the operational ErrorRecoveryLevel is less than 2. + + 12. Session continuation is defined in Section 5.3.6 Session + Continuation and Failure. + + 13. This clearing effect is only valid if the connection is being + logged out on a different connection and when the connection being + logged out on the target may have some partial PDUs pending to be + sent. In all other cases, the effect is "NA". + + 14. This clearing effect is only valid for a "close the session" + logout in a multi-connection session. In all other cases, the effect + is "NA". + + 15. Only applicable if this leading connection login is a session + reinstatement. If this is not the case, it is "NA". + + 16. This operation affects all logged-in initiators. + + 18. Session failure is defined in Section 5.3.6 Session Continuation + and Failure. + + + + + +Satran, et al. Standards Track [Page 251] + +RFC 3720 iSCSI April 2004 + + + 19. This operation affects all logged-in initiators and the clearing + effects are only applicable to the LU being reset. + + +-----+-----+-----+-----+-----+ + |DC(1)|DD(2)|SS(3)|CS(4)|DS(5)| + +---------------------+-----+-----+-----+-----+-----+ + |connection failure |N |Y |N |N |N | + +---------------------+-----+-----+-----+-----+-----+ + |connection state |Y |NA |Y |N |NA | + |timeout | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |session timeout/ |Y |Y |Y(7) |Y |NA | + |closure/reinstatement| | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |session continuation |N(11)|NA*12|NA |N |NA*13| + +---------------------+-----+-----+-----+-----+-----+ + |successful connection|Y |Y |Y |N |NA | + |close Logout | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |session failure |N |Y |N |N |N | + +---------------------+-----+-----+-----+-----+-----+ + |successful recovery |Y |Y |Y |N |N | + |Logout | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |failed Logout |N |Y(9) |N |N |N | + +---------------------+-----+-----+-----+-----+-----+ + |connection Login |NA |NA |N(8) |N(8) |NA | + |(leading | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |connection Login |N(11)|NA*12|N(8) |N |NA*13| + |(non-leading) | | | | | | + +---------------------+-----+-----+-----+-----+-----+ + |target cold reset |Y |Y |Y |Y(10)|NA | + +---------------------+-----+-----+-----+-----+-----+ + |target warm reset |Y |Y |N |N |NA | + +---------------------+-----+-----+-----+-----+-----+ + |LU reset |N |Y |N |N |N | + +---------------------+-----+-----+-----+-----+-----+ + |powercycle |Y |Y |Y |Y(10)|NA | + +---------------------+-----+-----+-----+-----+-----+ + + 1. Discontiguous Commands - commands allegiant to the connection in + question and waiting to be reordered in the iSCSI layer. All "Y"s in + this column assume that the task causing the event (if indeed the + event is the result of a task) is issued as an immediate command, + because the discontiguities can be ahead of the task. + + + + + +Satran, et al. Standards Track [Page 252] + +RFC 3720 iSCSI April 2004 + + + 2. Discontiguous Data - data PDUs received for the task in question + and waiting to be reordered due to prior discontiguities in DataSN. + + 3. StatSN + + 4. CmdSN + + 5. DataSN + + 7. It clears the StatSN on all the connections. + + 8. This sequence number is instantiated on this event. + + 9. A logout failure drives the connection state machine to the + CLEANUP_WAIT state, similar to the connection failure event. Hence, + it has a similar effect on this and several other protocol aspects. + + 10. This is cleared by virtue of the fact that all sessions with all + initiators are terminated. + + 11. This clearing effect is "Y" if it is a connection reinstatement. + + 12. This clearing effect is "Y" only if it is a connection + reinstatement and the operational ErrorRecoveryLevel is 2. + + 13. This clearing effect is "N" only if it is a connection + reinstatement and the operational ErrorRecoveryLevel is 2. + +F.2. Clearing Effects on SCSI Objects + + The only iSCSI protocol action that can effect clearing actions on + SCSI objects is the "I_T nexus loss" notification (Section 4.3.5.1 + Loss of Nexus notification). [SPC3] describes the clearing effects + of this notification on a variety of SCSI attributes. In addition, + SCSI standards documents (such as [SAM2] and [SBC]) define additional + clearing actions that may take place for several SCSI objects on SCSI + events such as LU resets and power-on resets. + + Since iSCSI defines a target cold reset as a protocol-equivalent to a + target power-cycle, the iSCSI target cold reset must also be + considered as the power-on reset event in interpreting the actions + defined in the SCSI standards. + + When the iSCSI session is reconstructed (between the same SCSI ports + with the same nexus identifier) reestablishing the same I_T nexus, + all SCSI objects that are defined to not clear on the "I_T nexus + loss" notification event, such as persistent reservations, are + automatically associated to this new session. + + + +Satran, et al. Standards Track [Page 253] + +RFC 3720 iSCSI April 2004 + + +Acknowledgements + + This protocol was developed by a design team that, in addition to the + authors, included Daniel Smith, Ofer Biran, Jim Hafner and John + Hufferd (IBM), Mark Bakke (Cisco), Randy Haagens (HP), Matt Wakeley + (Agilent, now Sierra Logic), Luciano Dalle Ore (Quantum), and Paul + Von Stamwitz (Adaptec, now TrueSAN Networks). + + Furthermore, a large group of people contributed to this work through + their review, comments, and valuable insights. We are grateful to + all of them. We especially thank those people who found the time and + patience to take part in our weekly phone conferences and + intermediate meetings in Almaden and Haifa, which helped shape this + document: Prasenjit Sarkar, Meir Toledano, John Dowdy, Steve Legg, + Alain Azagury (IBM), Dave Nagle (CMU), David Black (EMC), John Matze + (Veritas - now Okapi Software), Steve DeGroote, Mark Schrandt + (Cisco), Gabi Hecht (Gadzoox), Robert Snively and Brian Forbes + (Brocade), Nelson Nachum (StorAge), and Uri Elzur (Broadcom). Many + others helped edit and improve this document within the IPS working + group. We are especially grateful to David Robinson and Raghavendra + Rao (Sun), Charles Monia, Joshua Tseng (Nishan), Somesh Gupta + (Silverback), Michael Krause, Pierre Labat, Santosh Rao, Matthew + Burbridge, Bob Barry, Robert Elliott, Nick Martin (HP), Stephen + Bailey (Sandburst), Steve Senum, Ayman Ghanem, Dave Peterson (Cisco), + Barry Reinhold (Trebia Networks), Bob Russell (UNH), Eddy Quicksall + (iVivity, Inc.), Bill Lynn and Michael Fischer (Adaptec), Vince + Cavanna, Pat Thaler (Agilent), Jonathan Stone (Stanford), Luben + Tuikov (Splentec), Paul Koning (EqualLogic), Michael Krueger + (Windriver), Martins Krikis (Intel), Doug Otis (Sanlight), John + Marberg (IBM), Robert Griswold and Bill Moody (Crossroads), Bill + Studenmund (Wasabi Systems), Elizabeth Rodriguez (Brocade) and Yaron + Klein (Sanrad). The recovery chapter was enhanced with the help of + Stephen Bailey (Sandburst), Somesh Gupta (Silverback), and Venkat + Rangan (Rhapsody Networks). Eddy Quicksall contributed some examples + and began the Definitions section. Michael Fischer and Bob Barry + started the Acronyms section. Last, but not least, we thank Ralph + Weber for keeping us in line with T10 (SCSI) standardization. + + We would like to thank Steve Hetzler for his unwavering support and + for coming up with such a good name for the protocol, and Micky + Rodeh, Jai Menon, Clod Barrera, and Andy Bechtolsheim for helping + make this work happen. + + In addition to this document, we recommend you acquaint yourself with + the following in order to get a full understanding of the iSCSI + specification: "iSCSI Naming & Discovery"[RFC3721], "Bootstrapping + Clients using the iSCSI Protocol" [BOOT], "Securing Block Storage + Protocols over IP" [RFC3723] documents, "iSCSI Requirements and + + + +Satran, et al. Standards Track [Page 254] + +RFC 3720 iSCSI April 2004 + + + Design Considerations" [RFC3347] and "SCSI Command Ordering + Considerations with iSCSI" [CORD]. + + The "iSCSI Naming & Discovery" document is authored by: + + Mark Bakke (Cisco), Jim Hafner, John Hufferd, Kaladhar Voruganti + (IBM), and Marjorie Krueger (HP). + + The "Bootstrapping Clients using the iSCSI Protocol" document is + authored by: + + Prasenjit Sarkar (IBM), Duncan Missimer (HP), and Costa + Sapuntzakis (Cisco). + + The "Securing Block Storage Protocols over IP" document is authored + by: + + Bernard Aboba (Microsoft), Joshua Tseng (Nishan), Jesse Walker + (Intel), Venkat Rangan (Rhapsody Networks), and Franco + Travostino (Nortel Networks). + + The "iSCSI Requirements and Design Considerations" document is + authored by: + + Marjorie Krueger, Randy Haagens (HP), Costa Sapuntzakis, and Mark + Bakke (Cisco). + + The "SCSI Command Ordering Considerations with iSCSI" document is + authored by: + + Mallikarjun Chadalapaka, Rob Elliot (HP) + + We are grateful to all of them for their good work and for helping us + correlate this document with the ones they produced. + + + + + + + + + + + + + + + + + +Satran, et al. Standards Track [Page 255] + +RFC 3720 iSCSI April 2004 + + +Authors' Addresses + + Julian Satran + IBM Research Laboratory in Haifa + Haifa University Campus - Mount Carmel + Haifa 31905, Israel + + Phone +972.4.829.6264 + EMail: Julian_Satran@il.ibm.com + + + Kalman Meth + IBM Research Laboratory in Haifa + Haifa University Campus - Mount Carmel + Haifa 31905, Israel + + Phone +972.4.829.6341 + EMail: meth@il.ibm.com + + + Costa Sapuntzakis + Stanford University + 353 Serra Mall Dr #407 + Stanford, CA 94305 + + Phone: +1.650.723.2458 + EMail: csapuntz@alum.mit.edu + + + Efri Zeidner + XIV Ltd. + 1 Azrieli Center, + Tel-Aviv 67021, Israel + + Phone: +972.3.607.4722 + EMail: efri@xiv.co.il + + + Mallikarjun Chadalapaka + Hewlett-Packard Company + 8000 Foothills Blvd. + Roseville, CA 95747-5668, USA + + Phone: +1.916.785.5621 + EMail: cbm@rose.hp.com + + + + + + +Satran, et al. Standards Track [Page 256] + +RFC 3720 iSCSI April 2004 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2004). This document is subject + to the rights, licenses and restrictions contained in BCP 78, and + except as set forth therein, the authors retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET + ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE + INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at ietf- + ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + +Satran, et al. Standards Track [Page 257] + diff --git a/utils/open-isns/doc/rfc3722.txt b/utils/open-isns/doc/rfc3722.txt new file mode 100644 index 0000000..0eb44ff --- /dev/null +++ b/utils/open-isns/doc/rfc3722.txt @@ -0,0 +1,451 @@ + + + + + + +Network Working Group M. Bakke +Request for Comments: 3722 Cisco +Category: Standards Track April 2004 + + + String Profile for Internet Small Computer + Systems Interface (iSCSI) Names + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2004). All Rights Reserved. + +Abstract + + This document describes how to prepare internationalized iSCSI names + to increase the likelihood that name input and comparison work in + ways that make sense for typical users throughout the world. + + The Internet Small Computer Systems Interface (iSCSI) protocol + provides a way for hosts to access SCSI devices over an IP network. + The iSCSI end-points, called initiators and targets, each have a + globally-unique name that must be transcribable, as well as easily + compared. + +1. Introduction + + The iSCSI protocol [RFC3720] provides a way for hosts to access SCSI + [SAM2] devices over an IP network. The iSCSI end-points, called + initiators and targets, each have a globally-unique name, defined in + [RFC3721]. + + An iSCSI name is a string of UTF-8 [RFC3629] characters that includes + a type designator, a naming authority based on domain names, and a + unique part within the naming authority. The unique part may be + generated based on anything the naming authority deems useful, and + may include user input. + + These names may need to be transcribed (sent between two + administrators via email, voice, paper, etc), so a case-insensitive + comparison would be desirable. However, these names must often be + + + +Bakke Standards Track [Page 1] + +RFC 3722 String Profile for iSCSI Names April 2004 + + + compared by initiator and target implementations, most of which are + done in simple, embedded software. This makes case-sensitive + comparison highly desirable for these implementors. + + However, a completely case-sensitive implementation would result in + identifiers such as "example-name" and "Example-Name" being + different, which could lead to confusion as these names are + transcribed. + + The goal, then, is to generate iSCSI names that can be transcribed + and entered by users, and also compared byte-for-byte, with minimal + confusion. To attain these goals, iSCSI names are generalized using + a normalized character set (converted to lower case or equivalent), + with no white space allowed, and very limited punctuation. + + For those using only ASCII characters (U+0000 to U+007F), the + following characters are allowed: + + - ASCII dash character ('-' = U+002d) + - ASCII dot character ('.' = U+002e) + - ASCII colon character (':' = U+003a) + - ASCII lower-case characters ('a'..'z' = U+0061..U+007a) + - ASCII digit characters ('0'..'9' = U+0030..U+0039) + + In addition, any upper-case characters input via a user interface + MUST be mapped to their lower-case equivalents. + + This document specifies the valid character set for iSCSI names, + along with the rules for normalizing and generating iSCSI names based + on user input or other information that contains international + characters. + + In particular, it defines the following, as required by [RFC3454]: + + - The intended applicability of the profile: internationalized iSCSI + names. + + - The character repertoire that is the input and output to + stringprep: Unicode 3.2, specified in section 3. + + - The mappings used: specified in section 4. + + - The Unicode normalization used: specified in section 5. + + - The characters that are prohibited as output: specified in section + 6. + + This profile MUST be used with the iSCSI protocol. + + + +Bakke Standards Track [Page 2] + +RFC 3722 String Profile for iSCSI Names April 2004 + + +2. Terminology + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + Examples in this document use the notation for code points and names + from the Unicode Standard [Unicode3.2] and ISO/IEC 10646 [ISO10646]. + For example, the letter "a" may be represented as either "U+0061" or + "LATIN SMALL LETTER A". In the lists of prohibited characters, the + "U+" is left off to make the lists easier to read. The comments for + character ranges are shown in square brackets (such as "[SYMBOLS]") + and do not come from the standards. + +3. Character Repertoire + + This profile uses Unicode 3.2, as defined in [RFC3454] Appendix A. + +4. Mapping + + This profile specifies mapping using the following tables from + [RFC3454]. The following mapping tables MUST be used when generating + iSCSI names from Unicode characters. + + Table B.1 + Table B.2 + +5. Normalization + + Unicode normalization form KC MUST be used with this profile, as + described in [RFC3454]. + +6. Prohibited Output + + This profile specifies prohibiting using the following tables from + [RFC3454]. Characters appearing within these tables MUST NOT be used + within an iSCSI name. + + Table C.1.1 + Table C.1.2 + Table C.2.1 + Table C.2.2 + Table C.3 + Table C.4 + Table C.5 + Table C.6 + + + + + +Bakke Standards Track [Page 3] + +RFC 3722 String Profile for iSCSI Names April 2004 + + + Table C.7 + Table C.8 + Table C.9 + + Important note: this profile MUST be used with the iSCSI protocol. + The iSCSI protocol has additional naming rules that are checked + outside of this profile. + + In addition, this profile adds the following prohibitions. The full + set of prohibited characters are those from the tables above plus + those listed individually below. + +6.1. Inappropriate Characters from Common Input Mechanisms + + u+3002 is used as if it were u+002e in many domain name input + mechanisms used by applications, particularly in Asia. The character + u+3002 MUST NOT be used in an iSCSI name. + + 3002; ideographic full stop + +6.2. Currently-prohibited ASCII characters + + Some of the ASCII characters that are currently prohibited in iSCSI + names by [RFC3721] are also used in protocol elements such as URIs. + Some examples are described in [RFC2396] and [RFC2732]. Note that + there are many other RFCs that define additional URI schemes. + + The other characters in the range U+0000 to U+007F that are not + currently allowed are prohibited in iSCSI names to reserve them for + future use in protocol elements. Note that the dash (U+002D), dot + (U+002E), and colon (U+003A) are not prohibited. + + The following characters MUST NOT be used in iSCSI names: + + 0000-002C; [ASCII CONTROL CHARACTERS and SPACE through ,] + 002F; [ASCII /] + 003B-0040; [ASCII ; through @] + 005B-0060; [ASCII [ through `] + 007B-007F; [ASCII { through DEL] + +7. Bidirectional Characters + + This profile specifies checking bidirectional strings as described in + [RFC3454] section 6. + + + + + + + +Bakke Standards Track [Page 4] + +RFC 3722 String Profile for iSCSI Names April 2004 + + +8. Unassigned Code Points in Internationalized Domain Names + + If the processing in [RFC3720] specifies that a list of unassigned + code points be used, the system uses table A.1 from [RFC3454] as its + list of unassigned code points. + +9. Security Considerations + + ISO/IEC 10646 has many characters that look similar. In many cases, + users of security protocols might do visual matching, such as when + comparing the names of trusted third parties. This profile does + nothing to map similar-looking characters together. + + iSCSI names may be used by an initiator to verify that a target it + has discovered is the correct one, and by a target to verify that an + initiator is to be allowed access. If these names are interpreted + and compared differently by different iSCSI implementations, an + initiator could gain access to the wrong target, or could be denied + access to a legitimate target. + +10. IANA Considerations + + This is a profile of stringprep. It has been registered in the IANA + "Stringprep Profiles" registry. This process is described in the + IANA Considerations section of [RFC3454]. + +11. Summary + + This document describes a stringprep profile to be used with programs + generating names for iSCSI initiators and targets. + +12. Acknowledgements + + This document was produced as a result of discussions on iSCSI name + formats with Joe Czap, Jim Hafner, Howard Hall, Jack Harwood, John + Hufferd, Marjorie Krueger, Lawrence Lamers, Todd Sperry, Joshua + Tseng, and Kaladhar Voruganti, as well as discussions on the + normalization of names into identifiers with Paul Hoffman and Marc + Blanchet. + + Thanks also to Bob Snively for suggesting the use of the nameprep + process for iSCSI name normalization. + + Most of this document was copied from the stringprep profile for + Internationalized Domain Names [RFC3491], written by Paul Hoffman and + Marc Blanchet. + + + + + +Bakke Standards Track [Page 5] + +RFC 3722 String Profile for iSCSI Names April 2004 + + +13. References + +13.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of + Internationalized Strings ("stringprep")", RFC 3454, + December 2002. + + [RFC3720] Satran, J., Meth, K., Sapuntzakis, C. Chadalapaka, M. + and E. Zeidner, "Internet Small Computer Systems + Interface (iSCSI)", RFC 3720, April 2004. + +13.2. Informative References + + [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform + Resource Identifiers", RFC 2396, August 1998. + + [RFC2732] Hinden, R., Carpenter, B. and L. Masinter, "Format for + Literal IPv6 Addresses in URL's", RFC 2732, December + 1999. + + [RFC3491] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep + Profile for Internationalized Domain Names", RFC 3491, + March 2003. + [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", STD 63, RFC 3629, November 2003. + + [RFC3721] Bakke, M., Hafner, J., Hufferd, J., Voruganti, K. and M. + Krueger, "Internet Small Computer Systems Interface + (iSCSI) Naming and Discovery", RFC 3721, April 2004. + + [SAM2] ANSI T10. "SCSI Architectural Model 2", March 2000. + + [Unicode3.2] The Unicode Standard, Version 3.2.0: The Unicode + Consortium. The Unicode Standard, Version 3.2.0 is + defined by The Unicode Standard, Version 3.0 (Reading, + MA, Addison-Wesley, 2000. ISBN 0-201-61633-5), as + amended by the Unicode Standard Annex #27: Unicode 3.1 + (http://www.unicode.org/unicode/reports/tr27/) and by + the Unicode Standard Annex #28: Unicode 3.2 + (http://www.unicode.org/unicode/reports/tr28/). + + + + + + + +Bakke Standards Track [Page 6] + +RFC 3722 String Profile for iSCSI Names April 2004 + + + [ISO10646] ISO/IEC 10646-1:2000. International Standard -- + Information technology -- Universal Multiple-Octet Coded + Character Set (UCS) -- Part 1: Architecture and Basic + Multilingual Plane. + +14. Author's Address + + Mark Bakke + Cisco Systems, Inc. + 6450 Wedgwood Road + Maple Grove, MN + USA 55311 + + Voice: +1 763-398-1000 + EMail: mbakke@cisco.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Bakke Standards Track [Page 7] + +RFC 3722 String Profile for iSCSI Names April 2004 + + +15. Full Copyright Statement + + Copyright (C) The Internet Society (2004). This document is subject + to the rights, licenses and restrictions contained in BCP 78, and + except as set forth therein, the authors retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET + ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE + INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at ietf- + ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + +Bakke Standards Track [Page 8] + diff --git a/utils/open-isns/doc/rfc4018.txt b/utils/open-isns/doc/rfc4018.txt new file mode 100644 index 0000000..15df58d --- /dev/null +++ b/utils/open-isns/doc/rfc4018.txt @@ -0,0 +1,1291 @@ + + + + + + +Network Working Group M. Bakke +Request for Comments: 4018 Cisco +Category: Standards Track J. Hufferd + K. Voruganti + IBM + M. Krueger + HP + T. Sperry + Adaptec + April 2005 + + + Finding Internet Small Computer Systems Interface (iSCSI) Targets + and Name Servers by Using Service Location Protocol version 2 (SLPv2) + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2005). + +Abstract + + The iSCSI protocol provides a way for hosts to access SCSI devices + over an IP network. This document defines the use of the Service + Location Protocol (SLP) by iSCSI hosts, devices, and management + services, along with the SLP service type templates that describe the + services they provide. + +Table of Contents + + 1. Introduction................................................ 2 + 2. Notation Conventions........................................ 2 + 3. Terminology................................................. 3 + 4. Using SLP for iSCSI Service Discovery....................... 4 + 5. iSCSI SLP Templates......................................... 11 + 6. Security Considerations..................................... 18 + 7. IANA Considerations......................................... 19 + 8. Summary..................................................... 19 + 9. Normative References........................................ 19 + 10. Informative References...................................... 20 + 11. Acknowledgements............................................ 21 + + + +Bakke & Hufferd Standards Track [Page 1] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +1. Introduction + + iSCSI [RFC3720] is a protocol used to transport SCSI [SAM2] commands, + data, and status across an IP network. This protocol is connection- + oriented and is currently defined over TCP. iSCSI uses a client- + server relationship. The client end of the connection is an + initiator, and it sends SCSI commands; the server end of the + connection is called a target, and it receives and executes the + commands. + + There are several methods an iSCSI initiator can use to find the + targets to which it should connect. Two of these methods can be + accomplished without the use of SLP: + + - Each target and its address can be statically configured on the + initiator. + + - Each address providing targets can be configured on the initiator; + iSCSI provides a mechanism by which the initiator can query the + address for a list of targets. + + The above methods are further defined in "iSCSI Naming and Discovery + Requirements" [RFC3721]. + + Each of the above methods requires a small amount of configuration to + be done on each initiator. The ability to discover targets and name + services without having to configure initiators is a desirable + feature. The Service Location Protocol (SLP) [RFC2608] is an IETF + standards track protocol providing several features that will + simplify locating iSCSI services. This document describes how SLP + can be used in iSCSI environments to discover targets, addresses + providing targets, and storage management servers. + +2. Notation Conventions + + In this document, the key words "MUST", "MUST NOT", "REQUIRED", + "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", + and "OPTIONAL" are to be interpreted as described in [RFC2119]. + + + + + + + + + + + + + +Bakke & Hufferd Standards Track [Page 2] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +3. Terminology + + Here are some definitions that may aid readers who are unfamiliar + with SLP, SCSI, or iSCSI. Some of these definitions have been + reproduced from [RFC2608] and "Finding an RSIP Server with SLP" + [RFC3105]. + + User Agent (UA) A process working on the client's behalf + to establish contact with some service. + The UA retrieves service information from + the Service Agents or Directory Agents. + + Service Agent (SA) A process working on behalf of one or more + services to advertise the services and + their capabilities. + + Directory Agent (DA) A process that collects service + advertisements. There can only be one DA + present per given host. + + Scope A named set of services, typically making + up a logical administrative group. + + Service Advertisement A URL, attributes, and a lifetime + (indicating how long the advertisement is + valid) providing service access + information and capabilities description + for a particular service. + + Initiator A logical entity, typically within a host, + that sends SCSI commands to targets to be + executed. An initiator is usually present + in the form of a device driver. + + Target A logical entity, typically within a + storage controller or gateway that + receives SCSI commands from an initiator + and executes them. A target includes one + or more Logical Units (LUs); each LU is a + SCSI device, such as a disk or tape drive. + + iSCSI Name A UTF-8 character string that serves as a + unique identifier for iSCSI initiators and + targets. Its format and usage is further + defined in [RFC3721]. + + iSCSI Client A logical entity, typically a host that + includes at least one iSCSI Initiator. + + + +Bakke & Hufferd Standards Track [Page 3] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + iSCSI Server A logical entity, typically a storage + controller or gateway that includes at + least one iSCSI Target. + + Storage Management Server An addressable entity that provides + management services that benefit an iSCSI + environment. "Storage management server" + is used as a generic term and does not + indicate a specific protocol or service. + +4. Using SLP for iSCSI Service Discovery + + Two entities are involved in iSCSI discovery. The end result is that + an iSCSI initiator (e.g., a host) discovers iSCSI targets, usually + provided by storage controllers or gateways. + + iSCSI targets are registered with SLP as a set of service URLs, one + for each address on which the target may be accessed. Initiators + discover these targets by using SLP service requests. Targets that + do not directly support SLP or that are under the control of a + management service may be registered by a proxy service agent as part + of the software providing this service. + + iSCSI entities may also use SLP to discover higher-level management + services when these are needed. + + This section first describes the use of SLP for discovery of targets + by iSCSI initiators, it then describes the use of SLP to discover + storage management servers. + + This document assumes that SLPv2 will be used for discovering iSCSI- + related services; no attempt is made to include support for SLPv1. + +4.1. Discovering iSCSI Targets with SLP + + The following diagram shows the relationship among iSCSI clients, + servers, initiators, and targets. An iSCSI client includes at least + one iSCSI initiator, and an SLP user agent (UA). An iSCSI server + includes at least one iSCSI target an SLP service agent (SA). Some + entities, such as extended copy engines, include both initiators and + targets. These include both an SA, for its targets to be discovered, + and a UA, for its initiator(s) to discover other targets. + + + + + + + + + +Bakke & Hufferd Standards Track [Page 4] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + +---------------------------------+ + | iSCSI Client | + | +-----------+ | + | | iSCSI | | + | | initiator | | + | | "myhost" | | + | +-----------+ | + | | + +--------------------------+------+ + | iSCSI Driver | UA | + +--------------------------+------+ + | TCP/UDP/IP | + +----------------+----------------+ + | Interface 1 | Interface 2 | + +----------------+----------------+ + | | + +------------+ | | +------------+ + | SLP DA | | | | SLP DA | + | (optional) |----+ IP Networks +----| (optional) | + +------------+ | | +------------+ + | | + +-----------------+-----------------| + | Interface 1 | Interface 2 | + | 192.0.2.131 | 192.0.2.3 | + +-----------------+-----------------+ + | TCP/UDP/IP | + +---------------------------+-------+ + | iSCSI Driver | SA | + +---------------------------+-------| + | | + | +--------+ +--------+ +---------+ | + | | iSCSI | | iSCSI | | iSCSI | | + | | target | | target | | target | | + | | "one" | | "two" | | "three" | | + | +--------+ +--------+ +---------+ | + | iSCSI Server | + +-----------------------------------+ + + In the above drawing, the iSCSI server has three iSCSI targets that + the client could discover, named "one", "two" and "three". The iSCSI + client has an iSCSI initiator with the name "myhost". The iSCSI + client may use the initiator name in its SLP Service Requests as a + filter to discover only targets that are configured to accept iSCSI + connections from "myhost". + + Each iSCSI target and initiator has a unique name, called an iSCSI + Name. This identifier is the same regardless of the network path + (through adapter cards, networks, and interfaces on the storage + + + +Bakke & Hufferd Standards Track [Page 5] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + device) over which the target is discovered and accessed. For this + example, the iSCSI names "one", "two", and "three" are used for the + targets; the initiator uses the name "myhost". An actual iSCSI name + would incorporate more structure, including a naming authority, and + is not described here. + + Each of the iSCSI targets in the drawing can appear at two addresses, + since two network interfaces are present. Each target would have two + service URLs, unless a single service URL included a DNS host name + mapping to both addresses. + + An iSCSI target URL consists of its fully qualified host name or IP + address, the TCP port on which it is listening, and its iSCSI name. + An iSCSI server must register each of its individual targets at each + of its network addresses. + + The iSCSI server constructs a service advertisement of the type + "service:iscsi:target" for each of the service URLs it wishes to + register. The advertisement contains a lifetime, along with other + attributes that are defined in the service template. + + If the server in the above drawing is listening at TCP port 3260 for + both network addresses, the service URLs registered would be + + - 192.0.2.131:3260/one + + - 192.0.2.131:3260/two + + - 192.0.2.131:3260/three + + - 192.0.2.3:3260/one + + - 192.0.2.3:3260/two + + - 192.0.2.3:3260/three + + The remainder of the discovery procedure is identical to that used by + any client/server pair implementing SLP: + + 1. If an SLP DA is found, the SA contacts the DA and registers the + service advertisement. Whether or not one or more SLPv2 DAs are + discovered, the SA maintains the advertisement itself and answers + multicast UA queries directly. + + 2. When the iSCSI initiator requires contact information for an + iSCSI target, the UA either contacts the DA by using unicast or + the SA by using multicast. If a UA is configured with the + address of the SA, it may avoid multicast and may contact an SA + + + +Bakke & Hufferd Standards Track [Page 6] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + by using unicast. The UA includes a query based on the + attributes to indicate the characteristics of the target(s) it + requires. + + 3. Once the UA has the host name or address of the iSCSI server, as + well as the port number and iSCSI Target Name, it can begin the + normal iSCSI login to the target. + + As information contained in the iSCSI target template may exceed + common network datagram sizes, the SLP implementation for both UAs + and SAs supporting this template MUST implement SLP over TCP. + +4.1.1. Finding Targets Based on Initiator Credentials + + To be allowed access to an iSCSI target, an initiator must be + authenticated. The initiator may be required by the target to + produce one or more of the following credentials: + + - An iSCSI Initiator Name + + - An IP address + + - A CHAP, SRP, or Kerberos credential + + - Any combination of the above + + Most iSCSI targets allow access to only one or two initiators. In + the ideal discovery scenario, an initiator would send an SLP request + and receive responses ONLY for targets to which the initiator is + guaranteed a successful login. To achieve this goal, the iSCSI + target template contains the following attributes, each of which + allows a list of values: + + 1. auth-name: This attribute contains the list of initiator names + allowed to access this target, or the value "any", indicating + that no specific initiator name is required. + + 2. auth-addr: This attribute contains the list of host names + and/or IP addresses that will be allowed access to this target, + or the value "any", indicating that no specific address or + host name is required. If a large number of addresses is to + be allowed (perhaps a subnet), this attribute may contain the + value "any". + + + + + + + + +Bakke & Hufferd Standards Track [Page 7] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + 3. auth-cred: This attribute contains a list of "method/identifier" + credentials that will be allowed access to the target, provided + they can produce the correct password or other verifier during + the login process. If no specific credentials are required, the + value "any" is used. + + The list of valid method strings for auth-cred are defined in + [RFC3720], section 11.1, "AuthMethod". The identifier used after the + "/" is defined by the specific AuthMethod, also in [RFC3720]. + Examples showing initiator searches based on auth-xxxx attributes are + shown in the target-specific template section below. + + Also note that the auth-xxxx attributes are considered security + policy information. If these attributes are distributed, IPsec MUST + be implemented as specified in the Security Implementation section + below. + +4.1.2. Supporting Access by Multiple Identities to the Same Target + + If a target is to allow access to multiple host identities, more than + one combination of auth-xxxx attributes will have to be allowed. In + some of these cases, it is not possible to express the entire set of + valid combinations of auth-xxxx attributes within a single registered + service URL. For example, if a target can be addressed by + + auth-name=myhost1 AND auth-cred=CHAP/user1 (identity1) + + OR + + auth-name-myhost2 AND auth-cred=CHAP/user2 (identity2) + + the above cannot be specified in a single registered service URL, + since (auth-name=myhost1, auth-name=myhost2, auth-cred=CHAP/user1, + auth-cred=CHAP/user2) would allow either auth-name to be used with + either auth-cred. This necessitates the ability to register a target + and address under more than one service URL; one for (identity1) and + one for (identity2). + + Because service URLs must be unique, (identity1) and (identity2) must + each be registered under a unique service URL. For systems that + support the configuration of multiple identities to access a target, + the service URL must contain an additional, opaque string defining + the identity. This appears after the iSCSI name in the URL string + and is separated by a "/". Each registered (target-address, target- + name, initiator-identity) tuple can then register a set of auth-xxxx + attributes. + + + + + +Bakke & Hufferd Standards Track [Page 8] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +4.1.3. Using SLP in a Non-multicast Environment + + In some networks, the use of multicast for discovery purposes is + either unavailable or not allowed. These include public or service- + provider networks that are placed between an iSCSI client and a + server. These are probably most common between two iSCSI gateways, + one at a storage service provider site, and one at a customer site. + + In these networks, an initiator may allow the addresses of one or + more SAs to be configured instead of or in addition to its DA + configuration. The initiator would then make unicast SLP service + requests directly to these SAs, without the use of multicast to + discover them first. + + This functionality is well within the scope of the current SLP + protocol. The main consequence for implementors is that an initiator + configured to make direct unicast requests to an SA will have to add + this to the SLP API, if it is following the service location API + defined in [RFC2614]. + +4.2. Discovering Storage Management Services with SLP + + Storage management servers can be built to manage and control access + to targets in a variety of ways. They can provide extended services + beyond discovery, which could include storage allocation and + management. None of these services are defined here; the intent of + this document is to allow these services to be discovered by both + clients and servers, in addition to the target discovery already + being performed. + + The following drawing shows an iSCSI client, an iSCSI server, and a + storage management server. To simplify the drawing, the second IP + network is not shown but is assumed to exist. The storage management + server would use its own protocol (smsp) to provide capabilities to + iSCSI clients and servers; these clients and servers can both use SLP + to discover the storage management server. + + + + + + + + + + + + + + + +Bakke & Hufferd Standards Track [Page 9] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + +---------------------------+ + | iSCSI Client | + | | + | +-----------+ | + | | iSCSI | | + | | initiator | | + | +-----------+ | + | | + +---------------+------+----+ +------------+ + | iSCSI Driver | smsp | UA | | SLP DA | + +---------------+------+----+ | | + | TCP/UDP/IP | | (optional) | + +---------------+------+----+ +------------+ + | | + | IP Network | + ------------------------------------------ + | | + | | + +---------------+-----------+ +---------------------+ + | TCP/UDP/IP | | TCP/UDP/IP | + +---------------+------+----+ +---------------------+ + | iSCSI Driver | smsp | UA | | SA | smsp | + +---------------+------+----+ +---------------------+ + | | | | + | +--------+ +--------+ | | storage mgmt server | + | | iSCSI | | iSCSI | | | | + | | target | | target | | +---------------------+ + | | 1 | | 2 | | + | +--------+ +--------+ | + | | + | iSCSI Server | + +---------------------------+ + + Note the difference between the storage management server model and + the previously defined target discovery model. When target discovery + was used, the iSCSI Server implemented an SA, to be discovered by the + initiator's UA. In the storage management server model, the iSCSI + clients and servers both implement UAs, and the management server + implements the SA. + + A storage management server's URL contains the domain name or IP + address and TCP or UDP port number. No other information is + required. + + The storage management server constructs a service advertisement of + the type "service:iscsi:sms" for each of the addresses at which it + appears. The advertisement contains the URL and a lifetime, along + with other attributes that are defined in the service template. + + + +Bakke & Hufferd Standards Track [Page 10] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + The remainder of the discovery procedure is identical to that used to + discover iSCSI targets, except that both initiators and targets would + normally be "clients" of the storage management service. + + Targets that support a storage management service implement a UA in + addition to the SA. A target may alternatively just implement the UA + and allow the storage management service to advertise its targets + appropriately by providing an SA and registering the appropriate + service:iscsi:target registrations on the target's behalf: The target + device would not have to advertise its own targets. This has no + impact on the initiator. + + This allows the initiators' discovery of targets to be completely + interoperable regardless of which storage management service is used, + or whether one is used at all, or whether the target registrations + are provided directly by the target or by the management service. + +4.3. Internationalization Considerations + + SLP allows internationalized strings to be registered and retrieved. + Attributes in the template that are not marked with an 'L' (literal) + will be registered in a localized manner. An "en" (English) + localization MUST be registered, and others MAY be registered. + + Attributes that include non-ASCII characters will be encoded by using + UTF-8, as discussed in [RFC3722] and [RFC3491]. + +5. iSCSI SLP Templates + + Three templates are provided: an iSCSI target template, a management + service template, and an abstract template to encapsulate the two. + +5.1. The iSCSI Abstract Service Type Template + + This template defines the abstract service "service:iscsi". It is + used as a top-level service to encapsulate all other iSCSI-related + services. + + Name of submitter: Mark Bakke + Language of service template: en + Security Considerations: See section 6. + + Template Text: + -------------------------template begins here----------------------- + template-type=iscsi + template-version=1.0 + + template-description= + + + +Bakke & Hufferd Standards Track [Page 11] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + This is an abstract service type. The purpose of the iscsi + service type is to encompass all of the services used to support + the iSCSI protocol. + + template-url-syntax= + url-path= ; Depends on the concrete service type. + + --------------------------template ends here------------------------ + +5.2. The iSCSI Target Concrete Service Type Template + + This template defines the service "service:iscsi:target". An entity + containing iSCSI targets that wishes them discovered via SLP would + register each of them, with each of their addresses, as this service + type. + + Initiators (and perhaps management services) wishing to discover + targets in this way will generally use one of the following queries: + + 1. Find a specific target, given its iSCSI Target Name: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: (iscsi-name=iqn.2001-04.com.example:sn.456) + + 2. Find all of the iSCSI Target Names that may allow access to a + given initiator: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: (auth-name=iqn.1998-03.com.example:hostid.045A7B) + + 3. Find all of the iSCSI Target Names that may allow access to + any initiator: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: (auth-name=any) + + 4. Find all of the iSCSI Target Names that may allow access to + this initiator, or that will allow access to any initiator: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: &(auth-name=iqn.1998-03.com.example:hostid.045A7B) + (auth-name=any) + + + + + +Bakke & Hufferd Standards Track [Page 12] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + 5. Find all of the iSCSI Target Names that may allow access to + a given CHAP user name: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: (auth-cred=chap/my-user-name) + + 6. Find all of the iSCSI Target Names that may allow access to a + given initiator that supports two IP addresses, a CHAP credential + and SRP credential, and an initiator name: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: &(|(auth-name=iqn.com.example:host47)(auth-name=any) + |(auth-addr=192.0.2.3)(auth-addr=192.0.2.131)(auth-addr=any) + |(auth-cred=chap/foo)(auth-cred=srp/my-user-name) + (auth-cred=any)) + + 7. Find the iSCSI Target Names from which the given initiator is + allowed to boot: + + Service: service:iscsi:target + Scope: initiator-scope-list + Query: (boot-list=iqn.1998-03.com.example:hostid.045A7B) + + 8. In addition, a management service may wish to discover all + targets: + + Service: service:iscsi:target + Scope: management-server-scope-list + Query: <empty-string> + + More details on booting from an iSCSI target are defined in [BOOT]. + + Name of submitter: Mark Bakke + Language of service template: en + Security Considerations: see section 6. + + Template Text: + -------------------------template begins here----------------------- + template-type=iscsi:target + template-version=1.0 + + template-description= + + This is a concrete service type. The iscsi:target service type is + used to register individual target addresses to be discovered + by others. UAs will generally search for these by including one of + + + +Bakke & Hufferd Standards Track [Page 13] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + the following: + + - the iSCSI target name + - iSCSI initiator identifiers (iSCSI name, credential, IP address) + - the service URL + + template-url-syntax= + url-path = hostport "/" iscsi-name [ "/" identity ] + hostport = host [ ":" port ] + host = hostname / hostnumber ; DNS name or IP address + hostname = *( domainlabel "." ) toplabel + alphanum = ALPHA / DIGIT + domainlabel = alphanum / alphanum *[alphanum / "-"] alphanum + toplabel = ALPHA / ALPHA *[ alphanum / "-" ] alphanum + hostnumber = ipv4-number / ipv6-addr ; IPv4 or IPv6 address + ipv4-number = 1*3DIGIT 3("." 1*3DIGIT) + ipv6-addr = "[" ipv6-number "]" + ipv6-number = 6( h16 ":" ) ls32 + / "::" 5( h16 ":" ) ls32 + / [ h16 ] "::" 4( h16 ":" ) ls32 + / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + / [ *4( h16 ":" ) h16 ] "::" ls32 + / [ *5( h16 ":" ) h16 ] "::" h16 + / [ *6( h16 ":" ) h16 ] "::" + ls32 = ( h16 ":" h16 ) / ipv4-number + ; least-significant 32 bits of ipv6 address + h16 = 1*4HEXDIG + port = 1*DIGIT + iscsi-name = iscsi-char ; iSCSI target name + identity = iscsi-char ; optional identity string + iscsi-char = ALPHA / DIGIT / escaped / ":" / "-" / "." + ; Intended to allow UTF-8 encoded strings + escaped = 1*("\" HEXDIG HEXDIG) + ; + ; The iscsi-name part of the URL is required and must be the iSCSI + ; name of the target being registered. + ; A device representing multiple targets must individually + ; register each target/address combination with SLP. + ; The identity part of the URL is optional, and is used to + ; indicate an identity that is allowed to access this target. + ; + ; Example (split into two lines for clarity): + ; service:iscsi:target://192.0.2.3:3260/ + ; iqn.2001-04.com.example:sn.45678 + ; + ; IPv6 addresses are also supported; they use the notation + + + +Bakke & Hufferd Standards Track [Page 14] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + ; specified above and in [RFC3513], section 2.2 + + iscsi-name = string + # The iSCSI Name of this target. + # This must match the iscsi-name in the url-path. + + portal-group = integer + # The iSCSI portal group tag for this address. Addresses sharing + # the same iscsi-name and portal-group tag can be used within the + # same iSCSI session. Portal groups are described in [RFC3720]. + + transports = string M L + tcp + # This is a list of transport protocols that the registered + # entity supports. iSCSI is currently supported over TCP, + # but it is anticipated that it could be supported over other + # transports, such as SCTP, in the future. + tcp + + mgmt-entity = string O + # The fully qualified domain name, or IP address in dotted-decimal + # notation, of the management interface of the entity containing + # this target. + # + + alias = string O + # The alias string contains a descriptive name of the target. + + auth-name = string M X + # A list of iSCSI Initiator Names that can access this target. + # Normal iSCSI names will be 80 characters or less; max length + # is 255. + # Normally, only one or a few values will be in the list. + # Using the equivalence search on this will evaluate to "true" + # if any one of the items in this list matches the query. + # If this list contains the default name "any", any initiator + # is allowed to access this target, provided it matches + # the other auth-xxx attributes. + # + # This attribute contains security policy information. If this + # attribute is distributed via an Attribute Reply message, + # IPsec MUST be implemented. + + auth-addr = string M X + # A list of initiator IP addresses (or host names) which will + # be allowed access to this target. If this list contains the + # default name "any", any IP address is allowed access to this + # target, provided it matches the other auth-xxx attributes. + + + +Bakke & Hufferd Standards Track [Page 15] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + # + # This attribute contains security policy information. If this + # attribute is distributed via an Attribute Reply message, + # IPsec MUST be implemented. + + auth-cred = string M X + # A list of credentials which will be allowed access to the target + # (provided they can provide the correct password or other + # authenticator). Entries in this list are of the form + # "method/identifier", where the currently defined methods are + # "chap" and "srp", both of which take usernames as their + # identifiers. + # + # This attribute contains security policy information. If this + # attribute is distributed via an Attribute Reply message, + # IPsec MUST be implemented. + + boot-list = string M O + # A list of iSCSI Initiator Names that can boot from this target. + # This list works precisely like the auth-name attribute. A name + # appearing in this list must either appear in the access-list, + # or the access-list must contain the initiator name "iscsi". + # Otherwise, an initiator will be unable to find its boot + # target. If boot-list contains the name "iscsi", any host can boot + # from it, but I am not sure if this is useful to anyone. If this + # attribute is not registered, this target is not "bootable". + # + # Note that the LUN the host boots from is not specified here; a + # host will generally attempt to boot from LUN 0. + # + # It is quite possible that other attributes will need to be defined + # here for booting as well. + # + # This attribute contains security policy information. If this + # attribute is distributed via an Attribute Reply message, + # IPsec MUST be implemented. + + --------------------------template ends here------------------------ + +5.3. iSCSI Storage Management Service Templates + + This template defines the service "service:iscsi:sms". An entity + supporting one or more iSCSI management service protocols may + register itself with SLP as this service type. iSCSI clients and + servers wishing to discover storage management services using SLP + will usually search for them by the protocol(s) they support: + + + + + +Bakke & Hufferd Standards Track [Page 16] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + Service: service:iscsi:sms + Scope: initiator-scope-list + Query: (protocols=isns) + + Name of submitter: Mark Bakke + Language of service template: en + Security Considerations: see section 6. + + Template Text: + -------------------------template begins here----------------------- + template-type=iscsi:sms + template-version=1.0 + + template-description= + This is a concrete service type. The iscsi:sms service type + provides the capability for entities supporting iSCSI to discover + appropriate management services. + + template-url-syntax= + url-path = ; The URL of the management service [RFC2608]. + + protocols = string M + # The list of protocols supported by this name service. This + # list may be expanded in the future. There is no default. + # + # "isns" - This management service supports the use of the iSNS + # protocol for access management, health monitoring, and + # discovery management services. This protocol is defined + # in [ISNS]. + isns + + transports = string M L + tcp + # This is a list of transport protocols that the registered + # entity supports. + tcp, udp + + server-priority = integer + # The priority a client should give this server, when choosing + # between multiple servers with the same protocol type. + # When multiple servers are discovered for a given protocol type, + # this parameter indicates their relative precedence. Server + # precedence is protocol-specific; for some protocols, the primary + # server may have the highest server-priority value, while for + + + + + + + +Bakke & Hufferd Standards Track [Page 17] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + # others it may have the lowest. For example, with iSNS, the primary + # server has the lowest value (value 0). + + --------------------------template ends here------------------------ + +6. Security Considerations + + The SLPv2 security model as specified in [RFC2608] does not provide + confidentiality but does provide an authentication mechanism for UAs + to ensure that service advertisements only come from trusted SAs, + with the exception that it does not provide a mechanism to + authenticate "zero-result responses". See [RFC3723] for a discussion + of the SLPv2 [RFC2608] security model. + + Once a target or management server is discovered, authentication and + authorization are handled by the iSCSI protocol, or by the management + server's protocol. It is the responsibility of the providers of + these services to ensure that an inappropriately advertised or + discovered service does not compromise their security. + + When no security is used for SLPv2, there is a risk of distribution + of false discovery information. The primary countermeasure for this + risk is authentication. When this risk is a significant concern, + IPsec SAs and iSCSI in-band authentication SHOULD be used for iSCSI + traffic subject to this risk to ensure that iSCSI traffic only flows + between endpoints that have participated in IKE authentication and + iSCSI in-band authentication. For example, if an attacker + distributes discovery information falsely claiming that it is an + iSCSI target, it will lack the secret information necessary to + complete IKE authentication or iSCSI in-band authentication + successfully and therefore will be prevented from falsely sending or + receiving iSCSI traffic. + + A risk remains of a denial of service attack based on repeated use of + false discovery information that will cause initiation of IKE + negotiation. The countermeasures for this are administrative + configuration of each iSCSI Target to limit the peers it is willing + to communicate with (i.e., by IP address range and/or DNS domain), + and maintenance of a negative authentication cache to avoid + repeatedly contacting an iSCSI Target that fails to authenticate. + These three measures (i.e., IP address range limits, DNS domain + limits, negative authentication cache) MUST be implemented. + + The auth-name, auth-addr, auth-cred, and boot-list attributes + comprise security policy information. When these are distributed, + IPsec MUST be implemented. + + + + + +Bakke & Hufferd Standards Track [Page 18] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +6.1. Security Implementation + + Security for SLPv2 in an IP storage environment is specified in + [RFC3723]. IPsec is mandatory-to-implement for IPS clients and + servers. Thus, all IP storage clients, including those invoking SLP, + can be assumed to support IPsec. SLP servers, however, cannot be + assumed to implement IPsec, since there is no such requirement in + standard SLP. In particular, SLP Directory Agents (DA) may be + running on machines other than those running the IPS protocols. + + IPsec SHOULD be implemented for SLPv2 as specified in [RFC3723]; this + includes ESP with a non-null transform to provide both authentication + and confidentiality. + + When SLPv2 can be used to distribute auth-name, auth-addr, auth-cred, + and boot-list information (see section 5.2 above), IPsec MUST be + implemented, as these items are considered sensitive security policy + information. If IPsec is not implemented, auth-name, auth-addr, + auth-cred, and boot-list information MUST NOT be distributed via + SLPv2 and MUST NOT be used if discovered via SLPv2. + + Because the IP storage services have their own authentication + capabilities when located, SLPv2 authentication is OPTIONAL to + implement and use (as discussed in more detail in [RFC3723]). + +7. IANA Considerations + + This document describes three SLP Templates. They have been reviewed + and approved by the IESG and registered in the IANA's "SVRLOC + Templates" registry. This process is described in the IANA + Considerations section of [RFC2609]. + +8. Summary + + This document describes how SLP can be used by iSCSI initiators to + find iSCSI targets and storage management servers. Service type + templates for iSCSI targets and storage management servers are + presented. + +9. Normative References + + [RFC2608] Guttman, E., Perkins, C., Veizades, J., and M. Day, + "Service Location Protocol, Version 2", RFC 2608, June + 1999. + + [RFC2609] Guttman, E., Perkins, C., and J. Kempf, "Service + Templates and Service: Schemes", RFC 2609, June 1999. + + + + +Bakke & Hufferd Standards Track [Page 19] + +RFC 4018 iSCSI and SLPv2 April 2005 + + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3491] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep + Profile for Internationalized Domain Names (IDN)", RFC + 3491, March 2003. + + [RFC3513] Hinden, R. and S. Deering, "Internet Protocol Version 6 + (IPv6) Addressing Architecture", RFC 3513, April 2003. + + [RFC3720] Satran, J., Meth, K., Sapuntzakis, C., Chadalapaka, M., + and E. Zeidner, "Internet Small Computer Systems + Interface (iSCSI)", RFC 3720, April 2004. + + [RFC3722] Bakke, M., "String Profile for Internet Small Computer + Systems Interface (iSCSI) Names", RFC 3722, April 2004. + + [RFC3723] Aboba, B., Tseng, J., Walker, J., Rangan, V., and F. + Travostino, "Securing Block Storage Protocols over IP", + RFC 3723, April 2004. + +10. Informative References + + [RFC2614] Kempf, J. and E. Guttman, "An API for Service Location", + RFC 2614, June 1999. + + [SAM2] ANSI T10. "SCSI Architectural Model 2", March 2000. + + [RFC3721] Bakke, M., Hafner, J., Hufferd, J., Voruganti, K., and M. + Krueger, "Internet Small Computer Systems Interface + (iSCSI) Naming and Discovery", RFC 3721, April 2004. + + [ISNS] Tseng, J., Gibbons, K., Travostino, F., Du Laney, C. and + J. Souza, "Internet Storage Name Service", Work in + Progress, February 2004. + + [BOOT] Sarkar, P., Missimer, D. and C. Sapuntzakis, "A Standard + for Bootstrapping Clients using the iSCSI Protocol", Work + in Progress, March 2004. + + [RFC3105] Kempf, J. and G. Montenegro, "Finding an RSIP Server with + SLP", RFC 3105, October 2001. + + + + + + + + + +Bakke & Hufferd Standards Track [Page 20] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +11. Acknowledgements + + This document was produced by the iSCSI Naming and Discovery team, + including Joe Czap, Jim Hafner, John Hufferd, and Kaladhar Voruganti + (IBM), Howard Hall (Pirus), Jack Harwood (EMC), Yaron Klein (Sanrad), + Marjorie Krueger (HP), Lawrence Lamers (San Valley), Todd Sperry + (Adaptec), and Joshua Tseng (Nishan). Thanks also to Julian Satran + (IBM) for suggesting the use of SLP for iSCSI discovery, and to Matt + Peterson (Caldera) and James Kempf (Sun) for reviewing the document + from an SLP perspective. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Bakke & Hufferd Standards Track [Page 21] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +Authors' Addresses + + Mark Bakke + Cisco Systems, Inc. + 7900 International Drive, Suite 400 + Bloomington, MN + USA 55425 + + EMail: mbakke@cisco.com + + + Kaladhar Voruganti + IBM Almaden Research Center + 650 Harry Road + San Jose, CA 95120 + + EMail: kaladhar@us.ibm.com + + + John L. Hufferd + IBM Storage Systems Group + 5600 Cottle Road + San Jose, CA 95193 + + Phone: +1 408 997-6136 + EMail: jlhufferd@comcast.net + + + Marjorie Krueger + Hewlett-Packard Corporation + 8000 Foothills Blvd + Roseville, CA 95747-5668, USA + + Phone: +1 916 785-2656 + EMail: marjorie_krueger@hp.com + + + Todd Sperry + Adaptec, Inc. + 691 South Milpitas Boulevard + Milpitas, Ca. 95035 + + Phone: +1 408 957-4980 + EMail: todd_sperry@adaptec.com + + + + + + + +Bakke & Hufferd Standards Track [Page 22] + +RFC 4018 iSCSI and SLPv2 April 2005 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2005). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET + ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE + INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at ietf- + ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + +Bakke & Hufferd Standards Track [Page 23] + diff --git a/utils/open-isns/doc/rfc4171.txt b/utils/open-isns/doc/rfc4171.txt new file mode 100644 index 0000000..c02487c --- /dev/null +++ b/utils/open-isns/doc/rfc4171.txt @@ -0,0 +1,6891 @@ + + + + + + +Network Working Group J. Tseng +Request for Comments: 4171 Riverbed Technology +Category: Standards Track K. Gibbons + McDATA Corporation + F. Travostino + Nortel + C. Du Laney + Rincon Research Corporation + J. Souza + Microsoft + September 2005 + + + Internet Storage Name Service (iSNS) + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2005). + +Abstract + + This document specifies the Internet Storage Name Service (iSNS) + protocol, used for interaction between iSNS servers and iSNS clients, + which facilitates automated discovery, management, and configuration + of iSCSI and Fibre Channel devices (using iFCP gateways) on a TCP/IP + network. iSNS provides intelligent storage discovery and management + services comparable to those found in Fibre Channel networks, + allowing a commodity IP network to function in a capacity similar to + that of a storage area network. iSNS facilitates a seamless + integration of IP and Fibre Channel networks due to its ability to + emulate Fibre Channel fabric services and to manage both iSCSI and + Fibre Channel devices. iSNS thereby provides value in any storage + network comprised of iSCSI devices, Fibre Channel devices (using iFCP + gateways), or any combination thereof. + + + + + + + + + +Tseng, et al. Standards Track [Page 1] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +Table of Contents + + 1. Introduction................................................... 6 + 1.1. Conventions Used in This Document........................ 6 + 1.2. Purpose of This Document................................. 6 + 2. iSNS Overview.................................................. 6 + 2.1. iSNS Architectural Components ........................... 7 + 2.1.1. iSNS Protocol (iSNSP) ........................... 7 + 2.1.2. iSNS Client...................................... 7 + 2.1.3. iSNS Server...................................... 7 + 2.1.4. iSNS Database ................................... 7 + 2.1.5. iSCSI............................................ 7 + 2.1.6. iFCP............................................. 7 + 2.2. iSNS Functional Overview................................. 8 + 2.2.1. Name Registration Service........................ 8 + 2.2.2. Discovery Domain and Login Control Service....... 8 + 2.2.3. State Change Notification Service............... 10 + 2.2.4. Open Mapping between + Fibre Channel and iSCSI Devices................. 11 + 2.3. iSNS Usage Model........................................ 11 + 2.3.1. iSCSI Initiator................................. 12 + 2.3.2. iSCSI Target.................................... 12 + 2.3.3. iSCSI-FC Gateway................................ 12 + 2.3.4. iFCP Gateway.................................... 12 + 2.3.5. Management Station.............................. 12 + 2.4. Administratively Controlled iSNS Settings............... 13 + 2.5. iSNS Server Discovery .................................. 14 + 2.5.1. Service Location Protocol (SLP)................. 14 + 2.5.2. Dynamic Host Configuration Protocol (DHCP)...... 14 + 2.5.3. iSNS Heartbeat Message.......................... 14 + 2.6. iSNS and Network Address Translation (NAT).............. 14 + 2.7. Transfer of iSNS Database Records between iSNS Servers.. 15 + 2.8. Backup iSNS Servers..................................... 17 + 2.9. Transport Protocols..................................... 19 + 2.9.1. Use of TCP for iSNS Communication............... 19 + 2.9.2. Use of UDP for iSNS Communication............... 20 + 2.9.3. iSNS Multicast and Broadcast Messages........... 20 + 2.10. Simple Network Management Protocol (SNMP) Requirements.. 21 + 3. iSNS Object Model............................................. 21 + 3.1. Network Entity Object .................................. 22 + 3.2. Portal Object .......................................... 22 + 3.3. Storage Node Object..................................... 22 + 3.4. Portal Group Object..................................... 23 + 3.5. FC Device Object........................................ 24 + 3.6. Discovery Domain Object................................. 24 + 3.7. Discovery Domain Set Object............................. 24 + 3.8. iSNS Database Model..................................... 24 + 4. iSNS Implementation Requirements.............................. 25 + + + +Tseng, et al. Standards Track [Page 2] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + 4.1. iSCSI Requirements...................................... 25 + 4.1.1. Required Attributes for Support of iSCSI........ 26 + 4.1.2. Examples: iSCSI Object Model Diagrams........... 28 + 4.1.3. Required Commands and + Response Messages for Support of iSCSI.......... 30 + 4.2. iFCP Requirements....................................... 31 + 4.2.1. Required Attributes for Support of iFCP......... 31 + 4.2.2. Example: iFCP Object Model Diagram.............. 32 + 4.2.3. Required Commands and + Response Messages for Support of iFCP........... 34 + 5. iSNSP Message Format.......................................... 35 + 5.1. iSNSP PDU Header........................................ 35 + 5.1.1. iSNSP Version................................... 36 + 5.1.2. iSNSP Function ID............................... 36 + 5.1.3. iSNSP PDU Length................................ 36 + 5.1.4. iSNSP Flags..................................... 36 + 5.1.5. iSNSP Transaction ID............................ 36 + 5.1.6. iSNSP Sequence ID............................... 37 + 5.2. iSNSP Message Segmentation and Reassembly............... 37 + 5.3. iSNSP PDU Payload....................................... 37 + 5.3.1. Attribute Value 4-Byte Alignment................ 38 + 5.4. iSNSP Response Status Codes............................. 39 + 5.5. Authentication for iSNS Multicast and Broadcast Messages 39 + 5.6. Registration and Query Messages......................... 41 + 5.6.1. Source Attribute................................ 42 + 5.6.2. Message Key Attributes.......................... 42 + 5.6.3. Delimiter Attribute............................. 42 + 5.6.4. Operating Attributes............................ 43 + 5.6.5. Registration and Query Request Message Types ... 44 + 5.7. Response Messages....................................... 66 + 5.7.1. Status Code..................................... 66 + 5.7.2. Message Key Attributes in Response.............. 66 + 5.7.3. Delimiter Attribute in Response................. 67 + 5.7.4. Operating Attributes in Response................ 67 + 5.7.5. Registration and Query Response Message Type.... 67 + 5.8. Vendor-Specific Messages................................ 72 + 6. iSNS Attributes............................................... 73 + 6.1. iSNS Attribute Summary.................................. 73 + 6.2. Entity Identifier-Keyed Attributes...................... 76 + 6.2.1. Entity Identifier (EID)......................... 76 + 6.2.2. Entity Protocol................................. 76 + 6.2.3. Management IP Address .......................... 77 + 6.2.4. Entity Registration Timestamp .................. 77 + 6.2.5. Protocol Version Range.......................... 77 + 6.2.6. Registration Period............................. 78 + 6.2.7. Entity Index.................................... 78 + 6.2.8. Entity Next Index............................... 79 + 6.2.9. Entity ISAKMP Phase-1 Proposals................. 79 + + + +Tseng, et al. Standards Track [Page 3] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + 6.2.10. Entity Certificate.............................. 79 + 6.3. Portal-Keyed Attributes................................. 80 + 6.3.1. Portal IP Address............................... 80 + 6.3.2. Portal TCP/UDP Port............................. 80 + 6.3.3. Portal Symbolic Name............................ 80 + 6.3.4. Entity Status Inquiry Interval.................. 81 + 6.3.5. ESI Port........................................ 82 + 6.3.6. Portal Index.................................... 82 + 6.3.7. SCN Port........................................ 82 + 6.3.8. Portal Next Index............................... 83 + 6.3.9. Portal Security Bitmap.......................... 83 + 6.3.10. Portal ISAKMP Phase-1 Proposals................. 84 + 6.3.11. Portal ISAKMP Phase-2 Proposals................. 84 + 6.3.12. Portal Certificate.............................. 84 + 6.4. iSCSI Node-Keyed Attributes............................. 84 + 6.4.1. iSCSI Name...................................... 85 + 6.4.2. iSCSI Node Type................................. 85 + 6.4.3. iSCSI Node Alias................................ 86 + 6.4.4. iSCSI Node SCN Bitmap .......................... 86 + 6.4.5. iSCSI Node Index................................ 87 + 6.4.6. WWNN Token...................................... 87 + 6.4.7. iSCSI Node Next Index .......................... 89 + 6.4.8. iSCSI AuthMethod................................ 89 + 6.5. Portal Group (PG) Object-Keyed Attributes............... 89 + 6.5.1. Portal Group iSCSI Name......................... 90 + 6.5.2. PG Portal IP Addr............................... 90 + 6.5.3. PG Portal TCP/UDP Port.......................... 90 + 6.5.4. Portal Group Tag (PGT).......................... 90 + 6.5.5. Portal Group Index.............................. 90 + 6.5.6. Portal Group Next Index......................... 91 + 6.6. FC Port Name-Keyed Attributes .......................... 91 + 6.6.1. FC Port Name (WWPN)............................. 91 + 6.6.2. Port ID (FC_ID)................................. 91 + 6.6.3. FC Port Type.................................... 92 + 6.6.4. Symbolic Port Name.............................. 92 + 6.6.5. Fabric Port Name (FWWN)......................... 92 + 6.6.6. Hard Address.................................... 92 + 6.6.7. Port IP Address................................. 92 + 6.6.8. Class of Service (COS).......................... 93 + 6.6.9. FC-4 Types...................................... 93 + 6.6.10. FC-4 Descriptor................................. 93 + 6.6.11. FC-4 Features .................................. 93 + 6.6.12. iFCP SCN Bitmap................................. 93 + 6.6.13. Port Role....................................... 94 + 6.6.14. Permanent Port Name (PPN)....................... 95 + 6.7. Node-Keyed Attributes .................................. 95 + 6.7.1. FC Node Name (WWNN)............................. 95 + 6.7.2. Symbolic Node Name.............................. 95 + + + +Tseng, et al. Standards Track [Page 4] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + 6.7.3. Node IP Address................................. 95 + 6.7.4. Node IPA........................................ 96 + 6.7.5. Proxy iSCSI Name................................ 96 + 6.8. Other Attributes........................................ 96 + 6.8.1. FC-4 Type Code.................................. 96 + 6.8.2. iFCP Switch Name................................ 96 + 6.8.3. iFCP Transparent Mode Commands.................. 97 + 6.9. iSNS Server-Specific Attributes......................... 97 + 6.9.1. iSNS Server Vendor OUI.......................... 98 + 6.10. Vendor-Specific Attributes.............................. 98 + 6.10.1. Vendor-Specific Server Attributes............... 98 + 6.10.2. Vendor-Specific Entity Attributes............... 98 + 6.10.3. Vendor-Specific Portal Attributes............... 99 + 6.10.4. Vendor-Specific iSCSI Node Attributes........... 99 + 6.10.5. Vendor-Specific FC Port Name Attributes......... 99 + 6.10.6. Vendor-Specific FC Node Name Attributes......... 99 + 6.10.7. Vendor-Specific Discovery Domain Attributes..... 99 + 6.10.8. Vendor-Specific Discovery Domain Set Attributes. 99 + 6.10.9. Other Vendor-Specific Attributes................ 99 + 6.11. Discovery Domain Registration Attributes............... 100 + 6.11.1. DD Set ID Keyed Attributes..................... 100 + 6.11.2. DD ID Keyed Attributes......................... 101 + 7. Security Considerations...................................... 103 + 7.1. iSNS Security Threat Analysis ......................... 103 + 7.2. iSNS Security Implementation and Usage Requirements.... 104 + 7.3. Discovering Security Requirements of Peer Devices...... 105 + 7.4. Configuring Security Policies of iFCP/iSCSI Devices.... 106 + 7.5. Resource Issues........................................ 107 + 7.6. iSNS Interaction with IKE and IPSec.................... 107 + 8. IANA Considerations.......................................... 107 + 8.1. Registry of Block Storage Protocols.................... 107 + 8.2. Registry of Standard iSNS Attributes .................. 108 + 8.3. Block Structure Descriptor (BSD) Registry.............. 108 + 9. Normative References......................................... 109 + 10. Informative References....................................... 110 + Appendix A: iSNS Examples........................................ 112 + A.1. iSCSI Initialization Example........................... 112 + A.1.1. Simple iSCSI Target Registration............... 112 + A.1.2. Target Registration and DD Configuration....... 114 + A.1.3. Initiator Registration and Target Discovery.... 117 + Acknowledgements................................................. 121 + + + + + + + + + + +Tseng, et al. Standards Track [Page 5] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +1. Introduction + +1.1. Conventions Used in This Document + + "iSNS" refers to the storage network model and associated services + covered in the text of this document. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + All frame formats are in big endian network byte order. + + All unused fields and bitmaps, including those that are RESERVED, + SHOULD be set to zero when sending and ignored when receiving. + +1.2. Purpose of This Document + + This is a standards track document containing normative text + specifying the iSNS Protocol, used by iSCSI and iFCP devices to + communicate with the iSNS server. This document focuses on the + interaction between iSNS servers and iSNS clients; interactions among + multiple authoritative primary iSNS servers are a potential topic for + future work. + +2. iSNS Overview + + iSNS facilitates scalable configuration and management of iSCSI and + Fibre Channel (FCP) storage devices in an IP network by providing a + set of services comparable to that available in Fibre Channel + networks. iSNS thus allows a commodity IP network to function at a + level of intelligence comparable to a Fibre Channel fabric. iSNS + allows the administrator to go beyond a simple device-by-device + management model, where each storage device is manually and + individually configured with its own list of known initiators and + targets. Using the iSNS, each storage device subordinates its + discovery and management responsibilities to the iSNS server. The + iSNS server thereby serves as the consolidated configuration point + through which management stations can configure and manage the entire + storage network, including both iSCSI and Fibre Channel devices. + + iSNS can be implemented to support iSCSI and/or iFCP protocols as + needed; an iSNS implementation MAY provide support for one or both of + these protocols as desired by the implementor. Implementation + requirements within each of these protocols are further discussed in + Section 5. Use of iSNS is OPTIONAL for iSCSI and REQUIRED for iFCP. + + + + + +Tseng, et al. Standards Track [Page 6] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +2.1. iSNS Architectural Components + +2.1.1. iSNS Protocol (iSNSP) + + The iSNS Protocol (iSNSP) is a flexible and lightweight protocol that + specifies how iSNS clients and servers communicate. It is suitable + for various platforms, including switches and targets as well as + server hosts. + +2.1.2. iSNS Client + + iSNS clients initiate transactions with iSNS servers using the iSNSP. + iSNS clients are processes that are co-resident in the storage + device, and that can register device attribute information, download + information about other registered clients in a common Discovery + Domain (DD), and receive asynchronous notification of events that + occur in their DD(s). Management stations are a special type of iSNS + client that have access to all DDs stored in the iSNS. + +2.1.3. iSNS Server + + iSNS servers respond to iSNS protocol queries and requests, and + initiate iSNS protocol State Change Notifications. Properly + authenticated information submitted by a registration request is + stored in an iSNS database. + +2.1.4. iSNS Database + + The iSNS database is the information repository for the iSNS + server(s). It maintains information about iSNS client attributes. A + directory-enabled implementation of iSNS may store client attributes + in an LDAP directory infrastructure. + +2.1.5. iSCSI + + iSCSI (Internet SCSI) is an encapsulation of SCSI for a new + generation of storage devices interconnected with TCP/IP [iSCSI]. + +2.1.6. iFCP + + iFCP (Internet FCP) is a gateway-to-gateway protocol designed to + interconnect existing Fibre Channel and SCSI devices using TCP/IP. + iFCP maps the existing FCP standard and associated Fibre Channel + services to TCP/IP [iFCP]. + + + + + + + +Tseng, et al. Standards Track [Page 7] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +2.2. iSNS Functional Overview + + There are four main functions of the iSNS: + + 1) A Name Service Providing Storage Resource Discovery + + 2) Discovery Domain (DD) and Login Control Service + + 3) State Change Notification Service + + 4) Open Mapping of Fibre Channel and iSCSI Devices + +2.2.1. Name Registration Service + + The iSNS provides a registration function to allow all entities in a + storage network to register and query the iSNS database. Both + targets and initiators can register in the iSNS database, as well as + query for information about other initiators and targets. This + allows, for example, a client initiator to obtain information about + target devices from the iSNS server. This service is modeled on the + Fibre Channel Generic Services Name Server described in FC-GS-4, with + extensions, operating within the context of an IP network. + + The naming registration service also provides the ability to obtain a + network-unique Domain ID for iFCP gateways when one is required. + +2.2.2. Discovery Domain and Login Control Service + + The Discovery Domain (DD) Service facilitates the partitioning of + Storage Nodes into more manageable groupings for administrative and + login control purposes. It allows the administrator to limit the + login process of each host to the more appropriate subset of targets + registered in the iSNS. This is particularly important for reducing + the number of unnecessary logins (iSCSI logins or Fibre Channel Port + Logins), and for limiting the amount of time that the host spends + initializing login relationships as the size of the storage network + scales up. Storage Nodes must be in at least one common enabled DD + in order to obtain information about each other. Devices can be + members of multiple DDs simultaneously. + + Login Control allows targets to delegate their access + control/authorization policies to the iSNS server. This is + consistent with the goal of centralizing management of those storage + devices using the iSNS server. The target node or device downloads + the list of authorized initiators from the iSNS. Each node or device + is uniquely identified by an iSCSI Name or FC Port Name. Only + + + + + +Tseng, et al. Standards Track [Page 8] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + initiators that match the required identification and authorization + provided by the iSNS will be allowed access by that target Node + during session establishment. + + Placing Portals of a Network Entity into Discovery Domains allows + administrators to indicate the preferred IP Portal interface through + which storage traffic should access specific Storage Nodes of that + Network Entity. If no Portals of a Network Entity have been placed + into a DD, then queries scoped to that DD SHALL report all Portals of + that Network Entity. If one or more Portals of a Network Entity have + been placed into a DD, then queries scoped to that DD SHALL report + only those Portals that have been explicitly placed in the DD. + + DDs can be managed offline through a separate management workstation + using the iSNSP or SNMP. If the target opts to use the Login Control + feature of the iSNS, the target delegates management of access + control policy (i.e., the list of initiators allowed to log in to + that target) to the management workstations that are managing the + configuration in the iSNS database. + + If administratively authorized, a target can upload its own Login + Control list. This is accomplished using the DDReg message and + listing the iSCSI name of each initiator to be registered in the + target's DD. + + An implementation MAY decide that newly registered devices that have + not explicitly been placed into a DD by the management station will + be placed into a "default DD" contained in a "default DDS" whose + initial DD Set Status value is "enabled". This makes them visible to + other devices in the default DD. Other implementations MAY decide + that they are registered with no DD, making them inaccessible to + source-scoped iSNSP messages. + + The iSNS server uses the Source Attribute of each iSNSP message to + determine the originator of the request and to scope the operation to + a set of Discovery Domains. In addition, the Node Type (specified in + the iFCP or iSCSI Node Type bitmap field) may also be used to + determine authorization for the specified iSNS operation. For + example, only Control Nodes are authorized to create or delete + discovery domains. + + Valid and active Discovery Domains (DDs) belong to at least one + active Discovery Domain Set (DDS). Discovery Domains that do not + belong to an activated DDS are not enabled. The iSNS server MUST + maintain the state of DD membership for all Storage Nodes, even for + those that have been deregistered. DD membership is persistent + regardless of whether a Storage Node is actively registered in the + iSNS database. + + + +Tseng, et al. Standards Track [Page 9] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +2.2.3. State Change Notification Service + + The State Change Notification (SCN) service allows the iSNS Server to + issue notifications about network events that affect the operational + state of Storage Nodes. The iSNS client may register for + notifications on behalf of its Storage Nodes for notification of + events detected by the iSNS Server. SCNs notify iSNS clients of + explicit or implicit changes to the iSNS database; they do not + necessarily indicate the state of connectivity to peer storage + devices in the network. The response of a storage device to receipt + of an SCN is implementation-specific; the policy for responding to + SCNs is outside of the scope of this document. + + There are two types of SCN registrations: regular registrations and + management registrations. Management registrations result in + management SCNs, whereas regular registrations result in regular + SCNs. The type of registration and SCN message is indicated in the + SCN bitmap (see Sections 6.4.4 and 6.6.12). + + A regular SCN registration indicates that the Discovery Domain + Service SHALL be used to control the distribution of SCN messages. + Receipt of regular SCNs is limited to the discovery domains in which + the SCN-triggering event takes place. Regular SCNs do not contain + information about discovery domains. + + A management SCN registration can only by requested by Control Nodes. + Management SCNs resulting from management registrations are not bound + by the Discovery Domain service. Authorization to request management + SCN registrations may be administratively controlled. + + The iSNS server SHOULD be implemented with hardware and software + resources sufficient to support the expected number of iSNS clients. + However, if resources are unexpectedly exhausted, then the iSNS + server MAY refuse SCN service by returning an SCN Registration + Rejected (Status Code 17). The rejection might occur in situations + where the network size or current number of SCN registrations has + passed an implementation-specific threshold. A client not allowed to + register for SCNs may decide to monitor its sessions with other + storage devices directly. + + + The specific notification mechanism by which the iSNS server learns + of the events that trigger SCNs is implementation-specific, but can + include examples such as explicit notification messages from an iSNS + client to the iSNS server, or a hardware interrupt to a switch-hosted + iSNS server as a result of link failure. + + + + + +Tseng, et al. Standards Track [Page 10] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +2.2.4. Open Mapping between Fibre Channel and iSCSI Devices + + The iSNS database stores naming and discovery information about both + Fibre Channel and iSCSI devices. This allows the iSNS server to + store mappings of a Fibre Channel device to a proxy iSCSI device + "image" in the IP network. Similarly, mappings of an iSCSI device to + a "proxy WWN" can be stored under the WWNN Token field for that iSCSI + device. + + Furthermore, through use of iSCSI-FC gateways, Fibre Channel-aware + management stations can interact with the iSNS server to retrieve + information about Fibre Channel devices, and use this information to + manage Fibre Channel and iSCSI devices. This allows management + functions such as Discovery Domains and State Change Notifications to + be applied seamlessly to both iSCSI and Fibre Channel devices, + facilitating integration of IP networks with Fibre Channel devices + and fabrics. + + Note that Fibre Channel attributes are stored as iFCP attributes, and + that the ability to store this information in the iSNS server is + useful even if the iFCP protocol is not implemented. In particular, + tag 101 can be used to store a "Proxy iSCSI Name" for Fibre Channel + devices registered in the iSNS server. This field is used to + associate the FC device with an iSCSI registration entry that is used + for the Fibre Channel device to communicate with iSCSI devices in the + IP network. Conversely, tag 37 (see Section 6.1) contains a WWNN + Token field, which can be used to store an FC Node Name (WWNN) value + used by iSCSI-FC gateways to represent an iSCSI device in the Fibre + Channel domain. + + By storing the mapping between Fibre Channel and iSCSI devices in the + iSNS server, this information becomes open to any authorized iSNS + client wishing to retrieve and use this information. In many cases, + this provides advantages over storing the information internally + within an iSCSI-FC gateway, where the mapping is inaccessible to + other devices except by proprietary mechanisms. + +2.3. iSNS Usage Model + + The following is a high-level description of how each type of device + in a storage network can utilize iSNS. Each type of device interacts + with the iSNS server as an iSNS client and must register itself in + the iSNS database in order to access services provided by the iSNS. + + + + + + + + +Tseng, et al. Standards Track [Page 11] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +2.3.1. iSCSI Initiator + + An iSCSI initiator will query the iSNS server to discover the + presence and location of iSCSI target devices. It may also request + state change notifications (SCNs) so that it can be notified of new + targets that appear on the network after the initial bootup and + discovery. SCNs can also inform the iSCSI initiator of targets that + have been removed from or no longer available in the storage network, + so that incomplete storage sessions can be gracefully terminated and + resources for non-existent targets can be reallocated. + +2.3.2. iSCSI Target + + An iSCSI target allows itself to be discovered by iSCSI initiators by + registering its presence in the iSNS server. It may also register + for SCNs in order to detect the addition or removal of initiators for + resource allocation purposes. The iSCSI target device may also + register for Entity Status Inquiry (ESI) messages, which allow the + iSNS to monitor the target device's availability in the storage + network. + +2.3.3. iSCSI-FC Gateway + + An iSCSI-FC gateway bridges devices in a Fibre Channel network to an + iSCSI/IP network. It may use the iSNS server to store FC device + attributes discovered in the FC name server, as well as mappings of + FC device identifiers to iSCSI device identifiers. iSNS has the + capability to store all attributes of both iSCSI and Fibre Channel + devices; iSCSI devices are managed through direct interaction using + iSNS, while FC devices can be indirectly managed through iSNS + interactions with the iSCSI-FC gateway. This allows both iSCSI and + Fibre Channel devices to be managed in a seamless management + framework. + +2.3.4. iFCP Gateway + + An iFCP gateway uses iSNS to emulate the services provided by a Fibre + Channel name server for FC devices in its gateway region. iSNS + provides basic discovery and zoning configuration information to be + enforced by the iFCP gateway. When queried, iSNS returns information + on the N_Port network address used to establish iFCP sessions between + FC devices supported by iFCP gateways. + +2.3.5. Management Station + + A management station uses iSNS to monitor storage devices and to + enable or disable storage sessions by configuring discovery domains. + A management station usually interacts with the iSNS server as a + + + +Tseng, et al. Standards Track [Page 12] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Control Node endowed with access to all iSNS database records and + with special privileges to configure discovery domains. Through + manipulation of discovery domains, the management station controls + the scope of device discovery for iSNS clients querying the iSNS + server. + +2.4. Administratively Controlled iSNS Settings + + Some important operational settings for the iSNS server are + configured using administrative means, such as a configuration file, + a console port, an SNMP, or another implementation-specific method. + These administratively-controlled settings cannot be configured using + the iSNS Protocol, and therefore the iSNS server implementation MUST + provide for such an administrative control interface. + + The following is a list of parameters that are administratively + controlled for the iSNS server. In the absence of alternative + settings provided by the administrator, the following specified + default settings MUST be used. + + Setting Default Setting + ------- --------------- + ESI Non-Response Threshold 3 (see 5.6.5.13) + Management SCNs (Control Nodes only) enabled (see 5.6.5.8) + Default DD/DDS disabled + DD/DDS Modification + - Control Node enabled + - iSCSI Target Node Type disabled + - iSCSI Initiator Node Type disabled + - iFCP Target Port Role disabled + - iFCP Initiator Port Role disabled + Authorized Control Nodes N/A + + ESI Non-Response Threshold: determines the number of ESI messages + sent without receiving a response before the network + entity is deregistered from the iSNS database. + + Management SCN for Control Node: determines whether a registered + Control Node is permitted to register to receive + Management SCNs. + + Default DD/DDS: determines whether a newly registered device not + explicitly placed into a discovery domain (DD) and + discovery domain set (DDS) is placed into a default + DD/DDS. + + DD/DDS Modification: determines whether the specified type of Node is + allowed to add, delete or update DDs and DDSs. + + + +Tseng, et al. Standards Track [Page 13] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Authorized Control Nodes: a list of Nodes identified by iSCSI Name or + FC Port Name WWPN that are authorized to register as + Control Nodes. + +2.5. iSNS Server Discovery + +2.5.1. Service Location Protocol (SLP) + + The Service Location Protocol (SLP) provides a flexible and scalable + framework for providing hosts with access to information about the + existence, location, and configuration of networked services, + including the iSNS server. SLP can be used by iSNS clients to + discover the IP address or FQDN of the iSNS server. To implement + discovery through SLP, a Service Agent (SA) should be cohosted in the + iSNS server, and a User Agent (UA) should be in each iSNS client. + Each client multicasts a discovery message requesting the IP address + of the iSNS server(s). The SA responds to this request. Optionally, + the location of the iSNS server can be stored in the SLP Directory + Agent (DA). + + Note that a complete description and specification of SLP can be + found in [RFC2608], and is beyond the scope of this document. A + service template for using SLP to locate iSNS servers can be found in + [iSCSI-SLP]. + +2.5.2. Dynamic Host Configuration Protocol (DHCP) + + The IP address of the iSNS server can be stored in a DHCP server to + be downloaded by iSNS clients using a DHCP option. The DHCP option + number to be used for distributing the iSNS server location is found + in [iSNSOption]. + +2.5.3. iSNS Heartbeat Message + + The iSNS heartbeat message is described in Section 5.6.5.14. It + allows iSNS clients within the broadcast or multicast domain of the + iSNS server to discover the location of the active iSNS server and + any backup servers. + +2.6. iSNS and Network Address Translation (NAT) + + The existence of NAT will have an impact upon information retrieved + from the iSNS server. If the iSNS client exists in an addressing + domain different from that of the iSNS server, then IP address + information stored in the iSNS server may not be correct when + interpreted in the domain of the iSNS client. + + + + + +Tseng, et al. Standards Track [Page 14] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + There are several possible approaches to allow operation of iSNS + within a NAT network. The first approach is to require use of the + canonical TCP port number by both targets and initiators when + addressing targets across a NAT boundary, and for the iSNS client not + to query for nominal IP addresses. Rather, the iSNS client queries + for the DNS Fully Qualified Domain Name stored in the Entity + Identifier field when seeking addressing information. Once + retrieved, the DNS name can be interpreted in each address domain and + mapped to the appropriate IP address by local DNS servers. + + A second approach is to deploy a distributed network of iSNS servers. + Local iSNS servers are deployed inside and outside NAT boundaries, + with each local server storing relevant IP addresses for their + respective NAT domains. Updates among the network of decentralized, + local iSNS servers are handled using LDAP and appropriate NAT + translation rules implemented within the update mechanism in each + server. + + Finally, note that it is possible for an iSNS server in the private + addressing domain behind a NAT boundary to exclusively support iSNS + clients that are operating in the global IP addressing domain. If + this is the case, the administrator only needs to ensure that the + appropriate mappings are configured on the NAT gateways to allow the + iSNS clients to initiate iSNSP sessions to the iSNS server. All + registered addresses contained in the iSNS server are thus public IP + addresses for use outside the NAT boundary. Care should be taken to + ensure that there are no iSNS clients querying the server from inside + the NAT boundary. + +2.7. Transfer of iSNS Database Records between iSNS Servers + + Transfer of iSNS database records between iSNS servers has important + applications, including the following: + + 1) An independent organization needs to transfer storage information + to a different organization. Each organization independently + maintains its own iSNS infrastructure. To facilitate discovery + of storage assets of the peer organization using IP, iSNS + database records can be transferred between authoritative iSNS + servers from each organization. This allows storage sessions to + be established directly between devices residing in each + organization's storage network infrastructure over a common IP + network. + + 2) Multiple iSNS servers are desired for redundancy. Backup servers + need to maintain copies of the primary server's dynamically + changing database. + + + + +Tseng, et al. Standards Track [Page 15] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + To support the above applications, information in an iSNS server can + be distributed to other iSNS servers either using the iSNS protocol, + or through out-of-band mechanisms using non-iSNS protocols. The + following examples illustrate possible methods for transferring data + records between iSNS servers. In the first example, a back-end LDAP + information base is used to support the iSNS server, and the data is + transferred using the LDAP protocol. Once the record transfer of the + remote device is completed, it becomes visible and accessible to + local devices using the local iSNS server. This allows local devices + to establish sessions with remote devices (provided that firewall + boundaries can be negotiated). + + +-------------------------+ +-------------------------+ + |+------+ iSNSP | | iSNSP +-----+ | + ||dev A |<----->+------+ | | +------+<----->|dev C| | + |+------+ | | | | | | +-----+ | + |+------+ iSNSP |local | | | |remote| iSNSP +-----+ | + ||dev B |<----->| iSNS | | | | iSNS |<----->|dev D| | + |+------+ |server| | | |server| +-----+ | + |........ +--+---+ | WAN | +---+--+ | + |.dev C'. | | Link | | | + |........ | ============= | | + | | | | | | + | +--+---+ | | +---+--+ | + | | local|<--- <--- <--- <-|remote| | + | | LDAP | | LDAP: | | LDAP | | + | +------+ Xfer "dev C"| +------+ | + +-------------------------+ +-------------------------+ + Enterprise Enterprise + Network A Network B + + In the above diagram, two business partners wish to share storage + "dev C". Using LDAP, the record for "dev C" can be transferred from + Network B to Network A. Once accessible to the local iSNS server in + Network A, local devices A and B can now discover and connect to "dev + C". + + + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 16] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + +-------------------------+ +-------------------------+ + |+------+ iSNSP | | iSNSP +-----+ | + ||dev A |<----->+------+ | | +------+<----->|dev C| | + |+------+ | | | | | | +-----+ | + |+------+ iSNSP |local | | | |remote| iSNSP +-----+ | + ||dev B |<----->| iSNS | | | | iSNS |<----->|dev D| | + |+------+ |server| | | |server| +-----+ | + |........ +------+ | WAN | +---+--+ | + |.dev C'. ^ | Link | | | + |........ | ============= v | + | | | | |SNMP | + | | | | | | + | +--+----+ | | v | + | | SNMP |<--- <--- <--- <---- | + | | Mgmt | | SNMP: Xfer "dev C" | + | |Station| | | | + | +-------+ | | | + +-------------------------+ +-------------------------+ + Enterprise Enterprise + Network A Network B + + The above diagram illustrates a second example of how iSNS records + can be shared. This method uses an SNMP-based management station to + retrieve (GET) the desired record for "dev C" manually, and then to + store (SET) it on the local iSNS server directly. Once the record is + transferred to the local iSNS server in Network A, "dev C" becomes + visible and accessible (provided that firewall boundaries can be + negotiated) to other devices in Network A. + + Other methods, including proprietary protocols, can be used to + transfer device records between iSNS servers. Further discussion and + explanation of these methodologies is beyond the scope of this + document. + +2.8. Backup iSNS Servers + + This section offers a broad framework for implementation and + deployment of iSNS backup servers. Server failover and recovery are + topics of continuing research, and adequate resolution of issues such + as split brain and primary server selection is dependent on the + specific implementation requirements and deployment needs. The + failover mechanisms discussed in this document focus on the + interaction between iSNS clients and iSNS servers. Specifically, + what is covered in this document includes the following: + + - iSNS client behavior and the iSNS protocol interaction between the + client and multiple iSNS servers, some of which are backup + servers. + + + +Tseng, et al. Standards Track [Page 17] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + - Required failover behaviors of the collection of iSNS servers that + includes active and backup servers. + + However, note that this document does not specify the complete + functional failover requirements of each iSNS server. In particular, + it does not specify the complete set of protocol interactions among + the iSNS servers that are required to achieve stable failover + operation in an interoperable manner. + + For the purposes of this discussion, the specified backup mechanisms + pertain to interaction among different logical iSNS servers. Note + that it is possible to create multiple physical iSNS servers to form + a single logical iSNS server cluster, and thus to distribute iSNS + transaction processing among multiple physical servers. However, a + more detailed discussion of the interactions between physical servers + within a logical iSNS server cluster is beyond the scope of this + document. + + Multiple logical iSNS servers can be used to provide redundancy in + the event that the active iSNS server fails or is removed from the + network. The methods described in Section 2.7 above can be used to + transfer name server records to backup iSNS servers. Each backup + server maintains a redundant copy of the name server database found + in the primary iSNS server, and can respond to iSNS protocol messages + in the same way as the active server. Each backup server SHOULD + monitor the health and status of the active iSNS server, including + checking to make sure its own database is synchronized with the + active server's database. How each backup server accomplishes this + is implementation-dependent, and may (or may not) include using the + iSNS protocol. If the iSNS protocol is used, then the backup server + MAY register itself in the active server's iSNS database as a Control + Node, allowing it to receive state-change notifications. + + Generally, the administrator or some automated election process is + responsible for initial and subsequent designation of the primary + server and each backup server. + + A maximum of one logical backup iSNS server SHALL exist at any + individual IP address, in order to avoid conflicts from multiple + servers listening on the same canonical iSNS TCP or UDP port number. + + The iSNS heartbeat can also be used to coordinate the designation and + selection of primary and backup iSNS servers. + + Each backup server MUST note its relative precedence in the active + server's list of backup servers. If its precedence is not already + known, each backup server MAY learn it from the iSNS heartbeat + message, by noting the position of its IP address in the ordered list + + + +Tseng, et al. Standards Track [Page 18] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + of backup server IP addresses. For example, if it is the first + backup listed in the heartbeat message, then its backup precedence is + 1. If it is the third backup server listed, then its backup + precedence is 3. + + If a backup server establishes that it has lost connectivity to the + active server and other backup servers of higher precedence, then it + SHOULD assume that it is the active server. The method of + determining whether connectivity has been lost is implementation- + specific. One possible approach is to assume that if the backup + server does not receive iSNS heartbeat messages for a period of time, + then connectivity to the active server has been lost. Alternatively, + the backup server may establish TCP connections to the active server + and other backup servers, with loss of connectivity determined + through non-response to periodic echo or polling messages (using + iSNSP, SNMP, or other protocols). + + When a backup server becomes the active server, it SHALL assume all + active server responsibilities, including (if used) transmission of + the iSNS heartbeat message. If transmitting the iSNS heartbeat, the + backup server replaces the active Server IP Address and TCP/UDP Port + entries with its own IP address and TCP/UDP Port, and begins + incrementing the counter field from the last known value from the + previously-active iSNS server. However, it MUST NOT change the + original ordered list of backup server IP Address and TCP/UDP Port + entries. If the primary backup server or other higher-precedence + backup server returns, then the existing active server is responsible + for ensuring that the new active server's database is up-to-date + before demoting itself to its original status as backup. + + Since the primary and backup iSNS servers maintain a coordinated + database, no re-registration by an iSNS Client is required when a + backup server takes the active server role. Likewise, no re- + registration by an iSNS Client is required when the previous primary + server returns to the active server role. + +2.9. Transport Protocols + + The iSNS Protocol is transport-neutral. Query and registration + messages are transported over TCP or UDP. iSNS heartbeat messages + are transported using IP multicast or broadcast. + +2.9.1. Use of TCP for iSNS Communication + + It MUST be possible to use TCP for iSNS communication. The iSNS + server MUST accept TCP connections for client registrations. To + receive Entity Status Inquiry (ESI) (see Section 5.6.5.13) monitoring + the use of TCP, the client registers the Portal ESI Interval and the + + + +Tseng, et al. Standards Track [Page 19] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + port number of the TCP port that will be used to receive ESI + messages. The iSNS server initiates the TCP connection used to + deliver the ESI message. This TCP connection does not need to be + continuously open. + + To receive SCN notifications using TCP, the client registers the + iSCSI or iFCP SCN Bitmap and the port number of the TCP port in the + Portal used to receive SCNs. The iSNS server initiates the TCP + connection used to deliver the SCN message. This TCP connection does + not need to be continuously open. + + It is possible for an iSNS client to use the same TCP connection for + SCN, ESI, and iSNS queries. Alternatively, separate connections may + be used. + +2.9.2. Use of UDP for iSNS Communication + + The iSNS server MAY accept UDP messages for client registrations. + The iSNS server MUST accept registrations from clients requesting + UDP-based ESI and SCN messages. + + To receive UDP-based ESI monitoring messages, the client registers + the port number of the UDP port in at least one Portal to be used to + receive and respond to ESI messages from the iSNS server. If a + Network Entity has multiple Portals with registered ESI UDP Ports, + then ESI messages SHALL be delivered to every Portal registered to + receive such messages. + + To receive UDP-based SCN notification messages, the client registers + the port number of the UDP port in at least one Portal to be used to + receive SCN messages from the iSNS server. If a Network Entity has + multiple Portals with registered SCN UDP Ports, then SCN messages + SHALL be delivered to each Portal registered to receive such + messages. + + When using UDP to transport iSNS messages, each UDP datagram MUST + contain exactly one iSNS PDU (see Section 5). + +2.9.3. iSNS Multicast and Broadcast Messages + + iSNS multicast messages are transported using IP multicast or + broadcast. The iSNS heartbeat is the only iSNS multicast or + broadcast message. This message is originated by the iSNS server and + sent to all iSNS clients that are listening on the IP multicast + address allocated for the iSNS heartbeat. + + + + + + +Tseng, et al. Standards Track [Page 20] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +2.10. Simple Network Management Protocol (SNMP) Requirements + + The iSNS Server may be managed via the iSNS MIB [iSNSMIB] using an + SNMP management framework [RFC3411]. For a detailed overview of the + documents that describe the current Internet-Standard Management + Framework, please refer to Section 7 of RFC 3410 [RFC3410]. The iSNS + MIB provides the ability to configure and monitor an iSNS server + without using the iSNS protocol directly. SNMP management frameworks + have several requirements for object indexing in order for objects to + be accessed or added. + + SNMP uses an Object Identifier (OID) for object identification. The + size of each OID is restricted to a maximum of 128 sub-identifiers. + Both the iSCSI and iFCP protocol contain identifiers, such as the + iSCSI Name, that are greater the 128 characters in length. Using + such identifiers as an index would result in more than 128 sub- + identifiers per OID. In order to support objects that have key + identifiers whose maximum length is longer than the maximum SNMP- + supported length, the iSNS server provides secondary non-zero integer + index identifiers. These indexes SHALL be persistent for as long as + the server is active. Furthermore, index values for recently + deregistered objects SHOULD NOT be reused in the short term. Object + attributes, including indexes, are described in detail in Section 6. + + For SNMP based management applications to create a new entry in a + table of objects, a valid OID must be available to specify the table + row. The iSNS server supports this by providing, for each type of + object that can be added via SNMP, an object attribute that returns + the next available non-zero integer index. This allows an SNMP + client to request an OID to be used for registering a new object in + the server. Object attributes, including next available index + attributes, are described in detail in Section 6. + +3. iSNS Object Model + + iSNS provides the framework for the registration, discovery, and + management of iSCSI devices and Fibre Channel-based devices (using + iFCP). This architecture framework provides elements needed to + describe various storage device objects and attributes that may exist + on an IP storage network. Objects defined in this architecture + framework include Network Entity, Portal, Storage Node, FC Device, + Discovery Domain, and Discovery Domain Set. Each of these objects is + described in greater detail in the following sections. + + + + + + + + +Tseng, et al. Standards Track [Page 21] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +3.1. Network Entity Object + + The Network Entity object is a container of Storage Node objects and + Portal objects. It represents the infrastructure supporting access + to a unique set of one or more Storage Nodes. The Entity Identifier + attribute uniquely distinguishes a Network Entity, and is the key + used to register a Network Entity object in an iSNS server. All + Storage Nodes and Portals contained within a single Network Entity + object operate as a cohesive unit. + + Note that it is possible for a single physical device or gateway to + be represented by more than one logical Network Entity in the iSNS + database. For example, one of the Storage Nodes on a physical device + may be accessible from only a subset of the network interfaces (i.e., + Portals) available on that device. In this case, a logical network + entity (i.e., a "shadow entity") is created and used to contain the + Portals and Storage Nodes that can operate cooperatively. No object + (Portals, Storage Nodes, etc.) can be contained in more than one + logical Network Entity. + + Similarly, it is possible for a logical Network Entity to be + supported by more than one physical device or gateway. For example, + multiple FC-iSCSI gateways may be used to bridge FC devices in a + single Fibre Channel network. Collectively, the multiple gateways + can be used to support a single logical Network Entity that is used + to contain all the devices in that Fibre Channel network. + +3.2. Portal Object + + The Portal object is an interface through which access to Storage + Nodes within the Network Entity can be obtained. The IP address and + TCP/UDP Port number attributes uniquely distinguish a Portal object, + and combined are the key used to register a Portal object in an iSNS + server. A Portal is contained in one and only one Network Entity, + and may be contained in one or more DDs (see Section 3.6). + +3.3. Storage Node Object + + The Storage Node object is the logical endpoint of an iSCSI or iFCP + session. In iFCP, the session endpoint is represented by the World + Wide Port Name (WWPN). In iSCSI, the session endpoint is represented + by the iSCSI Name of the device. For iSCSI, the iSCSI Name attribute + uniquely distinguishes a Storage Node, and is the key used to + register a Storage Node object in an iSNS Server. For iFCP, the FC + Port Name (WWPN) attribute uniquely distinguishes a Storage Node, and + is the key used to register a Storage Node object in the iSNS Server. + Storage Node is contained in only one Network Entity object and may + be contained in one or more DDs (see Section 3.6). + + + +Tseng, et al. Standards Track [Page 22] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +3.4. Portal Group Object + + The Portal Group (PG) object represents an association between a + Portal and an iSCSI Node. Each Portal and iSCSI Storage Node + registered in an Entity can be associated using a Portal Group (PG) + object. The PG Tag (PGT), if non-NULL, indicates that the associated + Portal provides access to the associated iSCSI Storage Node in the + Entity. All Portals that have the same PGT value for a specific + iSCSI Storage Node allow coordinated access to that node. + + A PG object MAY be registered when a Portal or iSCSI Storage Node is + registered. Each Portal to iSCSI Node association is represented by + one and only one PG object. In order for a Portal to provide access + to an iSCSI Node, the PGT of the PG object MUST be non-NULL. If the + PGT value registered for a specified Portal and iSCSI Node is NULL, + or if no PGT value is registered, then the Portal does not provide + access to that iSCSI Node in the Entity. + + The PGT value indicates whether access to an iSCSI Node can be + coordinated across multiple Portals. All Portals that have the same + PGT value for a specific iSCSI Node can provide coordinated access to + that iSCSI Node. According to the iSCSI Specification, coordinated + access to an iSCSI node indicates the capability of coordinating an + iSCSI session with connections that span these Portals [iSCSI]. + + The PG object is uniquely distinguished by the iSCSI Name, Portal IP + Address, and Portal TCP Port values of the associated Storage Node + and Portal objects. These are represented in the iSNS Server by the + PG iSCSI Name, PG Portal IP Address, and PG Portal TCP/UDP Port + attributes, respectively. The PG object is also uniquely + distinguished in the iSNS Server by the PG Index value. + + A new PG object can only be registered by referencing its associated + iSCSI Storage Node or Portal object. A pre-existing PG object can be + modified or queried by using its Portal Group Index as message key, + or by referencing its associated iSCSI Storage Node or Portal object. + A 0-length Tag, Length, Value TLV is used to register a PGT NULL + value. + + The PG object is deregistered if and only if its associated iSCSI + Node and Portal objects are both removed. + + + + + + + + + + +Tseng, et al. Standards Track [Page 23] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +3.5. Device Object + + The FC Device represents the Fibre Channel Node. This object + contains information that may be useful in the management of the + Fibre Channel device. The FC Node Name (WWNN) attribute uniquely + distinguishes an FC Device, and is the key used to register an FC + Device object in the iSNS Server. + + The FC Device is contained in one or more Storage Node objects. + +3.6. Discovery Domain Object + + Discovery Domains (DD) are a security and management mechanism used + to administer access and connectivity to storage devices. For query + and registration purposes, they are considered containers for Storage + Node and Portal objects. A query by an iSNS client that is not from + a Control Node only returns information about objects with which it + shares at least one active DD. The only exception to this rule is + with Portals; if Storage Nodes of a Network Entity are registered in + the DD without Portals, then all Portals of that Network Entity are + implicit members of that DD. The Discovery Domain ID (DD_ID) + attribute uniquely distinguishes a Discovery Domain object, and is + the key used to register a Discovery Domain object in the iSNS + Server. + + A DD is considered active if it is a member of at least one active DD + Set. DDs that are not members of at least one enabled DDS are + considered disabled. A Storage Node can be a member of one or more + DDs. An enabled DD establishes connectivity among the Storage Nodes + in that DD. + +3.7. Discovery Domain Set Object + + The Discovery Domain Set (DDS) is a container object for Discovery + Domains (DDs). DDSs may contain one or more DDs. Similarly, each DD + can be a member of one or more DDSs. DDSs are a mechanism to store + coordinated sets of DD mappings in the iSNS server. Active DDs are + members of at least one active DD Set. Multiple DDSs may be + considered active at the same time. The Discovery Domain Set ID + (DDS_ID) attribute uniquely distinguishes a Discovery Domain Set + object, and is the key used to register a Discovery Domain Set object + in the iSNS Server. + +3.8. Database Model + + As presented to the iSNS client, each object of a specific type in + the iSNS database MUST have an implicit internal linear ordering + based on the key(s) for that object type. This ordering provides the + + + +Tseng, et al. Standards Track [Page 24] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + ability to respond to DevGetNext queries (see Section 5.6.5.3). The + ordering of objects in the iSNS database SHOULD NOT be changed with + respect to that implied ordering, as a consequence of object + insertions and deletions. That is, the relative order of surviving + object entries in the iSNS database SHOULD be preserved so that the + DevGetNext message encounters generally reasonable behavior. + + The following diagram shows the various objects described above and + their relationship to each other. + + +--------------+ +-----------+ + | NETWORK |1 *| | + | ENTITY |----| PORTAL | + | | | | + +--------------+ +-----------+ + |1 |1 |* + | | | + | |* | + | +----------+ | + | | PORTAL | | + | | GROUP | | + | +----------+ | + | |* | + | | | + |* |1 |* + +-----------+ +--------------+ +-----------+ +-----------+ + | FC |1 *| STORAGE |* *| DISCOVERY |* *| DISCOVERY | + | DEVICE |----| NODE |----| DOMAIN |----| DOMAIN | + | | | | | | | SET | + +-----------+ +--------------+ +-----------+ +-----------+ + + * represents 0 to many possible relationships + +4. iSNS Implementation Requirements + + This section details specific requirements for support of each of + these IP storage protocols. Implementation requirements for security + are described in Section 7. + +4.1. iSCSI Requirements + + Use of iSNS in support of iSCSI is OPTIONAL. iSCSI devices MAY be + manually configured with the iSCSI Name and IP address of peer + devices, without the aid or intervention of iSNS. iSCSI devices may + also use SLP [RFC2608] to discover peer iSCSI devices. However, iSNS + is useful for scaling a storage network to a larger number of iSCSI + devices. + + + + +Tseng, et al. Standards Track [Page 25] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +4.1.1. Required Attributes for Support of iSCSI + + The following attributes are available to support iSCSI. Attributes + indicated in the REQUIRED for Server column MUST be implemented by an + iSNS server used to support iSCSI. Attributes indicated in the + REQUIRED for Client column MUST be implemented by an iSCSI device + that elects to use the iSNS. Attributes indicated in the K (Key) + column uniquely identify the object type in the iSNS Server. A more + detailed description of each attribute is found in Section 6. + + REQUIRED for: + Object Attribute K Server Client + ------ --------- - ------ ------ + NETWORK ENTITY Entity Identifier * * * + Entity Protocol * * + Management IP Address * + Timestamp * + Protocol Version Range * + Registration Period * + Entity Index * + Entity IKE Phase-1 Proposal + Entity Certificate + + PORTAL IP Address * * * + TCP/UDP Port * * * + Portal Symbolic Name * + ESI Interval * + ESI Port * + Portal Index * + SCN Port * + Portal Security Bitmap * + Portal IKE Phase-1 Proposal + Portal IKE Phase-2 Proposal + Portal Certificate + + PORTAL GROUP PG iSCSI Name * * * + PG IP Address * * * + PG TCP/UDP Port * * * + PG Tag * * + PG Index * + + + + + + + + + + + +Tseng, et al. Standards Track [Page 26] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + STORAGE NODE iSCSI Name * * * + iSCSI Node Type * * + Alias * + iSCSI SCN Bitmap * + iSCSI Node Index * + WWNN Token + iSCSI AuthMethod + iSCSI Node Certificate + + DISCOVERY DOMAIN DD ID * * * + DD Symbolic Name * + DD Member iSCSI Node Index * + DD Member iSCSI Name * + DD Member Portal Index * + DD Member Portal IP Addr * + DD Member Portal TCP/UDP * + DD Features * + + DISCOVERY DOMAIN DDS Identifier * * + SET DDS Symbolic Name * + DDS Status * + + All iSCSI user-specified and vendor-specified attributes are OPTIONAL + to implement and use. + + + + + + + + + + + + + + + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 27] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +4.1.2. Examples: iSCSI Object Model Diagrams + + The following diagram models how a simple iSCSI-based initiator and + target is represented using database objects stored in the iSNS + server. In this implementation, each target and initiator is + attached to a single Portal. + + +----------------------------------------------------------------+ + | IP Network | + +------------+--------------------------------------+------------+ + | | + | | + +-----+------+------+-----+ +-----+------+------+-----+ + | | PORTAL | | | | PORTAL | | + | | -IP Addr 1 | | | | -IP Addr 2 | | + | | -TCP Port 1 | | | | -TCP Port 2 | | + | +-----+ +-----+ | | +-----+ +-----+ | + | | | | | | | | + | +-----+ +-----+ | | +-----+ +-----+ | + | | PORTAL GROUP| | | | PORTAL GROUP| | + | | -Prtl Tag 1 | | | | -Prtl Tag 2 | | + | +-----+ +-----+ | | +-----+ +-----+ | + | | | | | | | | + | +--------+ +--------+ | | +-------+ +--------+ | + | | | | | | | | + | | STORAGE NODE | | | | STORAGE NODE | | + | | -iSCSI Name | | | | -iSCSI Name | | + | | -Alias: "server1"| | | | -Alias: "disk1"| | + | | -Type: initiator | | | | -Type: target | | + | | | | | | | | + | +-------------------+ | | +------------------+ | + | | | | + | NETWORK ENTITY | | NETWORK ENTITY | + | -Entity ID (FQDN): | | -Entity ID (FQDN): | + | "strg1.example.com" | | "strg2.example.net" | + | -Protocol: iSCSI | | -Protocol: iSCSI | + | | | | + +-------------------------+ +-------------------------+ + + The object model can be expanded to describe more complex devices, + such as an iSCSI device with more than one storage controller, in + which each controller is accessible through any of multiple Portal + interfaces, possibly using multiple Portal Groups. The storage + controllers on this device can be accessed through alternate Portal + interfaces if any original interface should fail. The following + diagram describes such a device: + + + + + +Tseng, et al. Standards Track [Page 28] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + +---------------------------------------------------------------+ + | IP Network | + +-------------------+-----------------------+-------------------+ + | | + | | + +------------+------+------+---------+------+------+------------+ + | | PORTAL 1 | | PORTAL 2 | | + | | -IP Addr 1 | | -IP Addr 2 | | + | | -TCP Port 1 | | -TCP Port 2 | | + | +-----+ +-----+ +-----+ +-----+ | + | | | | | | + | +---------------+ +---------------------+ +---------------+ | + | +-------+ +----------------+ +-------------------+ +------+ | + | | | | | | | | + | +-------+ +-------+ +------+ +--------+ +--------+ +------+ | + | | | | | | | | + | | STORAGE NODE 1 | | STORAGE NODE 2 | | STORAGE NODE 3 | | + | | -iSCSI Name 1 | | -iSCSI Name 2 | | -iSCSI Name 3 | | + | | -Alias: "disk1"| | -Alias: "disk2"| | -Alias: "disk3"| | + | | -Type: target | | -Type: target | | -Type: target | | + | | | | | | | | + | +-----------------+ +-----------------+ +-----------------+ | + | | + | NETWORK ENTITY | + | -Entity ID (FQDN): "dev1.example.com" | + | -Protocol: iSCSI | + | | + | Portal Group Object Table | + | Storage-Node Portal Portal-Group-Tag | + | 1 1 10 | + | 1 2 NULL (no access permitted) | + | 2 1 20 | + | 2 2 20 | + | 3 1 30 | + | 3 2 10 | + | | + +---------------------------------------------------------------+ + + Storage Node 1 is accessible via Portal 1 with a PGT of 10. It does + not have a Portal Group Tag (PGT) assigned for Portal 2, so Storage + Node 1 cannot be accessed via Portal 2. + + Storage Node 2 can be accessed via both Portal 1 and Portal 2. Since + Storage Node 2 has the same PGT value assigned to both Portal 1 and + Portal 2, in this case 20, coordinated access via the Portals is + available [iSCSI]. + + + + + +Tseng, et al. Standards Track [Page 29] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Storage Node 3 can be accessed via Portal 1 or Portal 2. However, + since Storage Node 3 has different PGT values assigned to each + Portal, in this case 10 and 30, access is not coordinated [iSCSI]. + Because PGTs are assigned within the context of a Storage Node, the + PGT value of 10 used for Storage Node 1 and Storage Node 3 are not + interrelated. + +4.1.3. Required Commands and Response Messages for Support of iSCSI + + The following iSNSP messages and responses are available in support + of iSCSI. Messages indicated in the REQUIRED for Server column MUST + be implemented in iSNS servers used for iSCSI devices. Messages + indicated in the REQUIRED for Client column MUST be implemented in + iSCSI devices that elect to use the iSNS server. + + REQUIRED for: + Message Description Abbreviation Func_ID Server Client + ------------------- ------------ ------- ------ ------ + RESERVED 0x0000 + Device Attr Reg Request DevAttrReg 0x0001 * * + Dev Attr Query Request DevAttrQry 0x0002 * * + Dev Get Next Request DevGetNext 0x0003 * + Deregister Dev Request DevDereg 0x0004 * * + SCN Register Request SCNReg 0x0005 * + SCN Deregister Request SCNDereg 0x0006 * + SCN Event SCNEvent 0x0007 * + State Change Notification SCN 0x0008 * + DD Register DDReg 0x0009 * * + DD Deregister DDDereg 0x000A * * + DDS Register DDSReg 0x000B * * + DDS Deregister DDSDereg 0x000C * * + Entity Status Inquiry ESI 0x000D * + Name Service Heartbeat Heartbeat 0x000E + RESERVED 0x000F-0x00FF + Vendor Specific 0x0100-0x01FF + RESERVED 0x0200-0x7FFF + + The following are iSNSP response messages used in support of iSCSI: + + REQUIRED for: + Response Message Desc Abbreviation Func_ID Server Client + --------------------- ------------ ------- ------ ------ + RESERVED 0x8000 + Device Attr Register Rsp DevAttrRegRsp 0x8001 * * + Device Attr Query Rsp DevAttrQryRsp 0x8002 * * + Device Get Next Rsp DevGetNextRsp 0x8003 * + Device Dereg Rsp DevDeregRsp 0x8004 * * + SCN Register Rsp SCNRegRsp 0x8005 * + + + +Tseng, et al. Standards Track [Page 30] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + SCN Deregister Rsp SCNDeregRsp 0x8006 * + SCN Event Rsp SCNEventRsp 0x8007 * + SCN Response SCNRsp 0x8008 * + DD Register Rsp DDRegRsp 0x8009 * * + DD Deregister Rsp DDDeregRsp 0x800A * * + DDS Register Rsp DDSRegRsp 0x800B * * + DDS Deregister Rsp DDSDeregRsp 0x800C * * + Entity Stat Inquiry Rsp ESIRsp 0x800D * + RESERVED 0x800E-0x80FF + Vendor Specific 0x8100-0x81FF + RESERVED 0x8200-0xFFFF + +4.2. iFCP Requirements + + In iFCP, use of iSNS is REQUIRED. No alternatives exist for support + of iFCP Naming & Discovery functions. + +4.2.1. Required Attributes for Support of iFCP + + The following table displays attributes that are used by iSNS to + support iFCP. Attributes indicated in the REQUIRED for Server column + MUST be implemented by the iSNS server that supports iFCP. + Attributes indicated in the REQUIRED for Client column MUST be + supported by iFCP gateways. Attributes indicated in the K (Key) + column uniquely identify the object type in the iSNS Server. A more + detailed description of each attribute is found in Section 6. + + REQUIRED for: + Object Attribute K Server Client + ------ --------- - ------ ------ + NETWORK ENTITY Entity Identifier * * * + Entity Protocol * * + Management IP Address * + Timestamp * + Protocol Version Range * + Registration period + Entity Index + Entity IKE Phase-1 Proposal + Entity Certificate + + PORTAL IP Address * * * + TCP/UDP Port * * * + Symbolic Name * + ESI Interval * + ESI Port * + SCN Port * + Portal IKE Phase-1 Proposal + Portal IKE Phase-2 Proposal + + + +Tseng, et al. Standards Track [Page 31] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Portal Certificate + Security Bitmap * + + STORAGE NODE FC Port Name (WWPN) * * * + (FC Port) Port_ID * * + FC Port Type * * + Port Symbolic Name * + Fabric Port Name (FWWN) * + Hard Address * + Port IP Address * + Class of Service * + FC FC-4 Types * + FC FC-4 Descriptors * + FC FC-4 Features * + SCN Bitmap * + iFCP Port Role * + Permanent Port Name * + + FC DEVICE FC Node Name (WWNN) * * * + (FC Node) Node Symbolic Name * + Node IP Address * + Node IPA * + Proxy iSCSI Name + + DISCOVERY DOMAIN DD ID * * * + DD Symbolic Name * + DD Member FC Port Name * + DD Member Portal Index * + DD Member Portal IP Addr * + DD Member Portal TCP/UDP * + + DISCOVERY DOMAIN DDS ID * * + SET DDS Symbolic Name * + DDS Status * + + OTHER Switch Name + Preferred_ID + Assigned_ID + Virtual_Fabric_ID + + All iFCP user-specified and vendor-specified attributes are OPTIONAL + to implement and use. + +4.2.2. Example: iFCP Object Model Diagram + + The iFCP protocol allows native Fibre Channel devices or Fibre + Channel fabrics connected to an iFCP gateway to be directly + internetworked using IP. + + + +Tseng, et al. Standards Track [Page 32] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + When supporting iFCP, the iSNS server stores Fibre Channel device + attributes, iFCP gateway attributes, and Fibre Channel fabric switch + attributes that might also be stored in an FC name server. + + The following diagram shows a representation of a gateway supporting + multiple Fibre Channel devices behind it. The two Portal objects + represent IP interfaces on the iFCP gateway that can be used to + access any of the three Storage Node objects behind it. Note that + the FC Device object is not contained in the Network Entity object. + However, each FC Device has a relationship to one or more Storage + Node objects. + + +--------------------------------------------------------+ + | IP Network | + +--------+-----------------+-----------------------------+ + | | + +-+------+------+---+------+------+----------------------+ + | | PORTAL | | PORTAL | NETWORK ENTITY | + | | -IP Addr 1 | | -IP Addr 2 | -Entity ID (FQDN): | + | | -TCP Port 1 | | -TCP Port 2 | "gtwy1.example.com" | + | +-----+ +-----+ +-----+ +-----+ -Protocol: iFCP | + | | | | | | + | +-----+ +---------------+ +----------------------+ | + | +-----+ +---------------+ +-------------+ +------+ | + | | | | | | | | + | +-----+ +-----+ +----+ +------+ +----+ +------+ | + | |STORAGE NODE | |STORAGE NODE | |STORAGE NODE | | + | | -WWPN 1 | | -WWPN 2 | | -WWPN 3 | | + | | -Port ID 1 | | -Port ID 2 | | -Port ID 3 | | + | | -FWWN 1 | | -FWWN 2 | | -FWWN 3 | | + | | -FC COS | | -FC COS | | -FC COS | | + | +------+------+ +-------+-----+ +----+--------+ | + +--------|-------------------|------------|--------------+ + | | | + +------+------+ +---+------------+---+ + | FC DEVICE | | FC DEVICE | + | -WWNN 1 | | -WWNN 2 | + | | | | + +-------------+ +--------------------+ + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 33] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +4.2.3. Required Commands and Response Messages for Support of iFCP + + The iSNSP messages and responses displayed in the following tables + are available to support iFCP gateways. Messages indicated in the + REQUIRED TO IMPLEMENT column MUST be supported by the iSNS server + used by iFCP gateways. Messages indicated in the REQUIRED TO USE + column MUST be supported by the iFCP gateways themselves. + + REQUIRED for: + Message Description Abbreviation Func ID Server Client + ------------------- ------------ ------- ------ ------ + RESERVED 0x0000 + Device Attr Reg Request DevAttrReg 0x0001 * * + Device Attr Query Request DevAttrQry 0x0002 * * + Device Get Next Request DevGetNext 0x0003 * + Device Dereg Request DevDereg 0x0004 * * + SCN Register Request SCNReg 0x0005 * + SCN Deregister Request SCNDereg 0x0006 * + SCN Event SCNEvent 0x0007 * + State Change Notification SCN 0x0008 * + DD Register DDReg 0x0009 * * + DD Deregister DDDereg 0x000A * * + DDS Register DDSReg 0x000B * * + DDS Deregister DDSDereg 0x000C * * + Entity Status Inquiry ESI 0x000D * + Name Service Heartbeat Heartbeat 0x000E * + Reserved Reserved 0x000F-0x0010 + Request FC_DOMAIN_ID RqstDomId 0x0011 + Release FC_DOMAIN_ID RlseDomId 0x0012 + Get FC_DOMAIN_IDs GetDomId 0x0013 + RESERVED 0x0014-0x00FF + Vendor Specific 0x0100-0x01FF + RESERVED 0x0200-0x7FFF + + The following are iSNSP response messages in support of iFCP: + + REQUIRED for: + Response Message Desc Abbreviation Func_ID Server Client + --------------------- ------------ ------- ------ ------ + RESERVED 0x8000 + Device Attr Reg Rsp DevAttrRegRsp 0x8001 * * + Device Attr Query Rsp DevAttrQryRsp 0x8002 * * + Device Get Next Rsp DevGetNextRsp 0x8003 * + Device Deregister Rsp DevDeregRsp 0x8004 * * + SCN Register Rsp SCNRegRsp 0x8005 * + SCN Deregister Rsp SCNDeregRsp 0x8006 * + SCN Event Rsp SCNEventRsp 0x8007 * + SCN Rsp SCNRsp 0x8008 * + + + +Tseng, et al. Standards Track [Page 34] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + DD Register Rsp DDRegRsp 0x8009 * * + DD Deregister Rsp DDDeregRsp 0x800A * * + DDS Register Rsp DDSRegRsp 0x800B * * + DDS Deregister Rsp DDSDeregRsp 0x800C * * + Entity Status Inquiry Rsp ESIRsp 0x800D * + NOT USED 0x800E + RESERVED 0x800F-0x8010 + Request FC_DOMAIN_ID Rsp RqstDomIdRsp 0x8011 + Release FC_DOMAIN_ID Rsp RlseDomIdRsp 0x8012 + Get FC_DOMAIN_IDs GetDomIdRsp 0x0013 + RESERVED 0x8014-0x80FF + Vendor Specific 0x8100-0x81FF + RESERVED 0x8200-0xFFFF + +5. iSNSP Message Format + + The iSNSP message format is similar to the format of other common + protocols such as DHCP, DNS and BOOTP. An iSNSP message may be sent + in one or more iSNS Protocol Data Units (PDU). Each PDU is 4-byte + aligned. The following describes the format of the iSNSP PDU: + + Byte MSb LSb + Offset 0 15 16 31 + +---------------------+----------------------+ + 0 | iSNSP VERSION | FUNCTION ID | 4 Bytes + +---------------------+----------------------+ + 4 | PDU LENGTH | FLAGS | 4 Bytes + +---------------------+----------------------+ + 8 | TRANSACTION ID | SEQUENCE ID | 4 Bytes + +---------------------+----------------------+ + 12 | | + | PDU PAYLOAD | N Bytes + | ... | + +--------------------------------------------+ + 12+N | AUTHENTICATION BLOCK (Multicast/Broadcast) | L Bytes + +--------------------------------------------+ + Total Length = 12 + N + L + +5.1. iSNSP PDU Header + + The iSNSP PDU header contains the iSNSP VERSION, FUNCTION ID, PDU + LENGTH, FLAGS, TRANSACTION ID, and SEQUENCE ID fields as defined + below. + + + + + + + + +Tseng, et al. Standards Track [Page 35] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.1.1. iSNSP Version + + The iSNSP version described in this document is 0x0001. All other + values are RESERVED. The iSNS server MAY reject messages for iSNSP + version numbers that it does not support. + +5.1.2. iSNSP Function ID + + The FUNCTION ID defines the type of iSNS message and the operation to + be executed. FUNCTION_ID values with the leading bit cleared + indicate query, registration, and notification messages, whereas + FUNCTION_ID values with the leading bit set indicate response + messages. + + See Section 4 under the appropriate protocol (i.e., iSCSI or iFCP) + for a mapping of the FUNCTION_ID value to the iSNSP Command or + Response message. All PDUs comprising an iSNSP message must have the + same FUNCTION_ID value. + +5.1.3. iSNSP PDU Length + + The iSNS PDU Length specifies the length of the PDU PAYLOAD field in + bytes. The PDU Payload contains TLV attributes for the operation. + + Additionally, response messages contain a success/failure code. The + PDU Length MUST be 4-byte aligned. + +5.1.4. iSNSP Flags + + The FLAGS field indicates additional information about the message + and the type of Network Entity that generated the message. The + following table displays the valid flags: + + Bit Position Enabled (1) means: + ------------ ----------------- + 16 Sender is the iSNS client + 17 Sender is the iSNS server + 18 Authentication block is present + 19 Replace flag (for DevAttrReg) + 20 Last PDU of the iSNS message + 21 First PDU of the iSNS message + 22-31 RESERVED + +5.1.5. iSNSP Transaction ID + + The TRANSACTION ID MUST be set to a unique value for each + concurrently outstanding request message. Replies MUST use the same + TRANSACTION ID value as the associated iSNS request message. If a + + + +Tseng, et al. Standards Track [Page 36] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + message is retransmitted, the original TRANSACTION ID value MUST be + used. All PDUs comprising an iSNSP message must have the same + TRANSACTION ID value. + +5.1.6. iSNSP Sequence ID + + The SEQUENCE ID has a unique value for each PDU within a single + transaction. The SEQUENCE_ID value of the first PDU transmitted in a + given iSNS message MUST be zero (0), and each SEQUENCE_ID value in + each PDU MUST be numbered sequentially in the order in which the PDUs + are transmitted. Note that the two-byte SEQUENCE ID allows for up to + 65536 PDUs per iSNS message. + +5.2. iSNSP Message Segmentation and Reassembly + + iSNS messages may be carried in one or more iSNS PDUs. If only one + iSNS PDU is used to carry the iSNS message, then bit 21 (First PDU) + and bit 20 in the FLAGS field (Last PDU) SHALL both be set. If + multiple PDUs are used to carry the iSNS message, then bit 21 SHALL + be set in the first PDU of the message, and bit 20 SHALL be set in + the last PDU. + + All PDUs comprising the same iSNSP message SHALL have the same + FUNCTION_ID and TRANSACTION_ID values. Each PDU comprising an iSNSP + message SHALL have a unique SEQUENCE_ID value. + +5.3. iSNSP PDU Payload + + The iSNSP PDU PAYLOAD is of variable length and contains attributes + used for registration and query operations. The attribute data items + use a format similar to that of other protocols, such as DHCP + [RFC2131] options. Each iSNS attribute is specified in the PDU + Payload using Tag-Length-Value (TLV) data format, as shown below: + + Byte MSb LSb + Offset 0 31 + +--------------------------------------------+ + 0 | Attribute Tag | 4 Bytes + +--------------------------------------------+ + 4 | Attribute Length (N) | 4 Bytes + +--------------------------------------------+ + 8 | | + | Attribute Value | N Bytes + | | + +--------------------------------------------+ + Total Length = 8 + N + + + + + +Tseng, et al. Standards Track [Page 37] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Attribute Tag: a 4-byte field that identifies the attribute as + defined in Section 6.1. This field contains the + tag value from the indicated table. + + Attribute Length: a 4-byte field that indicates the length, in bytes, + of the value field to follow in the TLV. For + variable-length attributes, the value field MUST + contain padding bytes, if necessary, in order to + achieve 4-byte alignment. A "zero-length TLV" + contains only the attribute tag and length fields. + + Attribute Value: a variable-length field containing the attribute + value and padding bytes (if necessary). + + The above format is used to identify each attribute in the PDU + Payload. Note that TLV boundaries need not be aligned with PDU + boundaries; PDUs may carry one or more TLVs, or any fraction thereof. + The Response Status Code, contained in response message PDU Payloads + and described below, is not in TLV format. PDU Payloads for messages + that do not contain iSNS attributes, such as the Name Service + Heartbeat, do not use the TLV format. + +5.3.1. Attribute Value 4-Byte Alignment + + All attribute values are aligned to 4-byte boundaries. For variable + length attributes, if necessary, the TLV length MUST be increased to + the next 4-byte boundary through padding with bytes containing zero + (0). If an attribute value is padded, a combination of the tag and + attribute value itself is used to determine the actual value length + and number of pad bytes. There is no explicit count of the number of + pad bytes provided in the TLV. + + + + + + + + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 38] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.4. iSNSP Response Status Codes + + All iSNSP response messages contain a 4-byte Status Code field as the + first field in the iSNSP PDU PAYLOAD. If the original iSNSP request + message was processed normally by the iSNS server, or by the iSNS + client for ESI and SCN messages, then this field SHALL contain a + status code of 0 (Successful). A non-zero status code indicates + rejection of the entire iSNS client request message. + + Status Code Status Description + ----------- ----------------- + 0 Successful + 1 Unknown Error + 2 Message Format Error + 3 Invalid Registration + 4 RESERVED + 5 Invalid Query + 6 Source Unknown + 7 Source Absent + 8 Source Unauthorized + 9 No Such Entry + 10 Version Not Supported + 11 Internal Error + 12 Busy + 13 Option Not Understood + 14 Invalid Update + 15 Message (FUNCTION_ID) Not Supported + 16 SCN Event Rejected + 17 SCN Registration Rejected + 18 Attribute Not Implemented + 19 FC_DOMAIN_ID Not Available + 20 FC_DOMAIN_ID Not Allocated + 21 ESI Not Available + 22 Invalid Deregistration + 23 Registration Feature Not Supported + 24 and above RESERVED + +5.5. Authentication for iSNS Multicast and Broadcast Messages + + For iSNS multicast and broadcast messages (see Section 2.9.3), the + iSNSP provides authentication capability. The following section + details the iSNS Authentication Block, which is identical in format + to the SLP authentication block [RFC2608]. iSNS unicast messages + SHOULD NOT include the authentication block, but rather should rely + upon IPSec security mechanisms. + + + + + + +Tseng, et al. Standards Track [Page 39] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + If a message contains an authentication block, then the + "Authentication block present" bit in the iSNSP PDU header FLAGS + field SHALL be enabled. + + If a PKI is available with an [X.509] Certificate Authority (CA), + then public key authentication of the iSNS server is possible. The + authentication block leverages the DSA with SHA-1 algorithm, which + can easily integrate into a public key infrastructure. + + The authentication block contains a digital signature for the + multicast message. The digital signature is calculated on a per-PDU + basis. The authentication block contains the following information: + + 1. A time stamp, to prevent replay attacks. + 2. A structured authenticator containing a signature calculated over + the time stamp and the message being secured. + 3. An indicator of the cryptographic algorithm that was used to + calculate the signature. + 4. An indicator of the keying material and algorithm parameters, + used to calculate the signature. + + The authentication block is described in the following figure: + + Byte MSb LSb + Offset 0 31 + +----------------------------------+ + 0 | BLOCK STRUCTURE DESCRIPTOR | 4 Bytes + +----------------------------------+ + 4 | AUTHENTICATION BLOCK LENGTH | 4 Bytes + +----------------------------------+ + 8 | TIMESTAMP | 8 Bytes + +----------------------------------+ + 16 | SPI STRING LENGTH | 4 Bytes + +----------------------------------+ + 20 | SPI STRING | N Bytes + +----------------------------------+ + 20 + N | STRUCTURED AUTHENTICATOR | M Bytes + +----------------------------------+ + Total Length = 20 + N + M + + BLOCK STRUCTURE DESCRIPTOR (BSD): Defines the structure and algorithm + to use for the STRUCTURED AUTHENTICATOR. BSD values from + 0x00000000 to 0x00007FFF are assigned by IANA, while + values 0x00008000 to 0x00008FFF are for private use. + + + + + + + +Tseng, et al. Standards Track [Page 40] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + AUTHENTICATION BLOCK LENGTH: Defines the length of the authentication + block, beginning with the BSD field and running through + the last byte of the STRUCTURED AUTHENTICATOR. + + TIMESTAMP: This is an 8-byte unsigned, fixed-point integer giving the + number of seconds since 00:00:00 GMT on January 1, 1970. + + SPI STRING LENGTH: The length of the SPI STRING field. + + SPI STRING (Security Parameters Index): Index to the key and + algorithm used by the message recipient to decode the + STRUCTURED AUTHENTICATOR field. + + STRUCTURED AUTHENTICATOR: Contains the digital signature. For the + default BSD value of 0x0002, this field SHALL contain the + binary ASN.1 encoding of output values from the DSA with + SHA-1 signature calculation as specified in Section 2.2.2 + of [RFC3279]. + +5.6. Registration and Query Messages + + The iSNSP registration and query message PDU Payloads contain a list + of attributes, and have the following format: + + +----------------------------------------+ + | Source Attribute (Requests Only) | + +----------------------------------------+ + | Message Key Attribute[1] (if present) | + +----------------------------------------+ + | Message Key Attribute[2] (if present) | + +----------------------------------------+ + | . . . | + +----------------------------------------+ + | - Delimiter Attribute - | + +----------------------------------------+ + | Operating Attribute[1] (if present) | + +----------------------------------------+ + | Operating Attribute[2] (if present) | + +----------------------------------------+ + | Operating Attribute[3] (if present) | + +----------------------------------------+ + | . . . | + +----------------------------------------+ + + Each Source, Message Key, Delimiter, and Operating attribute is + specified in the PDU Payload using the Tag-Length-Value (TLV) data + format. iSNS Registration and Query messages are sent by iSNS Clients + + + + +Tseng, et al. Standards Track [Page 41] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + to the iSNS server IP Address and well-known TCP/UDP Port. The iSNS + Responses will be sent to the iSNS Client IP address and TCP/UDP port + number from the original request message. + +5.6.1. Source Attribute + + The Source Attribute is used to identify the Storage Node to the iSNS + server for queries and other messages that require source + identification. The Source Attribute uniquely identifies the source + of the message. Valid Source Attribute types are shown below. + + Valid Source Attributes + ----------------------- + iSCSI Name + FC Port Name WWPN + + For a query operation, the Source Attribute is used to limit the + scope of the specified operation to the Discovery Domains of which + the source is a member. Special Control Nodes, identified by the + Source Attribute, may be administratively configured to perform the + specified operation on all objects in the iSNS database without + scoping to Discovery Domains. + + For messages that change the contents of the iSNS database, the iSNS + server MUST verify that the Source Attribute identifies either a + Control Node or a Storage Node that is a part of the Network Entity + containing the added, deleted, or modified objects. + +5.6.2. Message Key Attributes + + Message Key attributes are used to identify matching objects in the + iSNS database for iSNS query and registration messages. If present, + the Message Key MUST be a Registration or Query Key for an object as + described in Sections 5.6.5 and 6.1. A Message Key is not required + when a query spans the entire set of objects available to the Source + or a registration is for a new Entity. + + iSCSI Names used in the Message Key MUST be normalized according to + the stringprep template [STRINGPREP]. Entity Identifiers (EIDs) used + in the Message Key MUST be normalized according to the nameprep + template [NAMEPREP]. + +5.6.3. Delimiter Attribute + + The Delimiter Attribute separates the Message Key attributes from the + Operating Attributes in a PDU Payload. The Delimiter Attribute has a + tag value of 0 and a length value of 0. The Delimiter Attribute is + always 8 bytes long (a 4-byte tag field and a 4-byte length field, + + + +Tseng, et al. Standards Track [Page 42] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + all containing zeros). If a Message Key is not required for a + message, then the Delimiter Attribute immediately follows the Source + Attribute. + +5.6.4. Operating Attributes + + The Operating Attributes are a list of one or more key and non-key + attributes related to the actual iSNS registration or query operation + being performed. + + Operating Attributes include object key attributes and non-key + attributes. Object key attributes uniquely identify iSNS objects. + Key attributes MUST precede the non-key attributes of each object in + the Operating Attributes. The tag value distinguishes the attribute + as an object key attribute (i.e., tag=1, 16&17, 32, 64, and 96) or a + non-key attribute. iSCSI Names used in the Operating Attributes MUST + be normalized according to the stringprep template [STRINGPREP]. + Entity Identifiers (EIDs) used in the Operating Attributes MUST be + normalized according to the nameprep template [NAMEPREP]. + + The ordering of Operating Attributes in the message is important for + determining the relationships among objects and their ownership of + non-key attributes. iSNS protocol messages that violate these + ordering rules SHALL be rejected with the Status Code of 2 (Message + Format Error). See the message descriptions for proper operating + attribute ordering requirements. + + Some objects are keyed by more than one object key attribute value. + For example, the Portal object is keyed by attribute tags 16 and 17. + When describing an object keyed by more than one key attribute, every + object key attribute of that object MUST be listed sequentially by + tag value in the message before non-key attributes of that object and + key attributes of the next object. A group of key attributes of this + kind is treated as a single logical key attribute when identifying an + object. + + Non-key attributes that immediately follow key attributes MUST be + attributes of the object referenced by the key attributes. All non- + key attributes of an object MUST be listed before the object key + attributes introducing the next object. + + Objects MUST be listed in inheritance order, according to their + containment order. Storage Node and Portal objects and their + respective attributes MUST follow the Network Entity object to which + they have a relationship. Similarly, FC Device objects MUST follow + the Storage Node object to which they have a relationship. + + + + + +Tseng, et al. Standards Track [Page 43] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Vendor-specific objects defined by tag values in the range 1537-2048 + have the same requirements described above. + +5.6.4.1. Operating Attributes for Query and Get Next Requests + + In Query and Get Next request messages, TLV attributes with length + value of 0 are used to indicate which Operating Attributes are to be + returned in the corresponding response. Operating Attribute values + that match the TLV attributes in the original message are returned in + the response message. + +5.6.5. Registration and Query Request Message Types + + The following describes each query and message type. + +5.6.5.1. Device Attribute Registration Request (DevAttrReg) + + The DevAttrReg message type is 0x0001. The DevAttrReg message + provides the means for iSNS clients to update existing objects or + register new objects. The value of the replace bit in the FLAGs + field determines whether the DevAttrReg message updates or replaces + an existing registration. + + The Source Attribute identifies the Node initiating the registration + request. + + The Message Key identifies the object the DevAttrReg message acts + upon. It MUST contain the key attribute(s) identifying an object. + This object MUST contain all attributes and related subordinate + object attributes that will be included in the Operating Attributes + of the DevAttrReg PDU Payload. The key attribute(s) identifying this + object MUST also be included among the Operating Attributes. + + If the Message Key contains an EID and no pre-existing objects match + the Message Key, then the DevAttrReg message SHALL create a new + Entity with the specified EID and any new object(s) specified by the + Operating Attributes. The replace bit SHALL be ignored. + + If the Message Key does not contain an EID, and no pre-existing + objects match the Message Key, then the DevAttrReg message SHALL be + rejected with a status code of 3 (Invalid Registration). + + If the Message Key is not present, then the DevAttrReg message + implicitly registers a new Network Entity. In this case, the replace + bit SHALL be ignored; a new Network Entity SHALL be created. + Existing entities, their objects, and their relationships remain + unchanged. + + + + +Tseng, et al. Standards Track [Page 44] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + The replace bit determines the kind of operation conducted on the + object identified in the DevAttrReg Message Key. The replace bit + only applies to the DevAttrReg message; it is ignored for all other + message types. + + If the replace bit is set, then the objects, attributes, and + relationships specified in the Operating Attributes SHALL replace the + object identified by the Message Key. The object and all of its + subordinate objects SHALL be deregistered, and the appropriate SCNs + SHALL be sent by the iSNS server for the deregistered objects. The + objects listed in the Operating Attributes are then used to replace + the just-deregistered objects. Note that additional SCNs SHALL be + sent for the newly-registered objects, if appropriate. Existing + objects and relationships that are not identified or that are + subordinate to the object identified by the Message Key MUST NOT be + affected or changed. + + If the replace bit is not set, then the message updates the + attributes of the object identified by the Message Key and its + subordinate objects. Existing object containment relationships MUST + NOT be changed. For existing objects, key attributes MUST NOT be + modified, but new subordinate objects MAY be added. + + The Operating Attributes represent objects, attributes, and + relationships that are to be registered. Multiple related objects + and attributes MAY be registered in a single DevAttrReg message. The + ordering of the objects in this message indicates the structure of, + and associations among, the objects to be registered. At least one + object MUST be listed in the Operating Attributes. Additional + objects (if any) MUST be subordinate to the first object listed. Key + attributes MUST precede non-key attributes of each object. A given + object may only appear a maximum of once in the Operating Attributes + of a message. If the Node identified by the Source Attribute is not + a Control Node, then the objects in the operating attributes MUST be + members of the same Network Entity as the Source Node. + + For example, to establish relationships between a Network Entity + object and its Portal and Storage Node objects, the Operating + Attributes list the key and non-key attributes of the Network Entity + object, followed by the key and non-key attributes of each Portal and + Storage Node object to be linked to that Network Entity. Similarly, + an FC Device object that follows a Storage Node object is considered + subordinate to that Storage Node. + + New PG objects are registered when an associated Portal or iSCSI Node + object is registered. An explicit PG object registration MAY follow + a Portal or iSCSI Node object registration in a DevAttrReg message. + + + + +Tseng, et al. Standards Track [Page 45] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + When a Portal is registered, the Portal attributes MAY immediately be + followed by a PGT attribute. The PGT attribute SHALL be followed by + the set of PG iSCSI Names representing nodes that will be associated + to the Portal using the indicated PGT value. Additional sets of PGTs + and PG iSCSI Names to be associated to the registered Portal MAY + follow. Indicated PGT values are assigned to the PG object + associated with the newly registered Portal and to the iSCSI Storage + Node(s) referenced immediately following the PGT attribute in the + operating attributes. + + When an iSCSI Storage Node is registered, the Storage Node attributes + MAY immediately be followed by a PGT attribute. The PGT attribute + SHALL be followed by the set of PG Portal IP-Address, PG TCP/UDP Port + pairs representing Portal objects that will be associated with the + Storage Node using the indicated PGT value. Additional sets of PGTs + and PG Portal IP-Address PG TCP/UDP Port pairs to be associated with + the registered Storage Node MAY follow. Indicated PGT values are + assigned to the PG object associated with the newly registered iSCSI + Storage Node and Portal object(s) referenced immediately following + the PGT attribute in the operating attributes. + + If the PGT value is not included in the Storage Node or Portal object + registration, and if a PGT value was not previously registered for + the relationship, then the PGT for the corresponding PG object SHALL + be registered with a value of 0x00000001. If the PGT attribute is + included in the registration message as a 0-length TLV, then the PGT + value for the corresponding PG object SHALL be registered as NULL. A + 0-length TLV for the PGT in an update registration message overwrites + the previous PGT value with NULL, indicating that there is no + relationship between the Storage Node and Portal. + + A maximum of one Network Entity object can be created or updated with + a single DevAttrReg message. Consequently, the Operating Attributes + MUST NOT contain more than one Network Entity object. There is no + limit to the number of Portal, Storage Node, and FC Device objects + that can listed in the Operating Attributes, provided they are all + subordinate to the listed Network Entity object. + + If the Message Key and Operating Attributes do not contain an EID + attribute, or if the EID attribute has a length of 0, then a new + Network Entity object SHALL be created and the iSNS server SHALL + supply a unique EID value for it. The assigned EID value SHALL be + included in the DevAttrReg Response message. If the Message Key and + Operating Attributes contain an EID that does not match the EID of an + existing Network Entity in the iSNS database, then a new Network + Entity SHALL be created and assigned the value contained in that EID + attribute. Finally, if the Message Key and Operating Attributes + contain an EID that matches the EID of an existing object in the iSNS + + + +Tseng, et al. Standards Track [Page 46] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + database, then the objects, attributes, and relationships specified + in the Operating Attributes SHALL be appended to the existing Network + Entity identified by the EID. + + A registration message that creates a new Network Entity object MUST + contain at least one Portal or one Storage Node. If the message does + not, then it SHALL be considered invalid and result in a response + with Status Code of 3 (Invalid Registration). + + If an iSNS Server does not support a registration feature, such as + explicit PG object registration, then the server SHALL return a + Status Code of 23 (Registration Feature Not Supported). + + Note that the iSNS server may modify or reject the registration of + certain attributes, such as ESI Interval. In addition, the iSNS + server may assign values for additional Operating Attributes that are + not explicitly registered in the original DevAttrReg message, such as + the EID and WWNN Token. + +5.6.5.2. Device Attribute Query Request (DevAttrQry) + + The DevAttrQry message type is 0x0002. The DevAttrQry message + provides an iSNS client with the means to query the iSNS server for + object attributes. + + The Source Attribute identifies the Node initiating the request. For + non-Control Nodes initiating the DevAttrQry message, the query is + scoped to the Discovery Domains of which the initiating Node is a + member. The DevAttrQry message SHALL only return information on + Storage Nodes and their related parent and subordinate objects, where + the Storage Node has a common Discovery Domain with the Node + identified in the Source Attribute. + + The Message Key may contain key or non-key attributes or no + attributes at all. If multiple attributes are used as the Message + Key, then they MUST all be from the same object type (e.g., IP + address and TCP/UDP Port are attributes of the Portal object type). + A Message Key with non-key attributes may match multiple instances of + the specific object type. A Message Key with zero-length TLV(s) is + scoped to every object of the type indicated by the zero-length + TLV(s). An empty Message Key field indicates the query is scoped to + the entire database accessible by the source Node. + + The DevAttrQry response message returns attributes of objects listed + in the Operating Attributes that are related to the Message Key of + the original DevAttrQry message. The Operating Attributes of the + DevAttrQry message contain zero-length TLVs that specify the + attributes that are to be returned in the DevAttrQryRsp message. A + + + +Tseng, et al. Standards Track [Page 47] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Message Key containing zero-length TLVs indicates that the set of + attributes specified in the Operating Attributes are to be returned + for each object matching the type indicated by the Message Key. + + If the Message Key contains non-zero length TLVs, then Operating + Attributes for the object matching the Message Key SHALL be returned + in the DevAttrQryRsp message. Each attribute type (i.e., zero-length + TLV) in the Operating Attributes indicates an attribute from the + object matching the Message Key, or from other objects in the same + Entity having a relationship to the object matching the Message Key, + is to be returned in the response. The ordering of the object keys + and associated attributes returned in the DevAttrQry response message + SHALL be the same as in the original query message. If no objects + match the Message Key, then the DevAttrQryRsp message SHALL NOT + return any operating attributes. Such a message and its + corresponding response SHALL NOT be considered an error. + + The Portal Group object determines whether a relationship exists + between a given Storage Node and Portal object. If the PGT of the + Portal Group is not NULL, then a relationship exists between the + indicated Storage Node and Portal; if the PGT is NULL, then no + relationship exists. Therefore, the value (NULL or not NULL) of the + PGT attribute of each Portal Group object determines the structure + and ordering of the DevAttrQry response to a query for Storage Nodes + and Portals. + + For example, an iSNS database contains a Network Entity having two + Portals and two Nodes. Each Storage Node has two Portal Groups, one + with a NULL PGT value for one Portal and another with a non-NULL PGT + value for the other Portal. The DevAttrQry message contains a + Message Key entry matching one of the Nodes, and Operating Attributes + with zero-length TLVs listing first the Node attributes, Portal + attributes, and then the PG attributes. The response message SHALL + therefore return first the matching Node object, then the requested + attributes of the one Portal object that can be used to access the + Storage Node (as indicated by the PGT), and finally the requested + attributes of the PG object used to access that Storage Node. The + order in which each object's attributes are listed is the same as the + ordering of the object's attributes in the Operating Attributes of + the original request message. + + If the Message Key Attribute contains zero-length TLV(s), then the + query returns requested attributes for all objects matching the + Message Key type (DD restrictions SHALL apply for non-Control Nodes). + If multiple objects match the Message Key type, then the attributes + for each object matching the Message Key MUST be listed before the + attributes for the next matching object are listed in the query + + + + +Tseng, et al. Standards Track [Page 48] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + response. In other words, the process described above must be + iterated in the message response for each object that matches the + Message Key type specified by the zero-length TLV(s). + + For example, an iSNS database contains only one Network Entity having + two Portals and three Nodes. All PG objects in the Entity have a PGT + value of 0x00000001. In the DevAttrQry message, the Message Key + contains a zero-length TLV specifying a Node type, and Operating + Attributes listing first the Node attributes, and then the Portal + attributes. The response message will return, in the following + order, the attributes for the first, next, and last Node objects, + each followed by attributes for both Portals. If that same + DevAttrQry message had instead contained a zero-length TLV specifying + the Network Entity type, then the response message would have + returned attributes for all three Node objects, followed by + attributes for the two Portals. + + If there is no Message Key Attribute, then the query returns all + attributes in the iSNS database (once again, DD restrictions SHALL + apply for non-Control Nodes). All attributes matching the type + specified by each zero-length TLV in the Operating Attributes SHALL + be listed. All attributes of each type SHALL be listed before the + attributes matching the next zero-length TLV are listed. + + For example, an iSNS database contains two Entities, each having two + Nodes and two Portals. The DevAttrQry message contains no Message + Key attribute, and Operating Attributes list first the Portal + attributes, and then the Node attributes. The Operating Attributes + of the response message will return attributes from each of the four + Portals, followed by attributes from each of the four nodes. + + If a DevAttrQry message requests an attribute for which the iSNS + server has no value, then the server SHALL NOT return the requested + attribute in the query response. Such query and response messages + SHALL NOT be considered errors. + + Registration and query messages for iSNS server-specific attributes + (i.e., tags in the range 132 to 384) SHALL be formatted using the + identifying key attribute of the Storage Node originating the query + (i.e., iSCSI Name or FC Port Name WWPN) for both the Source Attribute + and Message Key attribute. Operating Attributes SHALL include the + TLV of the server-specific attribute being requested. + + DD membership can be discovered through the DevAttrQry message by + including either DD member attributes (i.e., DD Member iSCSI Index, + DD Member iSCSI Node, DD Member iFCP Node, DD Member Portal Index, DD + Member Portal IP Addr, and DD Member Portal TCP/UDP) or the object + key of the Storage Node or Portal (i.e., iSCSI Name, iSCSI Index, + + + +Tseng, et al. Standards Track [Page 49] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Portal IP Addr, Portal TCP/UDP Port, and Portal Index) in the + Operating Attributes. Using DD member attributes SHALL return both + registered and unregistered member Storage Nodes and/or Portals of a + DD. DevAttrQry messages using the Storage Node and/or Portal object + key SHALL return only member Storage Nodes or Portals that are + currently registered in the iSNS database. + + The DevAttrQry message SHALL support the following minimum set of + Message Key Attributes: + + Valid Message Key Attributes for Queries + ---------------------------------------- + Entity Identifier + Entity Protocol + Portal IP-Address & Portal TCP/UDP Port + Portal Index + iSCSI Node Type + iSCSI Name + iSCSI Index + PG Index + FC Port Name WWPN + FC Port Type + FC-4 Type + Discovery Domain ID + Discovery Domain Set ID + Source Attribute (for server-specific attributes) + Switch Name (FC Device WWNN--for Virtual_Fabric_ID queries) + +5.6.5.3. Device Get Next Request (DevGetNext) + + The DevGetNext message type is 0x0003. This message provides the + iSNS client with the means to retrieve each and every instance of an + object type exactly once. + + The Source Attribute identifies the Node initiating the DevGetNext + request, and is used to scope the retrieval process to the Discovery + Domains of which the initiating Node is a member. + + The Message Key Attribute may be an Entity Identifier (EID), iSCSI + Name, iSCSI Index, Portal IP Address and TCP/UDP Port, Portal Index, + PG Index, FC Node Name WWNN, or FC Port Name WWPN. If the TLV length + of the Message Key Attribute(s) is zero, then the first object entry + in the iSNS database matching the Message Key type SHALL be returned + in the Message Key of the corresponding DevGetNextRsp message. If + non-zero-length TLV attributes are contained in the Message Key, then + the DevGetNext response message SHALL return the next object stored + after the object identified by the Message Key in the original + DevGetNext request message. + + + +Tseng, et al. Standards Track [Page 50] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + If the Message Key provided matches the last object instance in the + iSNS database, then the Status Code of 9 (No Such Entry) SHALL be + returned in the response. + + The Operating Attributes can be used to specify the scope of the + DevGetNext request, and to specify the attributes of the next object, + which are to be returned in the DevGetNext response message. All + Operating Attributes MUST be attributes of the object type identified + by the Message Key. For example, if the Message Key is an Entity_ID + attribute, then the Operating Attributes MUST NOT contain attributes + of Portals. + + Non-zero-length TLV attributes in the Operating Attributes are used + to scope the DevGetNext message. Only the next object with attribute + values that match the non-zero-length TLV attributes SHALL be + returned in the DevGetNext response message. + + Zero-length TLV attributes MUST be listed after non-zero-length + attributes in the Operating Attributes of the DevGetNext request + message. Zero-length TLV attributes specify the attributes of the + next object which are to be returned in the DevGetNext response + message. + + Note that there are no specific requirements concerning the order in + which object entries are retrieved from the iSNS database; the + retrieval order of object entries using the DevGetNext message is + implementation specific. + + The iSNS client is responsible for ensuring that information acquired + through use of the DevGetNext message is accurate and up-to-date. + There is no assurance that the iSNS database will not change between + successive DevGetNext request messages. If the Message Key provided + does not match an existing database entry, then attributes for the + next object key following the provided Message Key SHALL be returned. + For example, an object entry may have been deleted between successive + DevGetNext messages. This may result in a DevGetNext request in + which the Message Key does not match an existing object entry. In + this case, attributes for the next object stored in the iSNS database + are returned. + +5.6.5.4. Device Deregister Request (DevDereg) + + The DevDereg message type is 0x0004. This message is used to remove + object entries from the iSNS database. One or more objects may be + removed through a single DevDereg message. Note that deregistered + Storage Node objects will retain membership in their Discovery + Domain(s) until explicit deregistration of the membership(s) or + Discovery Domain(s). + + + +Tseng, et al. Standards Track [Page 51] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Upon receiving the DevDereg, the iSNS server removes all objects + identified by the Operating Attribute(s), and all subordinate objects + that are solely dependent on those identified objects. For example, + removal of a Network Entity also results in removal of all associated + Portal, Portal Group, Storage Node, and FC Device objects associated + with that Network Entity. FC Device objects SHALL not be + deregistered in this manner unless all Storage Nodes associated with + them have been deregistered. + + The DevDereg request PDU Payload contains a Source Attribute and + Operating Attribute(s); there are no Message Key Attributes. If the + Node identified by the Source Attribute is not a Control Node, then + it MUST be from the same Network Entity as the object(s) identified + for removal by the Operating Attribute(s). Valid Operating + Attributes are shown below: + + Valid Operating Attributes for DevDereg + --------------------------------------- + Entity Identifier + Portal IP-Address & Portal TCP/UDP Port + Portal Index + iSCSI Name + iSCSI Index + FC Port Name WWPN + FC Node Name WWNN + + The removal of the object may result in SCN messages to the + appropriate iSNS clients. + + Attempted deregistration of non-existing entries SHALL not be + considered an error. + + If all Nodes and Portals associated with a Network Entity are + deregistered, then the Network Entity SHALL also be removed. + + If both the Portal and iSCSI Storage Node objects associated with a + Portal Group object are removed, then that Portal Group object SHALL + also be removed. The Portal Group object SHALL remain registered as + long as either of its associated Portal or iSCSI Storage Node objects + remain registered. If a deleted Storage Node or Portal object is + subsequently re-registered, then a relationship between the re- + registered object and an existing Portal or Storage Node object + registration, indicated by the PG object, SHALL be restored. + + + + + + + + +Tseng, et al. Standards Track [Page 52] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.6.5.5. SCN Register Request (SCNReg) + + The SCNReg message type is 0x0005. The State Change Notification + Registration Request (SCNReg) message allows an iSNS client to + register a Storage Node to receive State Change Notification (SCN) + messages. + + The SCN notifies the Storage Node of changes to any Storage Nodes + within any DD of which it is a member. If the Storage Node is a + Control Node, it SHALL receive SCN notifications for changes in the + entire network. Note that whereas SCNReg sets the SCN Bitmap field, + the DevAttrReg message registers the UDP or TCP Port used by each + Portal to receive SCN messages. If no SCN Port fields of any Portals + of the Storage Node are registered to receive SCN messages, then the + SCNReg message SHALL be rejected with Status Code 17 (SCN + Registration Rejected). + + The SCNReg request PDU Payload contains a Source Attribute, a Message + Key Attribute, and an Operating Attribute. Valid Message Key + Attributes for a SCNReg are shown below: + + Valid Message Key Attributes for SCNReg + --------------------------------------- + iSCSI Name + FC Port Name WWPN + + The node with the iSCSI Name or FC Port Name WWPN attribute that + matches the Message Key in the SCNReg message is registered to + receive SCNs using the specified SCN bitmap. A maximum of one Node + SHALL be registered for each SCNReg message. + + The SCN Bitmap is the only operating attribute of this message, and + it always overwrites the previous contents of this field in the iSNS + database. The bitmap indicates the SCN event types for which the + Node is registering. + + Note that the settings of this bitmap determine whether the SCN + registration is for regular SCNs or management SCNs. Control Nodes + MAY conduct registrations for management SCNs; iSNS clients that are + not supporting Control Nodes MUST NOT conduct registrations for + management SCNs. Control Nodes that register for management SCNs + receive a copy of every SCN message generated by the iSNS server. It + is recommended that management registrations be used only when needed + in order to conserve iSNS server resources. In addition, a Control + Node that conducts such registrations should be prepared to receive + the anticipated volume of SCN message traffic. + + + + + +Tseng, et al. Standards Track [Page 53] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.6.5.6. SCN Deregister Request (SCNDereg) + + The SCNDereg message type is 0x0006. The SCNDereg message allows an + iSNS client to stop receiving State Change Notification (SCN) + messages. + + The SCNDereg request message PDU Payload contains a Source Attribute + and Message Key Attribute(s). Valid Message Key Attributes for a + SCNDereg are shown below: + + Valid Message Key Attributes for SCNDereg + ----------------------------------------- + iSCSI Name + FC Port Name WWPN + + The node with an iSCSI Name or FC Port Name WWPN attribute that + matches the Message Key Attributes in the SCNDereg message is + deregistered for SCNs. The SCN bitmap field of such Nodes are + cleared. A maximum of one Node SHALL be deregistered for each + SCNDereg message. + + There are no Operating Attributes in the SCNDereg message. + +5.6.5.7. SCN Event (SCNEvent) + + The SCNEvent message type is 0x0007. The SCNEvent is a message sent + by an iSNS client to request generation of a State Change + Notification (SCN) message by the iSNS server. The SCN, sent by the + iSNS server, then notifies iFCP, iSCSI, and Control Nodes within the + affected DD of the change indicated in the SCNEvent. + + Most SCNs are automatically generated by the iSNS server when Nodes + are registered or deregistered from the directory database. SCNs are + also generated when a network management application or Control Node + makes changes to the DD membership in the iSNS server. However, an + iSNS client can trigger an SCN by using SCNEvent. + + The SCNEvent message PDU Payload contains a Source Attribute, a + Message Key Attribute, and an Operating Attribute. Valid Key + Attributes for a SCNEvent are shown below: + + Valid Message Key Attributes for SCNEvent + ----------------------------------------- + iSCSI Name + FC Port Name WWPN + + + + + + +Tseng, et al. Standards Track [Page 54] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + The Operating Attributes section SHALL contain the SCN Event Bitmap + attribute. The bitmap indicates the event that caused the SCNEvent + to be generated. + +5.6.5.8. State Change Notification (SCN) + + The SCN message type is 0x0008. The SCN is a message generated by + the iSNS server, notifying a registered Storage Node of changes. + There are two types of SCN registrations: regular registrations and + management registrations. Regular SCNs notify iSNS clients of events + within the discovery domain. Management SCNs notify Control Nodes + that register for management SCNs of events occurring anywhere in the + network. + + If no active TCP connection to the SCN recipient exists, then the SCN + message SHALL be sent to one Portal of the registered Storage Node + that has a registered TCP or UDP Port value in the SCN Port field. + If more than one Portal of the Storage Node has a registered SCN Port + value, then the SCN SHALL be delivered to any one of the indicated + Portals, provided that the selected Portal is not the subject of the + SCN. + + The types of events that can trigger an SCN message, and the amount + of information contained in the SCN message, depend on the registered + SCN Event Bitmap for the Storage Node. The iSCSI Node SCN Bitmap is + described in Section 6.4.4. The iFCP SCN Bitmap is described in + Section 6.6.12. + + + + + + + + + + + + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 55] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + The format of the SCN PDU Payload is shown below: + + +----------------------------------------+ + | Destination Attribute | + +----------------------------------------+ + | Timestamp | + +----------------------------------------+ + | Source SCN Bitmap 1 | + +----------------------------------------+ + | Source Attribute [1] | + +----------------------------------------+ + | Source Attribute [2](if present) | + +----------------------------------------+ + | Source Attribute [3](if present) | + +----------------------------------------+ + | Source Attribute [n](if present) | + +----------------------------------------+ + | Source SCN Bitmap 2 (if present) | + +----------------------------------------+ + | . . . | + +----------------------------------------+ + + All PDU Payload attributes are in TLV format. + + The Destination Attribute is the Node identifier that is receiving + the SCN. The Destination Attribute can be an iSCSI Name or FC Port + Name. + + The Timestamp field, using the Timestamp TLV format, described in + Section 6.2.4, indicates the time the SCN was generated. + + The Source SCN Bitmap field indicates the type of SCN notification + (i.e., regular or management SCN), and the type of event that caused + the SCN to be generated; it does not necessarily correlate with the + original SCN bitmap registered in the iSNS server. + + Following the timestamp, the SCN message SHALL list the SCN bitmap, + followed by the key attribute (i.e., iSCSI Name or FC Port Name) of + the Storage Node affected by the SCN event. If the SCN is a + Management SCN, then the SCN message SHALL also list the DD_ID and/or + DDS_ID of the Discovery Domains and Discovery Domain Sets (if any) + that caused the change in state for that Storage Node. These + additional attributes (i.e., DD_ID and/or DDS_ID) shall immediately + follow the iSCSI Name or FC Port Name and precede the next SCN bitmap + for the next notification message (if any). The SCN bitmap is used + as a delineator for SCN messages providing multiple state change + notifications. + + + + +Tseng, et al. Standards Track [Page 56] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + For example, a regular SCN for notifying an iSNS client of a new + Portal available for a particular iSCSI target would contain the SCN + bitmap followed by the iSCSI Name of the target device as the source + attribute. If the SCN were a management SCN, then the iSCSI Name + would be followed by the DD_ID(s) of the shared Discovery Domains + that allow the destination Storage Node to have visibility to the + affected Storage Node. If a Discovery Domain Set (DDS) was enabled + in order to provide this visibility, then the appropriate DDS_ID + would be included as well. + + A management SCN is also generated to notify a Control Node of the + creation, deletion, or modification of a Discovery Domain or + Discovery Domain Set. In this case, the DD_ID and/or DDS_ID of the + affected Discovery Domain and/or Discovery Domain Set would follow + the SCN bitmap. + + For example, a management SCN to notify a Control Node of a new DD + within a Discovery Domain Set would contain both the DD_ID and the + DDS_ID of the affected Discovery Domain and Discovery Domain Set + among the Source Attributes. + + See Sections 6.4.4 and 6.6.12 for additional information on the SCN + Bitmap. + +5.6.5.9. DD Register (DDReg) + + The DDReg message type is 0x0009. This message is used to create a + new Discovery Domain (DD), to update an existing DD Symbolic Name + and/or DD Features attribute, and to add DD members. + + DDs are uniquely defined using DD_IDs. DD registration attributes + are described in Section 6.11. + + The DDReg message PDU Payload contains the Source Attribute and + optional Message Key and Operating Attributes. + + The Message Key, if used, contains the DD_ID of the Discovery Domain + to be registered. If the Message Key contains a DD_ID of an existing + DD entry in the iSNS database, then the DDReg message SHALL attempt + to update the existing entry. If the DD_ID in the Message Key (if + used) does not match an existing DD entry, then the iSNS server SHALL + reject the DDReg message with a status code of 3 (Invalid + Registration). If the DD_ID is included in both the Message Key and + Operating Attributes, then the DD_ID value in the Message Key MUST be + the same as the DD_ID value in the Operating Attributes. + + + + + + +Tseng, et al. Standards Track [Page 57] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + A DDReg message with no Message Key SHALL result in the attempted + creation of a new Discovery Domain (DD). If the DD_ID attribute + (with non-zero length) is included among the Operating Attributes in + the DDReg message, then the new Discovery Domain SHALL be assigned + the value contained in that DD_ID attribute. Otherwise, if the DD_ID + attribute is not contained among the Operating Attributes of the + DDReg message, or if the DD_ID is an operating attribute with a TLV + length of 0, then the iSNS server SHALL assign a DD_ID value. The + assigned DD_ID value is then returned in the DDReg Response message. + The Operating Attributes can also contain the DD Member iSCSI Node + Index, DD Member iSCSI Name, DD Member FC Port Name, DD Member Portal + IP Address, DD Member Portal TCP/UDP Port Number, or DD Member Portal + Index of members to be added to the DD. It may also contain the + DD_Symbolic_Name and/or DD_Features of the DD. + + This message SHALL add any DD members listed as Operating Attributes + to the Discovery Domain specified by the DD_ID. If the DD_Features + attribute is an Operating Attribute, then it SHALL be stored in the + iSNS server as the feature list for the specified DD. If the + DD_Symbolic_Name is an operating attribute and its value is unique + (i.e., it does not match the registered DD_Symbolic_Name for another + DD), then the value SHALL be stored in the iSNS database as the + DD_Symbolic_Name for the specified Discovery Domain. If the value + for the DD_Symbolic_Name is not unique, then the iSNS server SHALL + reject the attempted DD registration with a status code of 3 (Invalid + Registration). + + When creating a new DD, if the DD_Symbolic_Name is not included in + the Operating Attributes, or if it is included with a zero-length + TLV, then the iSNS server SHALL provide a unique DD_Symbolic_Name + value for the created DD. The assigned DD_Symbolic_Name value SHALL + be returned in the DDRegRsp message. + + When creating a new DD, if the DD_Features attribute is not included + in the Operating Attributes, then the iSNS server SHALL assign the + default value. The default value for DD_Features is 0. + + DD Member iSCSI Name, DD Member iFCP Node, DD Member Portal IP + Address, and DD Member TCP/UDP Port Number attributes included in the + Operating Attributes need not match currently existing iSNS database + entries. This allows, for example, a Storage Node to be added to a + DD even if the Storage Node is not currently registered in the iSNS + database. A Storage Node or Portal can thereby be added to a DD at + the time of the DDs creation, even if the Storage Node or Portal is + not currently active in the storage network. + + + + + + +Tseng, et al. Standards Track [Page 58] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + If the Operating Attributes contain a DD Member iSCSI Name value for + a Storage Node that is currently not registered in the iSNS database, + then the iSNS server MUST allocate an unused iSCSI Node Index for + that Storage Node. The assigned iSCSI Node Index SHALL be returned + in the DDRegRsp message as the DD Member iSCSI Node Index. The + allocated iSCSI Node Index value SHALL be assigned to the Storage + Node if and when it registers in the iSNS database. + + If the Operating Attributes contain a DD Member Portal IP Addr and DD + Member Portal TCP/UDP value for a Portal that is not currently + registered in the iSNS database, then the iSNS server MUST allocate + an unused Portal Index value for that Portal. The assigned Portal + Index value SHALL be returned in the DDRegRsp message as the DD + Member Portal Index. The allocated Portal Index value SHALL be + assigned to the Portal if and when it registers in the iSNS database. + + DD Member iSCSI Node Index and DD Member Portal Index attributes that + are provided in the Operating Attributes MUST match a corresponding + iSCSI Node Index or Portal Index of an existing Storage Node or + Portal entry in the iSNS database. Furthermore, the DD Member iSCSI + Node Index and DD Member Portal Index SHALL NOT be used to add + Storage Nodes or Portals to a DD unless those Storage Nodes or + Portals are actively registered in the iSNS database. + +5.6.5.10. DD Deregister (DDDereg) + + The DDDereg message type is 0x000A. This message allows an iSNS + client to deregister an existing Discovery Domain (DD) and to remove + members from an existing DD. + + DDs are uniquely identified using DD_IDs. DD registration attributes + are described in Section 6.11. + + The DDDereg message PDU Payload contains a Source Attribute, Message + Key Attribute, and optional Operating Attributes. + + The Message Key Attribute for a DDDereg message is the DD ID for the + Discovery Domain being removed or having members removed. If the DD + ID matches an existing DD and there are no Operating Attributes, then + the DD SHALL be removed and a success Status Code returned. Any + existing members of that DD SHALL remain in the iSNS database without + membership in the just-removed DD. + + If the DD ID matches an existing DD and there are Operating + Attributes matching DD members, then the DD members identified by the + Operating Attributes SHALL be removed from the DD and a successful + Status Code returned. + + + + +Tseng, et al. Standards Track [Page 59] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + If a DD Member iSCSI Name identified in the Operating Attributes + contains an iSCSI Name for a Storage Node that is not currently + registered in the iSNS database or contained in another DD, then the + association between that Storage Node and its pre-assigned iSCSI Node + Index SHALL be removed. The pre-assigned iSCSI Node Index value no + longer has an association to a specific iSCSI Name and can now be + re-assigned. + + If a DD Member Portal IP Address and DD Member TCP/UDP Port + identified in the Operating Attributes reference a Portal that is not + currently registered in the iSNS database or contained in another DD, + then the association between that Portal and its pre-assigned Portal + Index SHALL be removed. The pre-assigned Portal Index value can now + be reassigned. + + The attempted deregistration of non-existent DD entries SHALL not be + considered an error. + +5.6.5.11. DDS Register (DDSReg) + + The DDSReg message type is 0x000B. This message allows an iSNS + client to create a new Discovery Domain Set (DDS), to update an + existing DDS Symbolic Name and/or DDS Status, or to add DDS members. + + DDSs are uniquely defined using DDS_IDs. DDS registration attributes + are described in Section 6.11.1. + + The DDSReg message PDU Payload contains the Source Attribute and, + optionally, Message Key and Operating Attributes. + + The Message Key, if used, contains the DDS_ID of the Discover Domain + Set to be registered or modified. If the Message Key contains a + DDS_ID of an existing DDS entry in the iSNS database, then the DDSReg + message SHALL attempt to update the existing entry. If the DDS_ID in + the Message Key (if used) does not match an existing DDS entry, then + the iSNS server SHALL reject the DDSReg message with a status code of + 3 (Invalid Registration). If the DDS_ID is included in both the + Message Key and Operating Attributes, then the DDS_ID value in the + Message Key MUST be the same as the DDS_ID value in the Operating + Attributes. + + A DDSReg message with no Message Key SHALL result in the attempted + creation of a new Discovery Domain Set (DDS). If the DDS_ID + attribute (with non-zero length) is included among the Operating + Attributes in the DDSReg message, then the new Discovery Domain Set + SHALL be assigned the value contained in that DDS_ID attribute. + Otherwise, if the DDS_ID attribute is not contained among the + Operating Attributes of the DDSReg message, or if the DDS_ID is an + + + +Tseng, et al. Standards Track [Page 60] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + operating attribute with a TLV length of 0, then the iSNS server + SHALL assign a DDS_ID value. The assigned DDS_ID value is then + returned in the DDSReg Response message. The Operating Attributes + can also contain the DDS_Symbolic_Name, the DDS Status, and the + DD_IDs of Discovery Domains to be added to the DDS. + + When creating a new DDS, if the DDS Symbolic Name is included in the + Operating Attributes and its value is unique (i.e., it does not match + the registered DDS Symbolic Name for another DDS), then the value + SHALL be stored in the iSNS database as the DDS Symbolic Name for + that DDS. If the value for the DDS Symbolic Name is not unique, then + the iSNS server SHALL reject the attempted DDS registration with a + status code of 3 (Invalid Registration). + + When creating a new DDS, if the DDS Symbolic Name is not included in + the Operating Attributes, or if it is included with a zero-length + TLV, then the iSNS server SHALL provide a unique DDS Symbolic Name + value for the created DDS. The assigned DDS Symbolic Name value + SHALL be returned in the DDSRegRsp message. + + This message SHALL add any DD_IDs listed as Operating Attributes to + the Discovery Domain Set specified by the DDS_ID Message Key + Attribute. In addition, if the DDS_Symbolic_Name is an operating + attribute and the value is unique, then it SHALL be stored in the + iSNS database as the DDS_Symbolic_Name for the specified Discovery + Domain Set. + + If a DD_ID listed in the Operating Attributes does not match an + existing DD, then a new DD using the DD_ID SHALL be created. In this + case for the new DD, the iSNS server SHALL assign a unique value for + the DD Symbolic Name and SHALL set the DD Features attribute to the + default value of 0. These assigned values SHALL be returned in the + DDSRegRsp message. + +5.6.5.12. DDS Deregister (DDSDereg) + + The DDSDereg message type is 0x000C. This message allows an iSNS + client to deregister an existing Discovery Domain Set (DDS) or to + remove some DDs from an existing DDS. + + The DDSDereg message PDU Payload contains a Source Attribute, a + Message Key Attribute, and optional Operating Attributes. + + The Message Key Attribute for a DDSDereg message is the DDS ID for + the DDS being removed or having members removed. If the DDS ID + matches an existing DDS and there are no Operating Attributes, then + + + + + +Tseng, et al. Standards Track [Page 61] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + the DDS SHALL be removed and a success Status Code returned. Any + existing members of that DDS SHALL remain in the iSNS database + without membership in the just-removed DDS. + + If the DDS ID matches an existing DDS, and there are Operating + Attributes matching DDS members, then the DDS members SHALL be + removed from the DDS and a success Status Code returned. + + The attempted deregistration of non-existent DDS entries SHALL not be + considered an error. + +5.6.5.13. Entity Status Inquiry (ESI) + + The ESI message type is 0x000D. This message is sent by the iSNS + server, and is used to verify that an iSNS client Portal is reachable + and available. The ESI message is sent to the ESI UDP port provided + during registration, or to the TCP connection used for ESI + registration, depending on which communication type that is being + used. + + The ESI message PDU Payload contains the following attributes in TLV + format and in the order listed: the current iSNS timestamp, the EID, + the Portal IP Address, and the Portal TCP/UDP Port. The format of + this message is shown below: + + +----------------------------------------+ + | Timestamp | + +----------------------------------------+ + | Entity_ID | + +----------------------------------------+ + | Portal IP Address | + +----------------------------------------+ + | Portal TCP/UDP Port | + +----------------------------------------+ + + The ESI response message PDU Payload contains a status code, followed + by the Attributes from the original ESI message. + + If the Portal fails to respond to an administratively-determined + number of consecutive ESI messages, then the iSNS server SHALL remove + that Portal from the iSNS database. If there are no other remaining + ESI-monitored Portals for the associated Network Entity, then the + Network Entity SHALL also be removed. The appropriate State Change + Notifications, if any, SHALL be triggered. + + + + + + + +Tseng, et al. Standards Track [Page 62] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.6.5.14. Name Service Heartbeat (Heartbeat) + + This message, if used, is only sent by the active iSNS server. It + allows iSNS clients and backup servers listening to a broadcast or + multicast address to discover the IP address of the primary and + backup iSNS servers. It also allows concerned parties to monitor the + health and status of the primary iSNS server. + + This message is NOT in TLV format. There is no response message to + the Name Service Heartbeat. + + MSb LSb + 0 31 + +------------------------------------------------+ + | Active Server IP-Address | 16 Bytes + +------------------------------------------------+ + | iSNS TCP Port | iSNS UDP Port | 4 Bytes + +------------------------------------------------+ + | Interval | 4 Bytes + +------------------------------------------------+ + | Counter | 4 Bytes + +------------------------------------------------+ + | RESERVED | Backup Servers | 4 Bytes + +------------------------------------------------+ + | Primary Backup Server IP Address(if any) | 16 Bytes + +------------------------------------------------+ + |Backup TCP Port(if any)|Backup UDP Port(if any) | 4 Bytes + +------------------------------------------------+ + | 2nd Backup Server IP Address(if any) | 16 Bytes + +------------------------------------------------+ + |Backup TCP Port(if any)|Backup UDP Port(if any) | 4 Bytes + +------------------------------------------------+ + | . . . | + +------------------------------------------------+ + | VENDOR SPECIFIC | + +------------------------------------------------+ + + The heartbeat PDU Payload contains the following: + + Active Server IP Address: the IP Address of the active iSNS server in + IPv6 format. When this field contains an IPv4 + value, it is stored as an IPv4-mapped IPv6 address. + That is, the most significant 10 bytes are set to + 0x00, with the next two bytes set to 0xFFFF + [RFC2373]. When this field contains an IPv6 value, + the entire 16-byte field is used. + + Active TCP Port: the TCP Port of the server currently in use. + + + +Tseng, et al. Standards Track [Page 63] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Active UDP Port: the UDP Port of the server currently in use, + otherwise 0. + + Interval: the interval, in seconds, of the heartbeat. + + Counter: a count that begins at 0 when this server becomes + active. The count increments by one for each + heartbeat sent since this server became active. + + Backup Servers: the number of iSNS backup servers. The IP address, + TCP Port, and UDP Port of each iSNS backup server + follow this field. Note that if backup servers are + used, then the active iSNS server SHOULD be among + the list of backup servers. + + The content of the remainder of this message after the list of backup + servers is vendor-specific. Vendors may use additional fields to + coordinate between multiple iSNS servers, and/or to identify vendor- + specific features. + +5.6.5.15. Request FC_DOMAIN_ID (RqstDomId) + + The RqstDomId message type is 0x0011. This message is used for iFCP + Transparent Mode to allocate non-overlapping FC_DOMAIN_ID values + between 1 and 239. The iSNS server becomes the address assignment + authority for the entire iFCP fabric. To obtain multiple + FC_DOMAIN_ID values, this request must be repeated to the iSNS server + multiple times. iSNS clients that acquire FC_DOMAIN_ID values from + an iSNS server MUST register for ESI monitoring from that iSNS + server. + + The RqstDomId PDU Payload contains three TLV attributes in the + following order: the requesting Switch Name (WWN) as the Source + Attribute, the Virtual_Fabric_ID as the Message Key Attribute, and + Preferred ID as the operating attribute. The Virtual_Fabric_ID is a + string identifying the domain space for which the iSNS server SHALL + allocate non-overlapping integer FC_DOMAIN_ID values between 1 and + 239. The Preferred_ID is the nominal FC_DOMAIN_ID value requested by + the iSNS client. If the Preferred_ID value is available and has not + already been allocated for the Virtual_Fabric_ID specified in the + message, the iSNS server SHALL return the requested Preferred_ID + value as the Assigned_ID to the requesting client. + + The RqstDomId response contains a Status Code, and the TLV attribute + Assigned ID, which contains the integer value in the space requested. + If no further unallocated values are available from this space, the + iSNS server SHALL respond with the Status Code 18 "FC_DOMAIN_ID Not + Available". + + + +Tseng, et al. Standards Track [Page 64] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Once a FC_DOMAIN_ID value has been allocated to an iSNS client by the + iSNS server for a given Virtual_Fabric_ID, that FC_DOMAIN_ID value + SHALL NOT be reused until it has been deallocated, or until ESI + monitoring detects that the iSNS client no longer exists on the + network and objects for that client are removed from the iSNS + database. + + The iSNS server and client SHALL use TCP to transmit and receive + RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages. + +5.6.5.16. Release FC_DOMAIN_ID (RlseDomId) + + The RlseDomId message type is 0x0012. This message may be used by + iFCP Transparent Mode to release integer identifier values used to + assign 3-byte Fibre Channel PORT_ID values. + + The RlseDomId message contains three TLV attributes in the following + order: the requesting EID as the Source Attribute, the + Virtual_Fabric_ID as the Message Key Attribute, and Assigned_ID as + the operating attribute. Upon receiving the RlseDomId message, the + iSNS server SHALL deallocate the FC_DOMAIN_ID value contained in the + Assigned_ID attribute for the Virtual_Fabric_ID attribute specified. + Upon deallocation, that FC_DOMAIN_ID value can then be requested by + and assigned to a different iSNS client. + + The iSNS server and client SHALL use TCP to transmit and receive + RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages. + +5.6.5.17. Get FC_DOMAIN_IDs (GetDomId) + + The GetDomId message type is 0x0013. This message is used to learn + the currently-allocated FC_DOMAIN_ID values for a given + Virtual_Fabric_ID. + + The GetDomId message PDU Payload contains a Source Attribute and + Message Key Attribute. + + The Message Key Attribute for the GetDomId message is the + Virtual_Fabric_ID. The response to this message returns all the + FC_DOMAIN_ID values that have been allocated for the + Virtual_Fabric_ID specified. + + + + + + + + + + +Tseng, et al. Standards Track [Page 65] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.7. Messages + + The iSNSP response message PDU Payloads contain a Status Code, + followed by a list of attributes, and have the following format: + + MSb LSb + 0 31 + +----------------------------------------+ + | 4-byte STATUS CODE | + +----------------------------------------+ + | Message Key Attribute[1] (if present) | + +----------------------------------------+ + | Message Key Attribute[2] (if present) | + +----------------------------------------+ + | . . . | + +----------------------------------------+ + | - Delimiter Attribute - (if present) | + +----------------------------------------+ + | Operating Attribute[1] (if present) | + +----------------------------------------+ + | Operating Attribute[2] (if present) | + +----------------------------------------+ + | Operating Attribute[3] (if present) | + +----------------------------------------+ + | . . . | + +----------------------------------------+ + + The iSNSP Response messages SHALL be sent to the iSNS Client IP + Address and the originating TCP/UDP Port that was used for the + associated registration and query message. + +5.7.1. Status Code + + The first field in an iSNSP response message PDU Payload is the + Status Code for the operation that was performed. The Status Code + encoding is defined in Section 5.4. + +5.7.2. Message Key Attributes in Response + + Depending on the specific iSNSP request, the response message MAY + contain Message Key Attributes. Message Key Attributes generally + contain the interesting key attributes that are affected by the + operation specified in the original iSNS registration or query + message. + + + + + + + +Tseng, et al. Standards Track [Page 66] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.7.3. Delimiter Attribute in Response + + The Delimiter Attribute separates the key and Operating Attributes in + a response message, if they exist. The Delimiter Attribute has a tag + value of 0 and a length value of 0. The Delimiter Attribute is + effectively 8 bytes long: a 4-byte tag containing 0x00000000, and a 4 + Byte length field containing 0x00000000. + +5.7.4. Operating Attributes in Response + + The Operating Attributes in a response are the results related to the + iSNS registration or query operation being performed. Some response + messages will not have Operating Attributes. + +5.7.5. Registration and Query Response Message Types + + The following sections describe each query and message type. + +5.7.5.1. Device Attribute Registration Response (DevAttrRegRsp) + + The DevAttrRegRsp message type is 0x8001. The DevAttrRegRsp message + contains the results for the DevAttrReg message with the same + TRANSACTION ID. + + The Message Key in the DevAttrRegRsp message SHALL return the Message + Key in the original registration message. If the iSNS server + assigned the Entity Identifier for a Network Entity, then the Message + Key Attribute field SHALL contain the assigned Entity Identifier. + + The Operating Attributes of the DevAttrRegRsp message SHALL contain + the affected object's key and non-key attributes that have been + explicitly modified or created by the original DevAttrReg message. + Among the Operating Attributes, each modified or added non-key + attribute SHALL be listed after its key attribute(s) in the + DevAttrRegRsp message. Implicitly registered attributes MUST NOT be + returned in the DevAttrRegRsp message. Implicitly registered + attributes are those that are assigned a fixed default value or + secondary index value by the iSNS server. + + Implicitly registered PG objects (i.e., PG objects that are not + explicitly included in the registration or replace message) MUST NOT + have their key or non-key attributes returned in the DevAttrRegRsp + message. However, explicitly registered PG objects (i.e., those with + PGT values that are explicitly included in the registration or + replace message) SHALL have their PGT values returned in the + DevAttrRegRsp message. + + + + + +Tseng, et al. Standards Track [Page 67] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + For example, three Portals are registered in the original DevAttrReg + request message. Due to lack of resources, the iSNS server needs to + modify the registered ESI Interval value of one of those Portals. To + accomplish this, the iSNS server returns the key attributes + identifying the Portal, followed by the non-key modified ESI Interval + attribute value, as Operating Attributes of the corresponding + DevAttrRegRsp message. + + If the iSNS server rejects a registration due to invalid attribute + values or types, then the indicated status code SHALL be 3 (Invalid + Registration). If this occurs, then the iSNS server MAY include the + list of invalid attributes in the Operating Attributes of the + DevAttrRsp message. + + Some attributes values (e.g., ESI Interval, Registration Period) in + the original registration message MAY be modified by the iSNS server. + This can occur only for a limited set of attribute types, as + indicated in the table in Section 6.1. When this occurs, the + registration SHALL be considered a success (with status code 0), and + the changed value(s) indicated in the Operating Attributes of the + DevAttrRsp message. + +5.7.5.2. Device Attribute Query Response (DevAttrQryRsp) + + The DevAttrQryRsp message type is 0x8002. The DevAttrQryRsp message + contains the results for the DevAttrQry message with the same + TRANSACTION ID. + + The Message Key in the DevAttrQryRsp message SHALL return the Message + Key in the original query message. + + If no Operating Attributes are included in the original query, then + all Operating Attributes SHALL be returned in the response. + + For a successful query result, the DevAttrQryRsp Operating Attributes + SHALL contain the results of the original DevAttrQry message. + +5.7.5.3. Device Get Next Response (DevGetNextRsp) + + The DevGetNextRsp message type is 0x8003. The DevGetNextRsp message + contains the results for the DevGetNext message with the same + TRANSACTION ID. + + The Message Key Attribute field returns the object keys for the next + object after the Message Key Attribute in the original DevGetNext + message. + + + + + +Tseng, et al. Standards Track [Page 68] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + The Operating Attribute field returns the Operating Attributes of the + next object as requested in the original DevGetNext message. The + values of the Operating Attributes are those associated with the + object identified by the Message Key Attribute field of the + DevGetNextRsp message. + +5.7.5.4. Deregister Device Response (DevDeregRsp) + + The DevDeregRsp message type is 0x8004. This message is the response + to the DevDereg request message. + + This message response does not contain a Message Key, but MAY contain + Operating Attributes. + + In the event of an error, this response message contains the + appropriate status code as well as a list of objects from the + original DevDereg message that were not successfully deregistered + from the iSNS database. This list of objects is contained in the + Operating Attributes of the DevDeregRsp message. Note that an + attempted deregistration of a non-existent object does not constitute + an error, and non-existent entries SHALL not be returned in the + DevDeregRsp message. + +5.7.5.5. SCN Register Response (SCNRegRsp) + + The SCNRegRsp message type is 0x8005. This message is the response + to the SCNReg request message. + + The SCNRegRsp message does not contain any Message Key or Operating + Attributes. + +5.7.5.6. SCN Deregister Response (SCNDeregRsp) + + The SCNDeregRsp message type is 0x8006. This message is the response + to the SCNDereg request message. + + The SCNDeregRsp message does not contain any Message Key or Operating + Attributes. + +5.7.5.7. SCN Event Response (SCNEventRsp) + + The SCNEventRsp message type is 0x8007. This message is the response + to the SCNEvent request message. + + The SCNEventRsp message does not contain any Message Key or Operating + Attributes. + + + + + +Tseng, et al. Standards Track [Page 69] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.7.5.8. SCN Response (SCNRsp) + + The SCNRsp message type is 0x8008. This message is sent by an iSNS + client, and provides confirmation that the SCN message was received + and processed. + + The SCNRsp response contains the SCN Destination Attribute + representing the Node identifier that received the SCN. + +5.7.5.9. DD Register Response (DDRegRsp) + + The DDRegRsp message type is 0x8009. This message is the response to + the DDReg request message. + + The Message Key in the DDRegRsp message SHALL return the Message Key + in the original query message. If the original DDReg message did not + have a Message Key, then the DDRegRsp message SHALL not have a + Message Key. + + If the DDReg operation is successful, the DD ID of the DD created or + updated SHALL be returned as an operating attribute of the message. + + If the DD Symbolic Name attribute or DD Features attribute was + assigned or updated during the DDReg operation, then any new values + SHALL be returned as an operating attribute of the DDRegRsp message. + + If the iSNS server rejects a DDReg due to invalid attribute values or + types, then the indicated status code SHALL be 3 (Invalid + Registration). If this occurs, then the iSNS server MAY include the + list of invalid attributes in the Operating Attributes of the + DDRegRsp message. + +5.7.5.10. DD Deregister Response (DDDeregRsp) + + The DDDeregRsp message type is 0x800A. This message is the response + to the DDDereg request message. + + The DDDeregRsp message does not contain any Message Key or Operating + Attributes. + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 70] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.7.5.11. DDS Register Response (DDSRegRsp) + + The DDSRegRsp message type is 0x800B. This message is the response + to the DDSReg request message. + + The Message Key in the DDSRegRsp message SHALL contain the Message + Key of the original DDSReg message. If the original DDSReg message + did not have a Message Key, then the DDSRegRsp message SHALL NOT have + a Message Key. + + If the DDSReg operation is successful, the DDS ID of the DDS created + or updated SHALL be returned as an operating attribute of the + message. + + If the DDS Symbolic Name attribute or DDS Status attribute was + assigned or updated during the DDSRegRsp operation, then any new + values SHALL be returned as an operating attribute of the DDSRegRsp + message. + + If the iSNS server rejects a DDSReg due to invalid attribute values + or types, then the indicated status code SHALL be 3 (Invalid + Registration). If this occurs, then the iSNS server MAY include the + list of invalid attributes in the Operating Attributes of the + DDSRegRsp message. + +5.7.5.12. DDS Deregister Response (DDSDeregRsp) + + The DDSDeregRsp message type is 0x800C. This message is the response + to the DDSDereg request message. + + The DDSDeregRsp message does not contain any Message Key or Operating + Attributes. + +5.7.5.13. Entity Status Inquiry Response (ESIRsp) + + The ESIRsp message type is 0x800D. This message is sent by an iSNS + client and provides confirmation that the ESI message was received + and processed. + + The ESIRsp response message PDU Payload contains the attributes from + the original ESI message. These attributes represent the Portal that + is responding to the ESI. The ESIRsp Attributes are in the order + they were provided in the original ESI message. + + Upon receiving the ESIRsp from the iSNS client, the iSNS server SHALL + update the timestamp attribute for that Network Entity and Portal. + + + + + +Tseng, et al. Standards Track [Page 71] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +5.7.5.14. Request FC_DOMAIN_ID Response (RqstDomIdRsp) + + The RqstDomIdRsp message type is 0x8011. This message provides the + response for RqstDomId. + + The RqstDomId response contains a Status Code and the TLV attribute + Assigned ID, which contains the integer value in the space requested. + If no further unallocated values are available from this space, the + iSNS server SHALL respond with the Status Code 19 "FC_DOMAIN_ID Not + Available". + + Once a FC_DOMAIN_ID value is allocated by the iSNS server, it SHALL + NOT be reused until it has been deallocated by the iSNS client to + which the value was assigned, or until the ESI message detects that + the iSNS client no longer exists on the network. + + The iSNS server and client SHALL use TCP to transmit and receive + RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages. + +5.7.5.15. Release FC_DOMAIN_ID Response (RlseDomIdRsp) + + The RlseDomIdRsp message type is 0x8012. This message provides the + response for RlseDomId. The response contains an Error indicating + whether the request was successful. If the Assigned_ID value in the + original RlseDomId message is not allocated, then the iSNS server + SHALL respond with this message using the Status Code 20 + "FC_DOMAIN_ID Not Allocated". + + The iSNS server and client SHALL use TCP to transmit and receive + RqstDomId, RqstDomIdRsp, RlseDomId, and RlseDomIdRsp messages. + +5.7.5.16. Get FC_DOMAIN_IDs Response (GetDomIdRsp) + + The GetDomIdRsp message type is 0x8013. This message is used to + determine which FC_DOMAIN_ID values have been allocated for the + Virtual_Fabric_ID specified in the original GetDomId request message. + + The GetDomId response message PDU Payload contains a Status Code + indicating whether the request was successful, and a list of the + Assigned IDs from the space requested. The Assigned_ID attributes + are listed in TLV format. + +5.8. Vendor-Specific Messages + + Vendor-specific iSNSP messages have a functional ID of between 0x0100 + and 0x01FF, whereas vendor-specific responses have a functional ID of + between 0x8100 and 0x81FF. The first Message Key Attribute in a + + + + +Tseng, et al. Standards Track [Page 72] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + vendor-specific message SHALL be the company OUI (tag=256) + identifying the original creator of the proprietary iSNSP message. + The contents of the remainder of the message are vendor-specific. + +6. iSNS Attributes + + Attributes can be stored in the iSNS server using iSNSP registration + messages, and they can be retrieved using iSNSP query messages. + Unless otherwise indicated, these attributes are supplied by iSNS + clients using iSNSP registration messages. + +6.1. iSNS Attribute Summary + + The complete registry of iSNS attributes is maintained by IANA, and + the following table summarizes the initial set of iSNS attributes + available at the time of publication of this document. + + Attributes Length Tag Reg Key Query Key + ---------- ------ --- ------- --------- + Delimiter 0 0 N/A N/A + Entity Identifier (EID) 4-256 1 1 1|2|16&17|32|64 + Entity Protocol 4 2 1 1|2|16&17|32|64 + Management IP Address 16 3 1 1|2|16&17|32|64 + Timestamp 8 4 -- 1|2|16&17|32|64 + Protocol Version Range 4 5 1 1|2|16&17|32|64 + Registration Period 4 6 1 1|2|16&17|32|64 + Entity Index 4 7 1 1|2|16&17|32|64 + Entity Next Index 4 8 -- 1|2|16&17|32|64 + Entity ISAKMP Phase-1 var 11 1 1|2|16&17|32|64 + Entity Certificate var 12 1 1|2|16&17|32|64 + Portal IP Address 16 16 1 1|16&17|32|64 + Portal TCP/UDP Port 4 17 1 1|16&17|32|64 + Portal Symbolic Name 4-256 18 16&17 1|16&17|32|64 + ESI Interval 4 19 16&17 1|16&17|32|64 + ESI Port 4 20 16&17 1|16&17|32|64 + Portal Index 4 22 16&17 1|16&17|32|64 + SCN Port 4 23 16&17 1|16&17|32|64 + Portal Next Index 4 24 -- 1|16&17|32|64 + Portal Security Bitmap 4 27 16&17 1|16&17|32|64 + Portal ISAKMP Phase-1 var 28 16&17 1|16&17|32|64 + Portal ISAKMP Phase-2 var 29 16&17 1|16&17|32|64 + Portal Certificate var 31 16&17 1|16&17|32|64 + iSCSI Name 4-224 32 1 1|16&17|32|33 + iSCSI Node Type 4 33 32 1|16&17|32 + iSCSI Alias 4-256 34 32 1|16&17|32 + iSCSI SCN Bitmap 4 35 32 1|16&17|32 + iSCSI Node Index 4 36 32 1|16&17|32 + WWNN Token 8 37 32 1|16&17|32 + + + +Tseng, et al. Standards Track [Page 73] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + iSCSI Node Next Index 4 38 -- 1|16&17|32 + iSCSI AuthMethod var 42 32 1|16&17|32 + PG iSCSI Name 4-224 48 32|16&17 1|16&17|32|52 + PG Portal IP Addr 16 49 32|16&17 1|16&17|32|52 + PG Portal TCP/UDP Port 4 50 32|16&17 1|16&17|32|52 + PG Tag (PGT) 4 51 32|16&17 1|16&17|32|52 + PG Index 4 52 32|16&17 1|16&17|32|52 + PG Next Index 4 53 -- 1|16&17|32|52 + FC Port Name WWPN 8 64 1 1|16&17|64|66|96|128 + Port ID 4 65 64 1|16&17|64 + FC Port Type 4 66 64 1|16&17|64 + Symbolic Port Name 4-256 67 64 1|16&17|64 + Fabric Port Name 8 68 64 1|16&17|64 + Hard Address 4 69 64 1|16&17|64 + Port IP-Address 16 70 64 1|16&17|64 + Class of Service 4 71 64 1|16&17|64 + FC-4 Types 32 72 64 1|16&17|64 + FC-4 Descriptor 4-256 73 64 1|16&17|64 + FC-4 Features 128 74 64 1|16&17|64 + iFCP SCN bitmap 4 75 64 1|16&17|64 + Port Role 4 76 64 1|16&17|64 + Permanent Port Name 8 77 -- 1|16&17|64 + FC-4 Type Code 4 95 -- 1|16&17|64 + FC Node Name WWNN 8 96 64 1|16&17|64|96 + Symbolic Node Name 4-256 97 96 64|96 + Node IP-Address 16 98 96 64|96 + Node IPA 8 99 96 64|96 + Proxy iSCSI Name 4-256 101 96 64|96 + Switch Name 8 128 128 128 + Preferred ID 4 129 128 128 + Assigned ID 4 130 128 128 + Virtual_Fabric_ID 4-256 131 128 128 + iSNS Server Vendor OUI 4 256 -- SOURCE Attribute + Vendor-Spec iSNS Srvr 257-384 -- SOURCE Attribute + Vendor-Spec Entity 385-512 1 1|2|16&17|32|64 + Vendor-Spec Portal 513-640 16&17 1|16&17|32|64 + Vendor-Spec iSCSI Node 641-768 32 16&17|32 + Vendor-Spec FC Port Name 769-896 64 1|16&17|64 + Vendor-Spec FC Node Name 897-1024 96 64|96 + Vendor-Specific DDS 1025-1280 2049 2049 + Vendor-Specific DD 1281-1536 2065 2065 + Other Vendor-Specific 1537-2048 + DD_Set ID 4 2049 2049 1|32|64|2049|2065 + DD_Set Sym Name 4-256 2050 2049 2049 + DD_Set Status 4 2051 2049 2049 + DD_Set_Next_ID 4 2052 -- 2049 + DD_ID 4 2065 2049 1|32|64|2049|2065 + DD_Symbolic Name 4-256 2066 2065 2065 + + + +Tseng, et al. Standards Track [Page 74] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + DD_Member iSCSI Index 4 2067 2065 2065 + DD_Member iSCSI Name 4-224 2068 2065 2065 + DD_Member FC Port Name 8 2069 2065 2065 + DD_Member Portal Index 4 2070 2065 2065 + DD_Member Portal IP Addr 16 2071 2065 2065 + DD_Member Portal TCP/UDP 4 2072 2065 2065 + DD_Features 4 2078 2065 2065 + DD_ID Next ID 4 2079 -- 2065 + + The following are descriptions of the columns used in the above + table: + + Length: indicates the attribute length in bytes used for the TLV + format. Variable-length identifiers are NULL-terminated + and 4-byte aligned (NULLs are included in the length). + + Tag: the IANA-assigned integer tag value used to identify the + attribute. All undefined tag values are reserved. + + Reg Key: indicates the tag values for the object key in DevAttrReg + messages for registering a new attribute value in the + database. These tags represent attributes defined as + object keys in Section 4. + + Query Key: indicates the possible tag values for the Message Key and + object key that are used in the DevAttrQry messages for + retrieving a stored value from the iSNS database. + + The following is a summary of iSNS attribute tag values available for + future allocation by IANA at the time of publication: + + Tag Values Reg Key Query Key + ---------- ------- --------- + 9-10, 13-15 1 1|2|16&17|32|64 + 21, 25-26, 30 16&17 1|16&17|32|64 + 39-41, 44-47 32 1|16&17|32 + 54-63 32|16&17 1|16&17|32|52 + 78-82, 85-94 64 1|16&17|64 + 102-127 96 64|96 + 132-255 -- SOURCE Attribute + 2053-2064 2049 2049 + 2073-2077 2065 2065 + 2080-65535 To be assigned To be assigned + + Registration and query keys for attributes with tags in the range + 2080 to 65535 are to be documented in the RFC introducing the new + iSNS attributes. IANA will maintain registration of these values as + required by the new RFC. + + + +Tseng, et al. Standards Track [Page 75] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + New iSNS attributes with any of the above tag values MAY also be + designated as "read-only" attributes. The new RFC introducing these + attributes as "read-only" SHALL document them as such, and IANA will + record their corresponding Registration Keys (Reg Keys) as "--". + +6.2. Entity Identifier-Keyed Attributes + + The following attributes are stored in the iSNS server using the + Entity Identifier attribute as the key. + +6.2.1. Entity Identifier (EID) + + The Entity Identifier (EID) is variable-length UTF-8 encoded NULL- + terminated text-based description for a Network Entity. This key + attribute uniquely identifies each Network Entity registered in the + iSNS server. The attribute length varies from 4 to 256 bytes + (including the NULL termination), and is a unique value within the + iSNS server. + + If the iSNS client does not provide an EID during registration, the + iSNS server SHALL generate one that is unique within the iSNS + database. If an EID is to be generated, then the EID attribute value + in the registration message SHALL be empty (0 length). The generated + EID SHALL be returned in the registration response. + + In environments where the iSNS server is integrated with a DNS + infrastructure, the Entity Identifier may be used to store the Fully + Qualified Domain Name (FQDN) of the iSCSI or iFCP device. FQDNs of + greater than 255 bytes MUST NOT be used. + + If FQDNs are not used, the iSNS server can be used to generate EIDs. + EIDs generated by the iSNS server MUST begin with the string "isns:". + iSNS clients MUST NOT generate and register EIDs beginning with the + string "isns:". + + This field MUST be normalized according to the nameprep template + [NAMEPREP] before it is stored in the iSNS database. + +6.2.2. Entity Protocol + + The Entity Protocol is a required 4-byte integer attribute that + indicates the block storage protocol used by the registered NETWORK + ENTITY. Values used for this attribute are assigned and maintained + by IANA. The initial set of protocols supported by iSNS is as + follows: + + + + + + +Tseng, et al. Standards Track [Page 76] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Value Entity Protocol Type + ----- -------------------- + 1 No Protocol + 2 iSCSI + 3 iFCP + All others To be assigned by IANA + + 'No Protocol' is used to indicate that the Network Entity does not + support an IP block storage protocol. A Control Node or monitoring + Node would likely (but not necessarily) use this value. + + This attribute is required during initial registration of the Network + Entity. + +6.2.3. Management IP Address + + This field contains the IP Address that may be used to manage the + Network Entity and all Storage Nodes contained therein via the iSNS + MIB [iSNSMIB]. Some implementations may also use this IP address to + support vendor-specific proprietary management protocols. The + Management IP Address is a 16-byte field that may contain an IPv4 or + IPv6 address. When this field contains an IPv4 value, it is stored + as an IPv4-mapped IPv6 address. That is, the most significant 10 + bytes are set to 0x00, with the next two bytes set to 0xFFFF + [RFC2373]. When this field contains an IPv6 value, the entire 16- + byte field is used. If this field is not set, then in-band + management through the IP address of one of the Portals of the + Network Entity is assumed. + +6.2.4. Entity Registration Timestamp + + This field indicates the most recent time when the Network Entity + registration occurred or when an associated object attribute was + updated or queried by the iSNS client registering the Network Entity. + The time format is, in seconds, the update period since the standard + base time of 00:00:00 GMT on January 1, 1970. This field cannot be + explicitly registered. This timestamp TLV format is also used in the + SCN and ESI messages. + +6.2.5. Protocol Version Range + + This field contains the minimum and maximum version of the block + storage protocol supported by the Network Entity. The most + significant two bytes contain the maximum version supported, and the + least significant two bytes contain the minimum version supported. + If a range is not registered, then the Network Entity is assumed to + + + + + +Tseng, et al. Standards Track [Page 77] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + support all versions of the protocol. The value 0xffff is a wildcard + that indicates no minimum or maximum. If the Network Entity does not + support a protocol, then this field SHALL be set to 0. + +6.2.6. Registration Period + + This 4-byte unsigned integer field indicates the maximum period, in + seconds, that the registration SHALL be maintained by the server + without receipt of an iSNS message from the iSNS client that + registered the Network Entity. Entities that are not registered for + ESI monitoring MUST have a non-zero Registration Period. If a + Registration Period is not requested by the iSNS client and Entity + Status Inquiry (ESI) messages are not enabled for that client, then + the Registration Period SHALL be set to a non-zero value by the iSNS + server. This implementation-specific value for the Registration + Period SHALL be returned in the registration response to the iSNS + client. The Registration Period may be set to zero, indicating its + non-use, only if ESI messages are enabled for that Network Entity. + + The registration SHALL be removed from the iSNS database if an iSNS + Protocol message is not received from the iSNS client before the + registration period has expired. Receipt of any iSNS Protocol + message from the iSNS client automatically refreshes the Entity + Registration Period and Entity Registration Timestamp. To prevent a + registration from expiring, the iSNS client should send an iSNS + Protocol message to the iSNS server at intervals shorter than the + registration period. Such a message can be as simple as a query for + one of its own attributes, using its associated iSCSI Name or FC Port + Name WWPN as the Source attribute. + + For an iSNS client that is supporting a Network Entity with multiple + Storage Node objects, receipt of an iSNS message from any Storage + Node of that Network Entity is sufficient to refresh the registration + for all Storage Node objects of the Network Entity. + + If ESI support is requested as part of a Portal registration, the ESI + Response message received from the iSNS client by the iSNS server + SHALL refresh the registration. + +6.2.7. Entity Index + + The Entity Index is an unsigned non-zero integer value that uniquely + identifies each Network Entity registered in the iSNS server. Upon + initial registration of a Network Entity, the iSNS server assigns an + unused value for the Entity Index. Each Network Entity in the iSNS + database MUST be assigned a value for the Entity Index that is not + + + + + +Tseng, et al. Standards Track [Page 78] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + assigned to any other Network Entity. Furthermore, Entity Index + values for recently deregistered Network Entities SHOULD NOT be + reused in the short term. + + The Entity Index MAY be used to represent the Network Entity in + situations when the Entity Identifier is too long or otherwise + inappropriate. An example of this is when SNMP is used for + management, as described in Section 2.10. + +6.2.8. Entity Next Index + + This is a virtual attribute containing a 4-byte integer value that + indicates the next available (i.e., unused) Entity Index value. This + attribute may only be queried; the iSNS server SHALL return an error + code of 3 (Invalid Registration) to any client that attempts to + register a value for this attribute. A Message Key is not required + when exclusively querying for this attribute. + + The Entity Next Index MAY be used by an SNMP client to create an + entry in the iSNS server. SNMP requirements are described in Section + 2.10. + +6.2.9. Entity ISAKMP Phase-1 Proposals + + This field contains the IKE Phase-1 proposal, listing in decreasing + order of preference the protection suites acceptable to protect all + IKE Phase-2 messages sent and received by the Network Entity. This + includes Phase-2 SAs from the iSNS client to the iSNS server as well + as to peer iFCP and/or iSCSI devices. This attribute contains the SA + payload, proposal payload(s), and transform payload(s) in the ISAKMP + format defined in [RFC2408]. + + This field should be used if the implementer wishes to define a + single phase-1 SA security configuration used to protect all phase-2 + IKE traffic. If the implementer desires to have a different phase-1 + SA security configuration to protect each Portal interface, then the + Portal Phase-1 Proposal (Section 6.3.10) should be used. + +6.2.10. Entity Certificate + + This attribute contains one or more X.509 certificates that are bound + to the Network Entity. This certificate is uploaded and registered + to the iSNS server by clients wishing to allow other clients to + authenticate themselves and to access the services offered by that + Network Entity. The format of the X.509 certificate is found in + [RFC3280]. This certificate MUST contain a Subject Name with an + empty sequence and MUST contain a SubjectAltName extension encoded + + + + +Tseng, et al. Standards Track [Page 79] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + with the dNSName type. The Entity Identifier (Section 6.2.1) of the + identified Entity MUST be stored in the SubjectAltName field of the + certificate. + +6.3. Portal-Keyed Attributes + + The following Portal attributes are registered in the iSNS database + using the combined Portal IP-Address and Portal TCP/UDP Port as the + key. Each Portal is associated with one Entity Identifier object + key. + +6.3.1. Portal IP Address + + This attribute is the IP address of the Portal through which a + Storage Node can transmit and receive storage data. The Portal IP + Address is a 16-byte field that may contain an IPv4 or IPv6 address. + When this field contains an IPv4 address, it is stored as an IPv4- + mapped IPv6 address. That is, the most significant 10 bytes are set + to 0x00, with the next 2 bytes set to 0xFFFF [RFC2373]. When this + field contains an IPv6 address, the entire 16-byte field is used. + The Portal IP Address and the Portal TCP/UDP Port number (see 6.3.2 + below) are used as a key to identify a Portal uniquely. It is a + required attribute for registration of a Portal. + +6.3.2. Portal TCP/UDP Port + + The TCP/UDP port of the Portal through which a Storage Node can + transmit and receive storage data. Bits 16 to 31 represents the + TCP/UDP port number. Bit 15 represents the port type. If bit 15 is + set, then the port type is UDP. Otherwise it is TCP. Bits 0 to 14 + are reserved. + + If the field value is 0, then the port number is the implied + canonical port number and type of the protocol indicated by the + associated Entity Type. + + The Portal IP Address and the Portal TCP/UDP Port number are used as + a key to identify a Portal uniquely. It is a required attribute for + registration of a Portal. + +6.3.3. Portal Symbolic Name + + A variable-length UTF-8 encoded NULL-terminated text-based + description of up to 256 bytes. The Portal Symbolic Name is a user- + readable description of the Portal entry in the iSNS server. + + + + + + +Tseng, et al. Standards Track [Page 80] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.3.4. Entity Status Inquiry Interval + + This field indicates the requested time, in seconds, between Entity + Status Inquiry (ESI) messages sent from the iSNS server to this + Network Entity. ESI messages can be used to verify that a Portal + registration continues to be valid. To request monitoring by the + iSNS server, an iSNS client registers a non-zero value for this + Portal attribute using a DevAttrReg message. The client MUST + register an ESI Port on at least one of its Portals to receive the + ESI monitoring. + + If the iSNS server does not receive an expected response to an ESI + message, it SHALL attempt an administratively configured number of + re-transmissions of the ESI message. The ESI Interval period begins + with the iSNS server's receipt of the last ESI Response. All re- + transmissions MUST be sent before twice the ESI Interval period has + passed. If no response is received from any of the ESI messages, + then the Portal SHALL be deregistered. Note that only Portals that + have registered a value in their ESI Port field can be deregistered + in this way. + + If all Portals associated with a Network Entity that have registered + for ESI messages are deregistered due to non-response, and if no + registrations have been received from the client for at least two ESI + Interval periods, then the Network Entity and all associated objects + (including Storage Nodes) SHALL be deregistered. + + If the iSNS server is unable to support ESI messages or the ESI + Interval requested, it SHALL either reject the ESI request by + returning an "ESI Not Available" Status Code or modify the ESI + Interval attribute by selecting its own suitable value and returning + that value in the Operating Attributes of the registration response + message. + + If at any time an iSNS client that is registered for ESI messages has + not received an ESI message to any of its Portals as expected, then + the client MAY attempt to query the iSNS server using a DevAttrQry + message using its Entity_ID as the key. If the query result is the + error "no such entry", then the client SHALL close all remaining TCP + connections to the iSNS server and assume that it is no longer + registered in the iSNS database. Such a client MAY attempt re- + registration. + + + + + + + + + +Tseng, et al. Standards Track [Page 81] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.3.5. ESI Port + + This field contains the TCP or UDP port used for ESI monitoring by + the iSNS server at the Portal IP Address. Bits 16 to 31 represent + the port number. If bit 15 is set, then the port type is UDP. + Otherwise, the port is TCP. Bits 0 to 14 are reserved. + + If the iSNS client registers a valid TCP or UDP port number in this + field, then the client SHALL allow ESI messages to be received at the + indicated TCP or UDP port. If a TCP port is registered and a pre- + existing TCP connection from that TCP port to the iSNS server does + not already exist, then the iSNS client SHALL accept new TCP + connections from the iSNS server at the indicated TCP port. + + The iSNS server SHALL return an error if a Network Entity is + registered for ESI monitoring and none of the Portals of that Network + Entity has an entry for the ESI Port field. If multiple Portals have + a registered ESI port, then the ESI message may be delivered to any + one of the indicated Portals. + +6.3.6. Portal Index + + The Portal Index is a 4-byte non-zero integer value that uniquely + identifies each Portal registered in the iSNS database. Upon initial + registration of a Portal, the iSNS server assigns an unused value for + the Portal Index of that Portal. Each Portal in the iSNS database + MUST be assigned a value for the Portal Index that is not assigned to + any other Portal. Furthermore, Portal Index values for recently + deregistered Portals SHOULD NOT be reused in the short term. + + The Portal Index MAY be used to represent a registered Portal in + situations where the Portal IP-Address and Portal TCP/UDP Port is + unwieldy to use. An example of this is when SNMP is used for + management, as described in Section 2.10. + +6.3.7. SCN Port + + This field contains the TCP or UDP port used by the iSNS client to + receive SCN messages from the iSNS server. When a value is + registered for this attribute, an SCN message may be received on the + indicated port for any of the Storage Nodes supported by the Portal. + Bits 16 to 31 contain the port number. If bit 15 is set, then the + port type is UDP. Otherwise, the port type is TCP. Bits 0 to 14 are + reserved. + + If the iSNS client registers a valid TCP or UDP port number in this + field, then the client SHALL allow SCN messages to be received at the + indicated TCP or UDP port. If a TCP port is registered and a pre- + + + +Tseng, et al. Standards Track [Page 82] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + existing TCP connection from that TCP port to the iSNS server does + not already exist, then the iSNS client SHALL accept new TCP + connections from the iSNS server at the indicated TCP port. + + The iSNS server SHALL return an error if an SCN registration message + is received and none of the Portals of the Network Entity has an + entry for the SCN Port. If multiple Portals have a registered SCN + Port, then the SCN SHALL be delivered to any one of the indicated + Portals of that Network Entity. + +6.3.8. Portal Next Index + + This is a virtual attribute containing a 4-byte integer value that + indicates the next available (i.e., unused) Portal Index value. This + attribute may only be queried; the iSNS server SHALL return an error + code of 3 (Invalid Registration) to any client that attempts to + register a value for this attribute. A Message Key is not required + when exclusively querying for this attribute. + + The Portal Next Index MAY be used by an SNMP client to create an + entry in the iSNS server. SNMP requirements are described in Section + 2.10. + +6.3.9. Portal Security Bitmap + + This 4-byte field contains flags that indicate security attribute + settings for the Portal. Bit 31 (Lsb) of this field must be 1 + (enabled) for this field to contain significant information. If Bit + 31 is enabled, this signifies that the iSNS server can be used to + store and distribute security policies and settings for iSNS clients + (i.e., iSCSI devices). Bit 30 must be 1 for bits 25-29 to contain + significant information. All other bits are reserved for non- + IKE/IPSec security mechanisms to be specified in the future. + + Bit Position Flag Description + ------------ ---------------- + 25 1 = Tunnel Mode Preferred; 0 = No Preference + 26 1 = Transport Mode Preferred; 0 = No Preference + 27 1 = Perfect Forward Secrecy (PFS) Enabled; + 0 = PFS Disabled + 28 1 = Aggressive Mode Enabled; 0 = Disabled + 29 1 = Main Mode Enabled; 0 = MM Disabled + 30 1 = IKE/IPSec Enabled; 0 = IKE/IPSec Disabled + 31 (Lsb) 1 = Bitmap VALID; 0 = INVALID + All others RESERVED + + + + + + +Tseng, et al. Standards Track [Page 83] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.3.10. Portal ISAKMP Phase-1 Proposals + + This field contains the IKE Phase-1 proposal listing in decreasing + order of preference of the protection suites acceptable to protect + all IKE Phase-2 messages sent and received by the Portal. This + includes Phase-2 SAs from the iSNS client to the iSNS server as well + as to peer iFCP and/or iSCSI devices. This attribute contains the SA + payload, proposal payload(s), and transform payload(s) in the ISAKMP + format defined in [RFC2408]. + + This field should be used if the implementer wishes to define phase-1 + SA security configuration on a per-Portal basis, as opposed to on a + per-Network Entity basis. If the implementer desires to have a + single phase-1 SA security configuration to protect all phase-2 + traffic regardless of the interface used, then the Entity Phase-1 + Proposal (Section 6.2.9) should be used. + +6.3.11. Portal ISAKMP Phase-2 Proposals + + This field contains the IKE Phase-2 proposal, in ISAKMP format + [RFC2408], listing in decreasing order of preference the security + proposals acceptable to protect traffic sent and received by the + Portal. This field is used only if bits 31, 30, and 29 of the + + Security Bitmap (see 6.3.9) are enabled. This attribute contains the + SA payload, proposal payload(s), and associated transform payload(s) + in the ISAKMP format defined in [RFC2408]. + +6.3.12. Portal Certificate + + This attribute contains one or more X.509 certificates that are a + credential of the Portal. This certificate is used to identify and + authenticate communications to the IP address and TCP/UDP Port + supported by the Portal. The format of the X.509 certificate is + specified in [RFC3280]. This certificate MUST contain a Subject Name + with an empty sequence and MUST contain a SubjectAltName extension + encoded with the iPAddress type. The Portal IP Address (Section + 6.3.1) of the identified Portal SHALL be stored in the SubjectAltName + field of the certificate. + +6.4. iSCSI Node-Keyed Attributes + + The following attributes are stored in the iSNS database using the + iSCSI Name attribute as the key. Each set of Node-Keyed attributes + is associated with one Entity Identifier object key. + + Although the iSCSI Name key is associated with one Entity Identifier, + it is unique across the entire iSNS database. + + + +Tseng, et al. Standards Track [Page 84] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.4.1. iSCSI Name + + This is a variable-length UTF-8 encoded NULL-terminated text-based + description of up to 224 bytes. This key attribute is required for + iSCSI Storage Nodes and is provided by the iSNS client. The + registered iSCSI Name MUST conform to the format described in [iSCSI] + for iSCSI Names. The maximum size for an iSCSI Name is 223 bytes. + Including the NULL character and 4-byte alignment (see Section + 5.3.1), the maximum iSCSI Name field size is 224 bytes. + + If an iSCSI Name is registered without an EID key, then a Network + Entity SHALL be created and an EID assigned. The assigned EID SHALL + be returned in the registration response as an operating attribute. + + This field MUST be normalized according to the stringprep template + [STRINGPREP] before it is stored in the iSNS database. + +6.4.2. iSCSI Node Type + + This required 32-bit field is a bitmap indicating the type of iSCSI + Storage Node. The bit positions are defined below. A set bit (1) + indicates that the Node has the corresponding characteristics. + + Bit Position Node Type + ------------ --------- + 29 Control + 30 Initiator + 31 (Lsb) Target + All others RESERVED + + If the Target bit is set to 1, then the Node represents an iSCSI + target. The Target bit MAY be set by iSNS clients using the iSNSP. + + If the Initiator bit is set to 1, then the Node represents an iSCSI + initiator. The Initiator bit MAY be set by iSNS clients using the + iSNSP. + + If the control bit is set to 1, then the Node represents a gateway, a + management station, a backup iSNS server, or another device that is + not an initiator or target, but that requires the ability to send and + receive iSNSP messages, including state change notifications. + Setting the control bit is an administrative task that MUST be + performed on the iSNS server; iSNS clients SHALL NOT be allowed to + change this bit using the iSNSP. + + + + + + + +Tseng, et al. Standards Track [Page 85] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + This field MAY be used by the iSNS server to distinguish among + permissions by different iSCSI Node types for accessing various iSNS + functions. More than one Node Type bit may be simultaneously + enabled. + +6.4.3. iSCSI Node Alias + + This is a variable-length UTF-8 encoded NULL-terminated text-based + description of up to 256 bytes. The Alias is a user-readable + description of the Node entry in the iSNS database. + +6.4.4. iSCSI Node SCN Bitmap + + The iSCSI Node SCN Bitmap indicates events for which the registering + iSNS client wishes to receive a notification message. The following + table displays events that result in notifications, and the bit field + in the SCN Bitmap that, when enabled, results in the corresponding + notification. + + Note that this field is of dual use: it is used in the SCN + registration process to define interested events that will trigger an + SCN message, and it is also contained in each SCN message itself, to + indicate the type of event that triggered the SCN message. A set bit + (1) indicates the corresponding type of SCN. + + Bit Position Flag Description + ------------ ---------------- + 24 INITIATOR AND SELF INFORMATION ONLY + 25 TARGET AND SELF INFORMATION ONLY + 26 MANAGEMENT REGISTRATION/SCN + 27 OBJECT REMOVED + 28 OBJECT ADDED + 29 OBJECT UPDATED + 30 DD/DDS MEMBER REMOVED (Mgmt Reg/SCN only) + 31 (Lsb) DD/DDS MEMBER ADDED (Mgmt Reg/SCN only) + All others RESERVED + + DD/DDS MEMBER REMOVED indicates that an existing member of a + Discovery Domain and/or Discovery Domain Set has been removed. + + DD/DDS MEMBER ADDED indicates that a new member was added to an + existing DD and/or DDS. + + OBJECT REMOVED, OBJECT ADDED, and OBJECT UPDATED indicate a Network + Entity, Portal, Storage Node, FC Device, DD, and/or DDS object was + removed from, added to, or updated in the Discovery Domain or in the + iSNS database (Control Nodes only). + + + + +Tseng, et al. Standards Track [Page 86] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Regular SCNs provide information about objects that are updated in, + added to or removed from Discovery Domains of which the Storage Node + is a member. An SCN or SCN registration is considered a regular SCN + or regular SCN registration if the MANAGEMENT REGISTRATION/SCN flag + is cleared. All iSNS clients may register for regular SCNs. + + Management SCNs provide information about all changes to the network, + regardless of discovery domain membership. Registration for + management SCNs is indicated by setting bit 26 to 1. Only Control + Nodes may register for management SCNs. Bits 30 and 31 may only be + enabled if bit 26 is set to 1. + + TARGET AND SELF INFORMATION ONLY SCNs (bit 25) provides information + only about changes to target devices, or if the iSCSI Storage Node + itself has undergone a change. Similarly, INITIATOR AND SELF + INFORMATION ONLY SCNs (bit 24) provides information only about + changes to initiator Nodes, or to the target itself. + +6.4.5. iSCSI Node Index + + The iSCSI Node Index is a 4-byte non-zero integer value used as a key + that uniquely identifies each iSCSI Storage Node registered in the + iSNS database. Upon initial registration of the iSCSI Storage Node, + the iSNS server assigns an unused value for the iSCSI Node Index. + Each iSCSI Node MUST be assigned a value for the iSCSI Node Index + that is not assigned to any other iSCSI Storage Node. Furthermore, + iSCSI Node Index values for recently deregistered iSCSI Storage Nodes + SHOULD NOT be reused in the short term. + + The iSCSI Node Index may be used as a key to represent a registered + Node in situations where the iSCSI Name is too long to be used as a + key. An example of this is when SNMP is used for management, as + described in Section 2.10. + + The value assigned for the iSCSI Node Index SHALL persist as long as + the iSCSI Storage Node is registered in the iSNS database or a member + of a Discovery Domain. An iSCSI Node Index value that is assigned + for a Storage Node SHALL NOT be used for any other Storage Node as + long as the original node is registered in the iSNS database or a + member of a Discovery Domain. + +6.4.6. WWNN Token + + This field contains a globally unique 64-bit integer value that can + be used to represent the World Wide Node Name of the iSCSI device in + a Fibre Channel fabric. This identifier is used during the device + registration process and MUST conform to the requirements in [FC-FS]. + + + + +Tseng, et al. Standards Track [Page 87] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + The FC-iSCSI gateway uses the value found in this field to register + the iSCSI device in the Fibre Channel name server. It is stored in + the iSNS server to prevent conflict when "proxy" WWNN values are + assigned to iSCSI initiators establishing storage sessions to devices + in the FC fabric. + + If the iSNS client does not assign a value for WWNN Token, then the + iSNS server SHALL provide a value for this field upon initial + registration of the iSCSI Storage Node. The process by which the + WWNN Token is assigned by the iSNS server MUST conform to the + following requirements: + + 1. The assigned WWNN Token value MUST be unique among all WWN + entries in the existing iSNS database, and among all devices that + can potentially be registered in the iSNS database. + + 2. Once the value is assigned, the iSNS server MUST persistently + save the mapping between the WWNN Token value and registered + iSCSI Name. That is, successive re-registrations of the iSCSI + Storage Node keyed by the same iSCSI Name maintain the original + mapping to the associated WWNN Token value in the iSNS server. + Similarly, the mapping SHALL be persistent across iSNS server + reboots. Once assigned, the mapping can only be changed if a + DevAttrReg message from an authorized iSNS client explicitly + provides a different WWNN Token value. + + 3. Once a WWNN Token value has been assigned and mapped to an iSCSI + name, that WWNN Token value SHALL NOT be reused or mapped to any + other iSCSI name. + + 4. The assigned WWNN Token value MUST conform to the formatting + requirements of [FC-FS] for World Wide Names (WWNs). + + An iSNS client, such as an FC-iSCSI gateway or the iSCSI initiator, + MAY register its own WWNN Token value or overwrite the iSNS Server- + supplied WWNN Token value, if it wishes to supply its own iSCSI-FC + name mapping. This is accomplished using the DevAttrReg message with + the WWNN Token (tag=37) as an operating attribute. Once overwritten, + the new WWNN Token value MUST be stored and saved by the iSNS server, + and all requirements specified above continue to apply. If an iSNS + client attempts to register a value for this field that is not unique + in the iSNS database or that is otherwise invalid, then the + registration SHALL be rejected with an Status Code of 3 (Invalid + Registration). + + + + + + + +Tseng, et al. Standards Track [Page 88] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + There MAY be matching records in the iSNS database for the Fibre + Channel device specified by the WWNN Token. These records may + contain device attributes for that FC device registered in the Fibre + Channel fabric name server. + +6.4.7. iSCSI Node Next Index + + This is a virtual attribute containing a 4-byte integer value that + indicates the next available (i.e., unused) iSCSI Node Index value. + This attribute may only be queried; the iSNS server SHALL return an + error code of 3 (Invalid Registration) to any client that attempts to + register a value for this attribute. A Message Key is not required + when exclusively querying for this attribute. + + The iSCSI Node Next Index MAY be used by an SNMP client to create an + entry in the iSNS server. SNMP requirements are described in Section + 2.10. + +6.4.8. iSCSI AuthMethod + + This attribute contains a NULL-terminated string of UTF-8 text + listing the iSCSI authentication methods enabled for this iSCSI + Storage Node, in order of preference. The text values used to + identify iSCSI authentication methods are embedded in this string + attribute and delineated by a comma. The text values are identical + to those found in the main iSCSI document [iSCSI]; additional + vendor-specific text values are also possible. + + Text Value Description Reference + ---------- ----------- --------- + KB5 Kerberos V5 [RFC1510] + SPKM1 Simple Public Key GSS-API [RFC2025] + SPKM2 Simple Public Key GSS-API [RFC2025] + SRP Secure Remote Password [RFC2945] + CHAP Challenge Handshake Protocol [RFC1994] + none No iSCSI Authentication + +6.5. Portal Group (PG) Object-Keyed Attributes + + The following attributes are used to associate Portal and iSCSI + Storage Node objects. PG objects are stored in the iSNS database + using the PG iSCSI Name, the PG Portal IP Address, and the PG Portal + TCP/UDP Port as keys. New PG objects are implicitly or explicitly + created at the time that the corresponding Portal and/or iSCSI + Storage Node objects are registered. Section 3.4 has a general + discussion of PG usage. For further details on use of Portal Groups, + see [iSCSI]. + + + + +Tseng, et al. Standards Track [Page 89] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.5.1. Portal Group iSCSI Name + + This is the iSCSI Name for the iSCSI Storage Node that is associated + with the PG object. This name MAY represent an iSCSI Storage Node + not currently registered in the server. + +6.5.2. PG Portal IP Addr + + This is the Portal IP Address attribute for the Portal that is + associated with the PG object. This Portal IP Address MAY be that of + a Portal that is not currently registered in the server. + +6.5.3. PG Portal TCP/UDP Port + + This is the Portal TCP/UDP Port attribute for the Portal that is + associated with the PG object. This Portal TCP/UDP Port MAY be that + of a Portal that is not currently registered in the server. + +6.5.4. Portal Group Tag (PGT) + + This field is used to group Portals in order to coordinate + connections in a session across Portals to a specified iSCSI Node. + The PGT is a value in the range of 0-65535, or NULL. A NULL PGT + value is registered by using 0 for the length in the TLV during + registration. The two least significant bytes of the value contain + the PGT for the object. The two most significant bytes are reserved. + If a PGT value is not explicitly registered for an iSCSI Storage Node + and Portal pair, then the PGT value SHALL be implicitly registered as + 0x00000001. + +6.5.5. Portal Group Index + + The PG Index is a 4-byte non-zero integer value used as a key that + uniquely identifies each PG object registered in the iSNS database. + Upon initial registration of a PG object, the iSNS server MUST assign + an unused value for the PG Index. Furthermore, PG Index values for + recently deregistered PG objects SHOULD NOT be reused in the short + term. + + The PG Index MAY be used as the key to reference a registered PG in + situations where a unique index for each PG object is required. It + MAY also be used as the message key in an iSNS message to query or + update a pre-existing PG object. An example of this is when SNMP is + used for management, as described in Section 2.10. The value + assigned for the PG Index SHALL persist as long as the server is + active. + + + + + +Tseng, et al. Standards Track [Page 90] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.5.6. Portal Group Next Index + + The PG Next Index is a virtual attribute containing a 4-byte integer + value that indicates the next available (i.e., unused) PG Index + value. This attribute may only be queried; the iSNS server SHALL + return an error code of 3 (Invalid Registration) to any client that + attempts to register a value for this attribute. A Message Key is + not required when exclusively querying for this attribute. + + The Portal Group Next Index MAY be used by an SNMP client to create + an entry in the iSNS server. SNMP requirements are described in + Section 2.10. + +6.6. FC Port Name-Keyed Attributes + + The following attributes are registered in the iSNS database using + the FC Port World Wide Name (WWPN) attribute as the key. Each set of + FC Port-Keyed attributes is associated with one Entity Identifier + object key. + + Although the FC Port World Wide Name is associated with one Entity + Identifier, it is also globally unique. + +6.6.1. FC Port Name (WWPN) + + This 64-bit identifier uniquely defines the FC Port, and it is the + World Wide Port Name (WWPN) of the corresponding Fibre Channel + device. This attribute is the key for the iFCP Storage Node. This + globally unique identifier is used during the device registration + process, and it uses a value conforming to IEEE EUI-64 [EUI-64]. + +6.6.2. Port ID (FC_ID) + + The Port Identifier is a Fibre Channel address identifier assigned to + an N_Port or NL_Port during fabric login. The format of the Port + Identifier is defined in [FC-FS]. The least significant 3 bytes + contain this address identifier. The most significant byte is + RESERVED. + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 91] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.6.3. FC Port Type + + Indicates the type of FC port. Encoded values for this field are + listed in the following table: + + Type Description + ---- ----------- + 0x0000 Unidentified/Null Entry + 0x0001 Fibre Channel N_Port + 0x0002 Fibre Channel NL_Port + 0x0003 Fibre Channel F/NL_Port + 0x0004-0080 RESERVED + 0x0081 Fibre Channel F_Port + 0x0082 Fibre Channel FL_Port + 0x0083 RESERVED + 0x0084 Fibre Channel E_Port + 0x0085-00FF RESERVED + 0xFF11 RESERVED + 0xFF12 iFCP Port + 0xFF13-FFFF RESERVED + +6.6.4. Symbolic Port Name + + This is a variable-length UTF-8 encoded NULL-terminated text-based + description of up to 256 bytes that is associated with the iSNS- + registered FC Port Name in the network. + +6.6.5. Fabric Port Name (FWWN) + + This 64-bit identifier uniquely defines the fabric port. If the port + of the FC Device is attached to a Fibre Channel fabric port with a + registered Port Name, then that fabric Port Name SHALL be indicated + in this field. + +6.6.6. Hard Address + + This field is the requested hard address 24-bit NL Port Identifier, + included in the iSNSP for compatibility with Fibre Channel Arbitrated + Loop devices and topologies. The least significant 3 bytes of this + field contain the address. The most significant byte is RESERVED. + +6.6.7. Port IP Address + + The Fibre Channel IP address associated with the FC Port. When this + field contains an IPv4 value, it is stored as an IPv4-mapped IPv6 + address. That is, the most significant 10 bytes are set to 0x00, + with the next two bytes set to 0xFFFF [RFC2373]. When an IPv6 value + is contained in this field, then the entire 16-byte field is used. + + + +Tseng, et al. Standards Track [Page 92] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.6.8. Class of Service (COS) + + This 32-bit bit-map field indicates the Fibre Channel Class of + Service types that are supported by the registered port. In the + following table, a set bit (1) indicates a Class of Service + supported. + + Bit Position Description + ------------ ----------- + 29 Fibre Channel Class 2 Supported + 28 Fibre Channel Class 3 Supported + +6.6.9. FC-4 Types + + This 32-byte field indicates the FC-4 protocol types supported by the + associated port. This field can be used to support Fibre Channel + devices and is consistent with FC-GS-4. + +6.6.10. FC-4 Descriptor + + This is a variable-length UTF-8 encoded NULL-terminated text-based + description of up to 256 bytes that is associated with the iSNS- + registered device port in the network. This field can be used to + support Fibre Channel devices and is consistent with FC-GS-4. + +6.6.11. FC-4 Features + + This is a 128-byte array, 4 bits per type, for the FC-4 protocol + types supported by the associated port. This field can be used to + support Fibre Channel devices and is consistent with FC-GS-4. + +6.6.12. iFCP SCN Bitmap + + This field indicates the events the iSNS client is interested in. + These events can cause SCNs to be generated. SCNs provide + information about objects that are updated in, added to or removed + from Discovery Domains of which the source and destination are a + member. Management SCNs provide information about all changes to the + network. A set bit (1) indicates the type of SCN for the bitmap as + follows: + + + + + + + + + + + +Tseng, et al. Standards Track [Page 93] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Bit Position Flag Description + ------------ ---------------- + 24 INITIATOR AND SELF INFORMATION ONLY + 25 TARGET AND SELF INFORMATION ONLY + 26 MANAGEMENT REGISTRATION/SCN + 27 OBJECT REMOVED + 28 OBJECT ADDED + 29 OBJECT UPDATED + 30 DD/DDS MEMBER REMOVED (Mgmt Reg/SCN only) + 31 (Lsb) DD/DDS MEMBER ADDED (Mgmt Reg/SCN only) + All others RESERVED + + Further information on the use of the bit positions specified above + can be found in Section 6.4.4. + +6.6.13. Port Role + + This required 32-bit field is a bitmap indicating the type of iFCP + Storage Node. The bit fields are defined below. A set bit indicates + the Node has the corresponding characteristics. + + Bit Position Node Type + ------------ --------- + 29 Control + 30 FCP Initiator + 31 (Lsb) FCP Target + All Others RESERVED + + If the 'Target' bit is set to 1, then the port represents an FC + target. Setting of the 'Target' bit MAY be performed by iSNS clients + using the iSNSP. + + If the 'Initiator' bit is set to 1, then the port represents an FC + initiator. Setting of the 'Initiator' bit MAY be performed by iSNS + clients using the iSNSP. + + If the 'Control' bit is set to 1, then the port represents a gateway, + a management station, an iSNS backup server, or another device. + + This is usually a special device that is neither an initiator nor a + target, which requires the ability to send and receive iSNSP + messages, including state-change notifications. Setting the control + bit is an administrative task that MUST be administratively + configured on the iSNS server; iSNS clients SHALL NOT be allowed to + change this bit using the iSNSP. + + + + + + +Tseng, et al. Standards Track [Page 94] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + This field MAY be used by the iSNS server to distinguish among + permissions by different iSNS clients. For example, an iSNS server + implementation may be administratively configured to allow only + targets to receive ESIs, or to permit only Control Nodes to add, + modify, or delete discovery domains. + +6.6.14. Permanent Port Name (PPN) + + The Permanent Port Name can be used to support Fibre Channel devices + and is consistent with the PPN description in FC-GS-4 [FC-GS-4]. The + format of the PPN is identical to the FC Port Name WWPN attribute + format. + +6.7. Node-Keyed Attributes + + The following attributes are registered in the iSNS database using + the FC Node Name (WWNN) attribute as the key. Each set of FC Node- + Keyed attributes represents a single device and can be associated + with many FC Ports. + + The FC Node Name is unique across the entire iSNS database. + +6.7.1. FC Node Name (WWNN) + + The FC Node Name is a 64-bit identifier that is the World Wide Node + Name (WWNN) of the corresponding Fibre Channel device. This + attribute is the key for the FC Device. This globally unique + identifier is used during the device registration process, and it + uses a value conforming to IEEE EUI-64 [EUI-64]. + +6.7.2. Symbolic Node Name + + This is a variable-length UTF-8 encoded NULL-terminated text-based + description of up to 256 bytes that is associated with the iSNS- + registered FC Device in the network. + +6.7.3. Node IP Address + + This IP address is associated with the device Node in the network. + This field is included for compatibility with Fibre Channel. When + this field contains an IPv4 value, it is stored as an IPv4-mapped + IPv6 address. That is, the most significant 10 bytes are set to + 0x00, with the next two bytes set to 0xFFFF [RFC2373]. When an IPv6 + value is contained in this field, the entire 16-byte field is used. + + + + + + + +Tseng, et al. Standards Track [Page 95] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.7.4. Node IPA + + This field is the 8-byte Fibre Channel Initial Process Associator + (IPA) associated with the device Node in the network. The initial + process associator is used for communication between Fibre Channel + devices. + +6.7.5. Proxy iSCSI Name + + This is a variable-length UTF-8 encoded NULL-terminated text-based + field that contains the iSCSI Name used to represent the FC Node in + the IP network. It is used as a pointer to the matching iSCSI Name + entry in the iSNS server. Its value is usually registered by an FC- + iSCSI gateway connecting the IP network to the fabric containing the + FC device. + + Note that if this field is used, there SHOULD be a matching entry in + the iSNS database for the iSCSI device specified by the iSCSI name. + The database entry should include the full range of iSCSI attributes + needed for discovery and management of the "iSCSI proxy image" of the + FC device. + +6.8. Other Attributes + + The following are not attributes of the previously-defined objects. + +6.8.1. FC-4 Type Code + + This is a 4-byte field used to provide a FC-4 type during a FC-4 Type + query. The FC-4 types are consistent with the FC-4 Types as defined + in FC-FS. Byte 0 contains the FC-4 type. All other bytes are + reserved. + +6.8.2. iFCP Switch Name + + The iFCP Switch Name is a 64-bit World Wide Name (WWN) identifier + that uniquely identifies a distinct iFCP gateway in the network. + This globally unique identifier is used during the switch + registration/FC_DOMAIN_ID assignment process. The iFCP Switch Name + value used MUST conform to the requirements stated in [FC-FS] for + World Wide Names. The iSNS server SHALL track the state of all + FC_DOMAIN_ID values that have been allocated to each iFCP Switch + Name. If a given iFCP Switch Name is deregistered from the iSNS + database, then all FC_DOMAIN_ID values allocated to that iFCP Switch + Name SHALL be returned to the unused pool of values. + + + + + + +Tseng, et al. Standards Track [Page 96] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.8.3. iFCP Transparent Mode Commands + +6.8.3.1. Preferred ID + + This is a 4-byte unsigned integer field, and it is the requested + value that the iSNS client wishes to use for the FC_DOMAIN_ID. The + iSNS server SHALL grant the iSNS client the use of the requested + value as the FC_DOMAIN_ID, if the requested value has not already + been allocated. If the requested value is not available, the iSNS + server SHALL return a different value that has not been allocated. + +6.8.3.2. Assigned ID + + This is a 4-byte unsigned integer field that is used by an iFCP + gateway to reserve its own unique FC_DOMAIN_ID value from the range 1 + to 239. When a FC_DOMAIN_ID is no longer required, it SHALL be + released by the iFCP gateway using the RlseDomId message. The iSNS + server MUST use the Entity Status Inquiry message to determine + whether an iFCP gateway is still present on the network. + +6.8.3.3. Virtual_Fabric_ID + + This is a variable-length UTF-8 encoded NULL-terminated text-based + field of up to 256 bytes. The Virtual_Fabric_ID string is used as a + key attribute to identify a range of non-overlapping FC_DOMAIN_ID + values to be allocated using RqstDomId. Each Virtual_Fabric_ID + string submitted by an iSNS client SHALL have its own range of non- + overlapping FC_DOMAIN_ID values to be allocated to iSNS clients. + + +6.9. iSNS Server-Specific Attributes + + Access to the following attributes may be administratively + controlled. These attributes are specific to the iSNS server + instance; the same value is returned for all iSNS clients accessing + the iSNS server. Only query messages may be performed on these + attributes. Attempted registrations of values for these attributes + SHALL return a status code of 3 (Invalid Registration). + + A query for an iSNS Server-Specific attribute MUST contain the + identifying key attribute (i.e., iSCSI Name or FC Port Name WWPN) of + the Node originating the registration or query message as the Source + and Message Key attributes. The Operating Attributes are the + server-specific attributes being registered or queried. + + + + + + + +Tseng, et al. Standards Track [Page 97] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.9.1. iSNS Server Vendor OUI + + This attribute is the OUI (Organizationally Unique Identifier) + [802-1990] identifying the specific vendor implementing the iSNS + server. This attribute can only be queried; iSNS clients SHALL NOT be + allowed to register a value for the iSNS Server Vendor OUI. + +6.10. Vendor-Specific Attributes + + iSNS server implementations MAY define vendor-specific attributes for + private use. These attributes MAY be used to store optional data + that is registered and/or queried by iSNS clients in order to gain + optional capabilities. Note that any implementation of vendor- + specific attributes in the iSNS server SHALL NOT impose any form of + mandatory behavior on the part of the iSNS client. + + The tag values used for vendor-specific and user-specific use are + defined in Section 6.1. To avoid misinterpreting proprietary + attributes, the vendor's own OUI (Organizationally Unique Identifier) + MUST be placed in the upper three bytes of the attribute value field + itself. + + The OUI is defined in IEEE Std 802-1990 and is the same constant used + to generate 48 bit Universal LAN MAC addresses. A vendor's own iSNS + implementation will then be able to recognize the OUI in the + attribute field and be able to execute vendor-specific handling of + the attribute. + +6.10.1. Vendor-Specific Server Attributes + + Attributes with tags in the range 257 to 384 are vendor-specific or + site-specific attributes of the iSNS server. Values for these + attributes are administratively set by the specific vendor providing + the iSNS server implementation. Query access to these attributes may + be administratively controlled. These attributes are unique for each + logical iSNS server instance. Query messages for these attributes + SHALL use the key identifier (i.e., iSCSI Name or FC Port Name WWPN) + for both the Source attribute and Message Key attribute. These + attributes can only be queried; iSNS clients SHALL NOT be allowed to + register a value for server attributes. + +6.10.2. Vendor-Specific Entity Attributes + + Attributes in the range 385 to 512 are vendor-specific or site- + specific attributes used to describe the Network Entity object. + These attributes are keyed by the Entity Identifier attribute + (tag=1). + + + + +Tseng, et al. Standards Track [Page 98] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.10.3. Vendor-Specific Portal Attributes + + Attributes in the range 513 to 640 are vendor-specific or site- + specific attributes used to describe the Portal object. These + attributes are keyed by the Portal IP-Address (tag=16) and Portal + TCP/UDP Port (tag=17). + +6.10.4. Vendor-Specific iSCSI Node Attributes + + Attributes in the range 641 to 768 are vendor-specific or site- + specific attributes used to describe the iSCSI Node object. These + attributes are keyed by the iSCSI Name (tag=32). + +6.10.5. Vendor-Specific FC Port Name Attributes + + Attributes in the range 769 to 896 are vendor-specific or site- + specific attributes used to describe the N_Port Port Name object. + These attributes are keyed by the FC Port Name WWPN (tag=64). + +6.10.6. Vendor-Specific FC Node Name Attributes + + Attributes in the range 897 to 1024 are vendor-specific or site- + specific attributes used to describe the FC Node Name object. These + attributes are keyed by the FC Node Name WWNN (tag=96). + +6.10.7. Vendor-Specific Discovery Domain Attributes + + Attributes in the range 1025 to 1280 are vendor-specific or site- + specific attributes used to describe the Discovery Domain object. + These attributes are keyed by the DD_ID (tag=104). + +6.10.8. Vendor-Specific Discovery Domain Set Attributes + + Attributes in the range 1281 to 1536 are vendor-specific or site- + specific attributes used to describe the Discovery Domain Set object. + These attributes are keyed by the DD Set ID (tag=101) + +6.10.9. Other Vendor-Specific Attributes + + Attributes in the range 1537 to 2048 can be used for key and non-key + attributes that describe new vendor-specific objects specific to the + vendor's iSNS server implementation. + + + + + + + + + +Tseng, et al. Standards Track [Page 99] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.11. Discovery Domain Registration Attributes + +6.11.1. DD Set ID Keyed Attributes + +6.11.1.1. Discovery Domain Set ID (DDS ID) + + The DDS ID is an unsigned non-zero integer identifier used in the + iSNS directory database as a key to indicate a Discovery Domain Set + uniquely. A DDS is a collection of Discovery Domains that can be + enabled or disabled by a management station. This value is used as a + key for DDS attribute queries. When a Discovery Domain is + registered, it is initially not in any DDS. + + If the iSNS client does not provide a DDS_ID in a DDS registration + request message, the iSNS server SHALL generate a DDS_ID value that + is unique within the iSNS database for that new DDS. The created DDS + ID SHALL be returned in the response message. The DDS ID value of 0 + is reserved, and the DDS ID value of 1 is used for the default DDS + (see Section 2.2.2). + +6.11.1.2. Discovery Domain Set Symbolic Name + + A variable-length UTF-8 encoded NULL-terminated text-based field of + up to 256 bytes. This is a user-readable field used to assist a + network administrator in tracking the DDS function. When a client + registers a DDS symbolic name, the iSNS server SHALL verify it is + unique. If the name is not unique, then the DDS registration SHALL + be rejected with an "Invalid Registration" Status Code. The invalid + attribute(s), in this case the DDS symbolic name, SHALL be included + in the response. + +6.11.1.3. Discovery Domain Set Status + + The DDS_Status field is a 32-bit bitmap indicating the status of the + DDS. Bit 0 of the bitmap indicates whether the DDS is Enabled (1) or + Disabled (0). The default value for the DDS Enabled flag is Disabled + (0). + + Bit Position DDS Status + ------------ --------- + 31 (Lsb) DDS Enabled (1) / DDS Disabled (0) + All others RESERVED + +6.11.1.4. Discovery Domain Set Next ID + + This is a virtual attribute containing a 4-byte integer value that + indicates the next available (i.e., unused) Discovery Domain Set + Index value. This attribute may only be queried; the iSNS server + + + +Tseng, et al. Standards Track [Page 100] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + SHALL return an error code of 3 (Invalid Registration) to any client + that attempts to register a value for this attribute. A Message Key + is not required when exclusively querying for this attribute. + + The Discovery Domain Set Next Index MAY be used by an SNMP client to + create an entry in the iSNS server. SNMP requirements are described + in Section 2.10. + +6.11.2. DD ID Keyed Attributes + +6.11.2.1. Discovery Domain ID (DD ID) + + The DD ID is an unsigned non-zero integer identifier used in the iSNS + directory database as a key to identify a Discovery Domain uniquely. + This value is used as the key for any DD attribute query. If the + iSNS client does not provide a DD_ID in a DD registration request + message, the iSNS server SHALL generate a DD_ID value that is unique + within the iSNS database for that new DD (i.e., the iSNS client will + be registered in a new DD). The created DD ID SHALL be returned in + the response message. The DD ID value of 0 is reserved, and the DD + ID value of 1 is used for the default DD (see Section 2.2.2). + +6.11.2.2. Discovery Domain Symbolic Name + + A variable-length UTF-8 encoded NULL-terminated text-based field of + up to 256 bytes. When a client registers a DD symbolic name, the + iSNS server SHALL verify it is unique. If the name is not unique, + then the DD registration SHALL be rejected with an "Invalid + Registration" Status Code. The invalid attribute(s), in this case + the DD symbolic name, SHALL be included in the response. + +6.11.2.3. Discovery Domain Member: iSCSI Node Index + + This is the iSCSI Node Index of a Storage Node that is a member of + the DD. The DD may have a list of 0 to n members. The iSCSI Node + Index is one alternative representation of membership in a Discovery + Domain, the other alternative being the iSCSI Name. The Discovery + Domain iSCSI Node Index is a 4-byte non-zero integer value. + + The iSCSI Node Index can be used to represent a DD member in + situations where the iSCSI Name is too long to be used. An example + of this is when SNMP is used for management, as described in Section + 2.10. + + The iSCSI Node Index and the iSCSI Name stored as a member in a DD + SHALL be consistent with the iSCSI Node Index and iSCSI Name + attributes registered for the Storage Node object in the iSNS server. + + + + +Tseng, et al. Standards Track [Page 101] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +6.11.2.4. Discovery Domain Member: iSCSI Name + + A variable-length UTF-8 encoded NULL-terminated text-based field of + up to 224 bytes. It indicates membership for the specified iSCSI + Storage Node in the Discovery Domain. Note that the referenced + Storage Node does not need to be actively registered in the iSNS + database before the iSNS client uses this attribute. There is no + limit to the number of members that may be in a DD. Membership is + represented by the iSCSI Name of the iSCSI Storage Node. + +6.11.2.5. Discovery Domain Member: FC Port Name + + This 64-bit identifier attribute indicates membership for an iFCP + Storage Node (FC Port) in the Discovery Domain. Note that the + referenced Storage Node does not need to be actively registered in + the iSNS database before the iSNS client uses this attribute. There + is no limit to the number of members that may be in a DD. Membership + is represented by the FC Port Name (WWPN) of the iFCP Storage Node. + +6.11.2.6. Discovery Domain Member: Portal Index + + This attribute indicates membership in the Discovery Domain for a + Portal. It is an alternative representation for Portal membership to + the Portal IP Address and Portal TCP/UDP Port. The referenced Portal + MUST be actively registered in the iSNS database before the iSNS + client uses this attribute. + +6.11.2.7. Discovery Domain Member: Portal IP Address + + This attribute and the Portal TCP/UDP Port attribute indicate + membership in the Discovery Domain for the specified Portal. Note + that the referenced Portal does not need to be actively registered in + the iSNS database before the iSNS client uses this attribute. + +6.11.2.8. Discovery Domain Member: Portal TCP/UDP Port + + This attribute and the Portal IP Address attribute indicate + membership in the Discovery Domain for the specified Portal. Note + that the referenced Portal does not need to be actively registered in + the iSNS database before the iSNS client uses this attribute. + +6.11.2.9. Discovery Domain Features + + The Discovery Domain Features is a bitmap indicating the features of + this DD. The bit positions are defined below. A bit set to 1 + indicates the DD has the corresponding characteristics. + + + + + +Tseng, et al. Standards Track [Page 102] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Bit Position DD Feature + ------------ ---------- + 31 (Lsb) Boot List Enabled (1)/Boot List Disabled (0) + All others RESERVED + + Boot List: this feature indicates that the target(s) in this DD + provides boot capabilities for the member initiators, as described in + [iSCSI-boot]. + +6.11.2.10. Discovery Domain Next ID + + This is a virtual attribute containing a 4-byte integer value that + indicates the next available (i.e., unused) Discovery Domain Index + value. This attribute may only be queried; the iSNS server SHALL + return an error code of 3 (Invalid Registration) to any client that + attempts to register a value for this attribute. A Message Key is + not required when exclusively querying for this attribute. + +7. Security Considerations + +7.1. iSNS Security Threat Analysis + + When the iSNS protocol is deployed, the interaction between iSNS + server and iSNS clients is subject to the following security threats: + + a) An attacker could alter iSNS protocol messages, such as to direct + iSCSI and iFCP devices to establish connections with rogue peer + devices, or to weaken/eliminate IPSec protection for iSCSI or + iFCP traffic. + + b) An attacker could masquerade as the real iSNS server using false + iSNS heartbeat messages. This could cause iSCSI and iFCP devices + to use rogue iSNS servers. + + c) An attacker could gain knowledge about iSCSI and iFCP devices by + snooping iSNS protocol messages. Such information could aid an + attacker in mounting a direct attack on iSCSI and iFCP devices, + such as a denial-of-service attack or outright physical theft. + + To address these threats, the following capabilities are needed: + + a) Unicast iSNS protocol messages may need to be authenticated. In + addition, to protect against threat c), confidentiality support + is desirable and is REQUIRED when certain functions of iSNS + server are utilized. + + + + + + +Tseng, et al. Standards Track [Page 103] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + b) Multicast iSNS protocol messages such as the iSNS heartbeat + message may need to be authenticated. These messages need not be + confidential since they do not leak critical information. + +7.2. iSNS Security Implementation and Usage Requirements + + If the iSNS server is used to distribute authorizations for + communications between iFCP and iSCSI peer devices, IPsec ESP with + null transform MUST be implemented, and non-null transform MAY be + implemented. If a non-null transform is implemented, then the DES + encryption algorithm SHOULD NOT be used. + + If the iSNS server is used to distribute security policy for iFCP and + iSCSI devices, then authentication, data integrity, and + confidentiality MUST be supported and used. Where confidentiality is + desired or required, IPSec ESP with non-null transform SHOULD be + used, and the DES encryption algorithm SHOULD NOT be used. + + If the iSNS server is used to provide the boot list for clients, as + described in Section 6.11.2.9, then the iSCSI boot client SHOULD + implement a secure iSNS connection. + + In order to protect against an attacker masquerading as an iSNS + server, client devices MUST support the ability to authenticate + broadcast or multicast messages such as the iSNS heartbeat. The iSNS + authentication block (which is identical in format to the SLP + authentication block) SHALL be used for this purpose. iSNS clients + MUST implement the iSNS authentication block and MUST support BSD + value 0x002. If the iSNS server supports broadcast or multicast iSNS + messages (i.e., the heartbeat), then the server MUST implement the + iSNS authentication block and MUST support BSD value 0x002. Note + that the authentication block is used only for iSNS broadcast or + multicast messages and MUST NOT be used in unicast iSNS messages. + + There is no requirement that the communicating identities in iSNS + protocol messages be kept confidential. Specifically, the identity + and location of the iSNS server is not considered confidential. + + For protecting unicast iSNS protocol messages, iSNS servers + supporting security MUST implement ESP in tunnel mode and MAY + implement transport mode. + + All iSNS implementations supporting security MUST support the replay + protection mechanisms of IPsec. + + iSNS security implementations MUST support both IKE Main Mode and + Aggressive Mode for authentication, negotiation of security + associations, and key management, using the IPSec DOI [RFC2407]. + + + +Tseng, et al. Standards Track [Page 104] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + Manual keying SHOULD NOT be used since it does not provide the + necessary rekeying support. Conforming iSNS security implementations + MUST support authentication using a pre-shared key, and MAY support + certificate-based peer authentication using digital signatures. Peer + authentication using the public key encryption methods outlined in + IKEs Sections 5.2 and 5.3 [RFC2409] SHOULD NOT be supported. + + Conforming iSNS implementations MUST support both IKE Main Mode and + Aggressive Mode. IKE Main Mode with pre-shared key authentication + SHOULD NOT be used when either of the peers use dynamically assigned + IP addresses. Although Main Mode with pre-shared key authentication + offers good security in many cases, situations where dynamically + assigned addresses are used force the use of a group pre-shared key, + which is vulnerable to man-in-the-middle attack. IKE Identity + Payload ID_KEY_ID MUST NOT be used. + + When digital signatures are used for authentication, either IKE Main + Mode or IKE Aggressive Mode MAY be used. In all cases, access to + locally stored secret information (pre-shared key or private key for + digital signing) MUST be suitably restricted, since compromise of the + secret information nullifies the security properties of the IKE/IPsec + protocols. + + When digital signatures are used to achieve authentication, an IKE + negotiator SHOULD use IKE Certificate Request Payload(s) to specify + the certificate authority (or authorities) that are trusted in + accordance with its local policy. IKE negotiators SHOULD check the + pertinent Certificate Revocation List (CRL) before accepting a PKI + certificate for use in IKE's authentication procedures. + + When the iSNS server is used without security, IP block storage + protocol implementations MUST support a negative cache for + authentication failures. This allows implementations to avoid + continually contacting discovered endpoints that fail authentication + within IPsec or at the application layer (in the case of iSCSI + Login). The negative cache need not be maintained within the IPsec + implementation, but rather within the IP block storage protocol + implementation. + +7.3. Discovering Security Requirements of Peer Devices + + Once communication between iSNS clients and the iSNS server has been + secured through use of IPSec, the iSNS client devices have the + capability to discover the security settings that they need to use + for their peer-to-peer communications using the iSCSI and/or iFCP + protocols. This provides a potential scaling advantage over device- + by-device configuration of individual security policies for each + iSCSI and iFCP device. + + + +Tseng, et al. Standards Track [Page 105] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + The iSNS server stores security settings for each iSCSI and iFCP + device interface. These security settings, which can be retrieved by + authorized hosts, include use or non-use of IPSec, IKE, Main Mode, + and Aggressive Mode. For example, IKE may not be enabled for a + particular interface of a peer device. If a peer device can learn of + this in advance by consulting the iSNS server, it will not need to + waste time and resources attempting to initiate an IKE phase 1 + session with that peer device interface. + + If iSNS is used for this purpose, then the minimum information that + should be learned from the iSNS server is the use or non-use of IKE + and IPSec by each iFCP or iSCSI peer device interface. This + information is encoded in the Security Bitmap field of each Portal of + the peer device, and is applicable on a per-interface basis for the + peer device. iSNS queries for acquiring security configuration data + about peer devices MUST be protected by IPSec/ESP authentication. + +7.4. Configuring Security Policies of iFCP/iSCSI Devices + + Use of iSNS for distribution of security policies offers the + potential to reduce the burden of manual device configuration, and to + decrease the probability of communications failures due to + incompatible security policies. If iSNS is used to distribute + security policies, then IPSec authentication, data integrity, and + confidentiality MUST be used to protect all iSNS protocol messages. + + The complete IKE/IPSec configuration of each iFCP and/or iSCSI device + can be stored in the iSNS server, including policies that are used + for IKE Phase 1 and Phase 2 negotiations between client devices. The + IKE payload format includes a series of one or more proposals that + the iSCSI or iFCP device will use when negotiating the appropriate + IPsec policy to use to protect iSCSI or iFCP traffic. + + In addition, the iSCSI Authentication Methods used by each iSCSI + device can also be stored in the iSNS server. The iSCSI AuthMethod + field (tag=42) contains a null-terminated string embedded with the + text values indicating iSCSI authentication methods to be used by + that iSCSI device. + + Note that iSNS distribution of security policy is not necessary if + the security settings can be determined by other means, such as + manual configuration or IPsec security policy distribution. If a + network entity has already obtained its security configuration via + other mechanisms, then it MUST NOT request security policy via iSNS. + + + + + + + +Tseng, et al. Standards Track [Page 106] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +7.5. Resource Issues + + The iSNS protocol is lightweight and will not generate a significant + amount of traffic. iSNS traffic is characterized by occasional + registration, notification, and update messages that do not consume + significant amounts of bandwidth. Even software-based IPSec + implementations should not have a problem handling the traffic loads + generated by the iSNS protocol. + + To fulfill iSNS security requirements, the only additional resources + needed beyond what is already required for iSCSI and iFCP involve the + iSNS server. Because iSCSI and iFCP end nodes are already required + to implement IKE and IPSec, these existing requirements can also be + used to fulfill IKE and IPSec requirements for iSNS clients. + +7.6. iSNS Interaction with IKE and IPSec + + When IPSec security is enabled, each iSNS client with at least one + Storage Node that is registered in the iSNS database SHALL maintain + at least one phase-1 security association with the iSNS server. All + iSNS protocol messages between iSNS clients and the iSNS server SHALL + be protected by a phase-2 security association. + + When a Network Entity is removed from the iSNS database, the iSNS + server SHALL send a phase-1 delete message to the associated iSNS + client IKE peer, and tear down all phase-1 and phase-2 SAs associated + with that iSNS client. + +8. IANA Considerations + + The well-known TCP and UDP port number for iSNS is 3205. + + The standards action of this RFC creates two registries to be + maintained by IANA in support of iSNSP and assigns initial values for + both registries. The first registry is of Block Storage Protocols + supported by iSNS. The second registry is a detailed registry of + standard iSNS attributes that can be registered to and queried from + the iSNS server. Note that this RFC uses the registry created for + Block Structure Descriptor (BSD) in Section 15 of Service Location + Protocol, Version 2 [RFC2608]. + +8.1. Registry of Block Storage Protocols + + In order to maintain a registry of block storage protocols supported + by iSNSP, IANA will assign a 32-bit unsigned integer number for each + block storage protocol supported by iSNS. This number is stored in + the iSNS database as the Entity Protocol. The initial set of values + to be maintained by IANA for Entity Protocol is indicated in the + + + +Tseng, et al. Standards Track [Page 107] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + table in Section 6.2.2. Additional values for new block storage + protocols to be supported by iSNS SHALL be assigned by the IPS WG + Chairperson, or by a Designated Expert [RFC2434] appointed by the + IETF Transport Area Director. + +8.2. Registry of Standard iSNS Attributes + + IANA is responsible for creating and maintaining the Registry of + Standard iSNS Attributes. The initial list of iSNS attributes is + described in Section 6. For each iSNS attribute this information + MUST include, its tag value, the attribute length, and the tag values + for the set of permissible registration and query keys that can be + used for that attribute. The initial list of iSNS attributes to be + maintained by IANA is indicated in Section 6.1. + + Additions of new standard attributes to the Registry of Standard iSNS + Attributes SHALL require IETF Consensus [RFC2434]. The RFC required + for this process SHALL specify use of tag values reserved for IANA + allocation in Section 6.1. The RFC SHALL specify as a minimum, the + new attribute tag value, attribute length, and the set of permissible + registration and query keys that can be used for the new attribute. + The RFC SHALL also include a discussion of the reasons for the new + attribute(s) and how the new attribute(s) are to be used. + + As part of the process of obtaining IETF Consensus, the proposed RFC + and its supporting documentation SHALL be made available to the IPS + WG mailing list or, if the IPS WG is disbanded at the time, to a + mailing list designated by the IETF Transport Area Director. The + review and comment period SHALL last at least three months before the + IPS WG Chair or a person designated by the IETF Transport Area + Director decides either to reject the proposal or to forward the + draft to the IESG for publication as an RFC. When the specification + is published as an RFC, then IANA will register the new iSNS + attribute(s) and make the registration available to the community. + +8.3. Block Structure Descriptor (BSD) Registry + + Note that IANA is already responsible for assigning and maintaining + values used for the Block Structure Descriptor for the iSNS + Authentication Block (see Section 5.5). Section 15 of [RFC2608] + describes the process for allocation of new BSD values. + + + + + + + + + + +Tseng, et al. Standards Track [Page 108] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +9. Normative References + + [iSCSI] Satran, J., Meth, K., Sapuntzakis, C., Chadalapaka, M., + and E. Zeidner, "Internet Small Computer Systems + Interface (iSCSI)", RFC 3720, April 2004. + + [iFCP] Monia, C., Mullendore, R., Travostino, F., Jeong, W., + and M. Edwards, "iFCP - A Protocol for Internet Fibre + Channel Storage Networking", RFC 4172, September 2005. + + [iSNSOption] Monia, C., Tseng, J., and K. Gibbons, The IPv4 Dynamic + Host Configuration Protocol (DHCP) Option for the + Internet Storage Name Service, RFC 4174, September 2005. + + [RFC2608] Guttman, E., Perkins, C., Veizades, J., and M. Day, + "Service Location Protocol, Version 2 ", RFC 2608, June + 1999. + + [iSCSI-SLP] Bakke, M., Hufferd, J., Voruganti, K., Krueger, M., and + T. Sperry, "Finding Internet Small Computer Systems + Interface (iSCSI) Targets and Name Servers by Using + Service Location Protocol version 2 (SLP), RFC 4018, + April 2005. + + [iSCSI-boot] Sarkar, P., Missimer, D., and C. Sapuntzakis, + "Bootstrapping Clients using the Internet Samll Computer + System Interface (iSCSI) Protocol", RFC 4173, September + 2005. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [STRINGPREP] Bakke, M., "String Profile for Internet Small Computer + Systems Interface (iSCSI) Names", RFC 3722, April 2004. + + [NAMEPREP] Hoffman, P. Nameprep: A Stringprep Profile for + Internationalized Domain Names, July 2002. + + [RFC2407] Piper, D., "The Internet IP Security Domain of + Interpretation for ISAKMP", RFC 2407, November 1998. + + [RFC2408] Maughan, D., Schertler, M., Schneider, M., and J. + Turner, "Internet Security Association and Key + Management Protocol (ISAKMP)", RFC 2408, November 1998. + + [RFC2409] Harkins, D. and D. Carrel, "The Internet Key Exchange + (IKE)", RFC 2409, November 1998. + + + + +Tseng, et al. Standards Track [Page 109] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + [EUI-64] Guidelines for 64-bit Global Identifier (EUI-64) + Registration Authority, May 2001, IEEE + + [RFC3279] Bassham, L., Polk, W., and R. Housley, "Algorithms and + Identifiers for the Internet X.509 Public Key + Infrastructure Certificate and Certificate Revocation + List (CRL) Profile", RFC 3279, April 2002. + + [RFC3280] Housley, R., Polk, W., Ford, W., and D. Solo, "Internet + X.509 Public Key Infrastructure Certificate and + Certificate Revocation List (CRL) Profile", RFC 3280, + April 2002. + + [802-1990] IEEE Standards for Local and Metropolitan Area Networks: + Overview and Architecture, Technical Committee on + Computer Communications of the IEEE Computer Society, + May 31, 1990 + + [FC-FS] Fibre Channel Framing and Signaling Interface, NCITS + Working Draft Project 1331-D + +10. Informative References + + [iSNSMIB] Gibbons, K., et al., "Definitions of Managed Objects for + iSNS (Internet Storage name Service)", Work in Progress, + July 2003. + + [X.509] ITU-T Recommendation X.509 (1997 E): Information + Technology - Open Systems Interconnection - The + Directory: Authentication Framework, June 1997 + + [FC-GS-4] Fibre Channel Generic Services-4 (work in progress), + NCITS Working Draft Project 1505-D + + [RFC1510] Kohl, J. and C. Neuman, "The Kerberos Network + Authentication Service (V5)", RFC 1510, September 1993. + + [RFC2025] Adams, C., "The Simple Public-Key GSS-API Mechanism + (SPKM)", RFC 2025, October 1996. + + [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 2434, + October 1998. + + [RFC2945] Wu, T., "The SRP Authentication and Key Exchange + System", RFC 2945, September 2000. + + + + + +Tseng, et al. Standards Track [Page 110] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + [RFC1994] Simpson, W., "PPP Challenge Handshake Authentication + Protocol (CHAP)", RFC 1994, August 1996. + + [RFC2131] Droms, R., "Dynamic Host Configuration Protocol", RFC + 2131, March 1997. + + [RFC3410] Case, J., Mundy, R., Partain, D., and B. Stewart, + "Introduction and Applicability Statements for + Internet-Standard Management Framework", RFC 3410, + December 2002. + + [RFC3411] Harrington, D., Presuhn, R., and B. Wijnen, "An + Architecture for Describing Simple Network Management + Protocol (SNMP) Management Frameworks", STD 62, RFC + 3411, December 2002. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 111] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +Appendix A: iSNS Examples + +A.1. iSCSI Initialization Example + + This example assumes an SLP Service Agent (SA) has been implemented + on the iSNS host, and an SLP User Agent (UA) has been implemented on + the iSNS initiator. See [RFC2608] for further details on SAs and + UAs. This example also assumes that the target is configured to use + the iSNS server, and have its access control policy subordinated to + the iSNS server. + +A.1.1. Simple iSCSI Target Registration + + In this example, a simple target with a single iSCSI name registers + with the iSNS server. The target is represented in the iSNS by an + Entity containing one Storage Node, one Portal, and an implicitly + registered Portal Group that provides a relationship between the + Storage Node and Portal. The target has not been assigned a Fully + Qualified Domain Name (FQDN) by the administrator. In this example, + because a PG object is not explicitly registered, a Portal Group with + a PGT of 1 is implicitly registered. In this example SLP is used to + discover the location of the iSNS Server. An alternative is to use + the iSNS DHCP option [iSNSOption] to discover the iSNS server. + + +--------------------------+------------------+-------------------+ + | iSCSI Target Device | iSNS Server |Management Station | + +--------------------------+------------------+-------------------+ + |Discover iSNS--SLP------->| |/*mgmt station is | + | |<--SLP--iSNS Here:| administratively | + | | 192.0.2.100 | authorized to view| + | | | all DDs. Device | + | DevAttrReg--------->| | NAMEabcd was | + |Src:(tag=32) "NAMEabcd" | | previously placed | + |Key: <none present> | | into DDabcd along | + |Oper Attrs: | | with devpdq and | + |tag=1: NULL | | devrst. | + |tag=2: "iSCSI" | | | + |tag=16: 192.0.2.5 | | | + |tag=17: 5001 | | | + |tag=32: "NAMEabcd" | | | + |tag=33: target | | | + |tag=34: "disk 1" | | | + | |<---DevAttrRegRsp | | + | |SUCCESS | | + | |Key:(tag=1) "isns:0001" | + | |Oper Attrs: | | + | |tag=1: "isns:0001"| | + | |tag=2: "iSCSI" | | + + + +Tseng, et al. Standards Track [Page 112] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + | |tag=16: 192.0.2.5 | | + | |tag=17: 5001 | | + | |tag=32: "NAMEabcd"|/* previously | + | |tag=33: target | placed in a DD */ | + | |tag=34: "disk 1" | | + | | | | + | | SCN-------->| | + | |(or SNMP notification) | + | |dest:(tag=32):"MGMTname1" | + | |time:(tag=4): <current time> | + | |tag=35: "MGT-SCN, OBJ-ADD" | + | |tag=32: "NAMEabcd"| | + | | |<-------SCNRsp | + | DevAttrQry--------->| | | + |Src:(tag=32) "NAMEabcd" | | | + |Key:(tag=33) "initiator" | | | + |Oper Attrs: | | | + |tag=16: NULL | | | + |tag=17: NULL | | | + |tag=32: NULL | | | + |/*Query asks for all initr| | | + |devices' IP address, port |<---DevAttrQryRsp | | + |number, and Name*/ |SUCCESS | | + | |tag=16:192.0.2.1 | | + | |tag=17:50000 | | + | |tag=32:"devpdq" | | + | |tag=16:192.0.2.2 | | + | |tag=17:50000 | | + | |tag=32:"devrst" | | + |/*************************| |<-----DevAttrQry | + |Our target "NAMEabcd" | |src: "MGMTname1" | + |discovers two initiators | key:(tag=32)"NAMEabcd" + |in shared DDs. It will | |Op Attrs: | + |accept iSCSI logins from | |tag=16: NULL | + |these two identified | |tag=17: NULL | + |initiators presented by | |tag=32: NULL | + |iSNS | | | + |*************************/| DevAttrQryRsp--->| | + | |SUCCESS | | + | |tag=16: 192.0.2.5 | | + | |tag=17: 5001 | | + | |tag=32: "NAMEabcd"| | + +--------------------------+------------------+-------------------+ + + + + + + + + +Tseng, et al. Standards Track [Page 113] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +A.1.2. Target Registration and DD Configuration + + In this example, a more complex target, with two Storage Nodes and + two Portals using ESI monitoring, registers with the iSNS. This + target has been configured with a Fully Qualified Domain Name (FQDN) + in the DNS servers, and the user wishes to use this identifier for + the device. The target explicitly registers Portal Groups to + describe how each Portal provides access to each Storage Node. One + target Storage Node allows coordinated access through both Portals. + The other Storage Node allows access, but not coordinated access, + through both Portals. + + +--------------------------+------------------+-------------------+ + | iSCSI Target Device | iSNS Server |Management Station | + +--------------------------+------------------+-------------------+ + |Discover iSNS--SLP--> | |/*mgmt station is | + | |<--SLP--iSNS Here:| administratively | + | | 192.0.2.100 | authorized to view| + | DevAttrReg--> | | all DDs */ | + |Src: | | | + |tag=32: "NAMEabcd" | | | + |Msg Key: | | | + |tag=1: "jbod1.example.com"| | | + |Oper Attrs: | | | + |tag=1: "jbod1.example.com"| | | + |tag=2: "iSCSI" | | | + |tag=16: 192.0.2.4 | | | + |tag=17: 5001 | | | + |tag=19: 5 | | | + |tag=20: 5002 | | | + |tag=16: 192.0.2.5 | | | + |tag=17: 5001 | | | + |tag=19: 5 | | | + |tag=20: 5002 | | | + |tag=32: "NAMEabcd" | | | + |tag=33: "Target" | | | + |tag=34: "Storage Array 1" | | | + |tag=51: 10 | | | + |tag=49: 192.0.2.4 | | | + |tag=50: 5001 | | | + |tag=49: 192.0.2.5 | | | + |tag=50: 5001 | | | + |tag=32: "NAMEefgh" | | | + |tag=33: "Target" | | | + |tag=34: "Storage Array 2" |/*****************| | + |tag=51: 20 |jbod1.example.com is | + |tag=49: 192.0.2.4 |now registered in | | + |tag=50: 5001 |iSNS, but is not | | + + + +Tseng, et al. Standards Track [Page 114] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + |tag=51: 30 |in any DD. Therefore, | + |tag=49: 192.0.2.5 |no other devices | | + |tag=50: 5001 |can "see" it. | | + | |*****************/| | + | |<--DevAttrRegRsp | | + | |SUCCESS | | + | |Msg Key: | | + | |tag=1: "jbod1.example.com" | + | |Oper Attrs: | | + | |tag=1: "jbod1.example.com" | + | |tag=2: "iSCSI" | | + | |tag=16: 192.0.2.4 | | + | |tag=17: 5001 | | + | |tag=19: 5 | | + | |tag=20: 5002 | | + | |tag=16: 192.0.2.5 | | + | |tag=17: 5001 | | + | |tag=19: 5 | | + | |tag=20: 5002 | | + | |tag=32: "NAMEabcd"| | + | |tag=33: "Target" | | + | |tag=34: "Storage Array 1" | + | |tag=48: "NAMEabcd"| | + | |tag=49: 192.0.2.4 | | + | |tag=50: 5001 | | + | |tag=51: 10 | | + | |tag=48: "NAMEabcd"| | + | |tag=49: 192.0.2.5 | | + | |tag=50: 5001 | | + | |tag=51: 10 | | + | |tag=32: "NAMEefgh"| | + | |tag=33: "Target" | | + | |tag=34: "Storage Array 2" | + | |tag=43: X.509 cert| | + | |tag=48: "NAMEefgh"| | + | |tag=49: 192.0.2.4 | | + | |tag=50: 5001 | | + | |tag=51: 20 | | + | |tag=48: "NAMEefgh"| | + | |tag=49: 192.0.2.5 | | + | |tag=50: 5001 | | + | |tag=51: 30 | | + | | | | + | | SCN------> | | + | | (or SNMP notification) | + | |dest:(tag=32)"mgmt.example.com" | + | |time:(tag=4): <current time> | + | |tag=35: "MGT-SCN, OBJ-ADD" | + + + +Tseng, et al. Standards Track [Page 115] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + | |tag=32: "NAMEabcd"| | + | |tag=35: "MGT-SCN, OBJ-ADD" | + | |tag=32: "NAMEefgh"| | + | | |<--SCNRsp | + | | |SUCCESS | + | | tag=32:"mgmt.example.com"| + | | | | + | | |<--DevAttrQry | + | | |Src: | + | | tag=32:"mgmt.example.com" + | | |Msg Key: | + | | |tag=32: "NAMEabcd" | + | | |Oper Attrs: | + | | |tag=16: <0-length> | + | | |tag=17: <0-length> | + | | |tag=32: <0-length> | + | | | | + | | DevAttrQryRsp--> | | + | |SUCCESS | | + | |Msg Key: | | + | |tag=32: "NAMEabcd"| | + | |Oper Attrs: | | + | |tag=16: 192.0.2.4 | | + | |tag=17: 5001 | | + | |tag=32:"NAMEabcd" | | + | |tag=16: 192.0.2.5 | | + | |tag=17: 5001 | | + | |tag=32:"NAMEabcd" | | + | | |Src: | + | | tag=32:"mgmt.example.com" + | | |Msg Key: | + | | |tag=32: "NAMEefgh" | + | | |Oper Attrs: | + | | |tag=16: <0-length> | + | | |tag=17: <0-length> | + | | |tag=32: <0-length> | + | | | | + | | DevAttrQryRsp--> | | + | |SUCCESS | | + | |Msg Key: | | + | |tag=32: "NAMEefgh"| | + | |Oper Attrs: | | + | |tag=16: 192.0.2.4 | | + | |tag=17: 5001 | | + | |tag=32:"NAMEefgh" | | + | |tag=16: 192.0.2.5 |/**Mgmt Station ***| + | |tag=17: 5001 |displays device, | + | |tag=32:"NAMEefgh" |the operator decides + + + +Tseng, et al. Standards Track [Page 116] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + | | |to place "NAMEabcd"| + | | |into Domain "DDxyz"| + |/*************************| |******************/| + |Target is now registered | | | + |in iSNS. It is then placed| |<--DDReg | + |in a pre-existing DD with | |Src: | + |DD_ID 123 by a management | tag=32:"mgmt.example.com" + |station. | |Msg Key: | + |*************************/| |tag=2065: 123 | + | | |Oper Attrs: | + | | |tag=2068: "NAMEabcd" + | | DDRegRsp-----> | | + | |SUCCESS | | + | |Msg Key: | | + | |tag=2065: 123 | | + | |Oper Attrs: | | + | |tag=2065: 123 | | + +--------------------------+------------------+-------------------+ + +A.1.3. Initiator Registration and Target Discovery + + The following example illustrates a new initiator registering with + the iSNS, and discovering the target NAMEabcd from the example in + A.1.2. + + +--------------------------+------------------+-------------------+ + | iSCSI Initiator | iSNS |Management Station | + +--------------------------+------------------+-------------------+ + |Discover iSNS--SLP--> | |/*mgmt station is | + | |<--SLP--iSNS Here:| administratively | + | | 192.36.53.1 | authorized to view| + |DevAttrReg--> | | all DDs ********/ | + |Src: | | | + |tag=32: "NAMEijkl" | | | + |Msg Key: | | | + |tag=1: "svr1.example.com" | | | + |Oper Attrs: | | | + |tag=1: "svr1.example.com" | | | + |tag=2: "iSCSI" | | | + |tag=16: 192.20.3.1 |/*****************| | + |tag=17: 5001 |Device not in any | | + |tag=19: 5 |DD, so it is | | + |tag=20: 5002 |inaccessible by | | + |tag=32: "NAMEijkl" |other devices | | + |tag=33: "Initiator" |*****************/| | + |tag=34: "Server1" | | | + |tag=51: 11 | | | + |tag=49: 192.20.3.1 | | | + + + +Tseng, et al. Standards Track [Page 117] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + |tag=50: 5001 | | | + | |<--DevAttrRegRsp | | + | |SUCCESS | | + | |Msg Key: | | + | |tag=1: "svr1.example.com" | + | |Oper Attrs: | | + | |tag=1: "svr1.example.com" | + | |tag=2: "iSCSI" | | + | |tag=16: 192.20.3.1| | + | |tag=17: 5001 | | + | |tag=19: 5 | | + | |tag=20: 5002 | | + | |tag=32: "NAMEijkl"| | + | |tag=33: "Initiator" | + | |tag=34: "Server1" | | + | |tag=48: "NAMEijkl"| | + | |tag=49: 192.20.3.1| | + | |tag=50: 5001 | | + | |tag=51: 11 | | + | | | | + | | SCN------> | | + | | (or SNMP notification) | + | |dest:(tag=32)"mgmt.example.com" | + | |time:(tag=4): <current time> | + | |tag=35: "MGT-SCN, OBJ-ADD" | + | |tag=32: "NAMEijkl"| | + | | | | + | | |<------SCNRsp | + | | |SUCCESS | + | | tag=32:"mgmt.example.com" + | | | | + |SCNReg--> | | | + |Src: | | | + |tag=32: "NAMEijkl" | | | + |Msg Key: | | | + |tag=32: "NAMEijkl" | | | + |Oper Attrs: | | | + |tag=35: <TARG&SELF, OBJ-RMV/ADD/UPD> | | + | |<--SCNRegRsp | | + | |SUCCESS | | + | | | | + | | |<----DevAttrQry | + | | |Src: | + | | tag=32:"mgmt.example.com" + | | |Msg Key: | + | | |tag=32: "NAMEijkl" | + | | |Oper Attrs: | + | | |tag=16: <0-length> | + + + +Tseng, et al. Standards Track [Page 118] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + | | |tag=17: <0-length> | + | | |tag=32: <0-length> | + | | DevAttrQryRsp--->| | + | |SUCCESS | | + | |Msg Key: | | + | |tag=32: "NAMEijkl"| | + | |Oper Attrs: | | + | |tag=16:192.20.3.1 | | + | |tag=17: 5001 | | + | |tag=32:"NAMEijkl" | | + | | |/**Mgmt Station ***| + | | |displays device, the + | | |operator decides to| + | | |place "NAMEijkl" into + | | |pre-existing Disc | + | | |Domain "DDxyz" with| + | | |device NAMEabcd | + | | |******************/| + | | |<--DDReg | + | | |Src: | + | | tag=32:"mgmt.example.com" + | | |Msg Key: | + | | |tag=2065: 123 | + | | |Oper Attrs: | + | | |tag=2068: "NAMEijkl" + | | | | + | | DDRegRsp---->| | + | |SUCCESS | | + | |Msg Key: | | + | |tag=2065: 123 | | + | |Oper Attrs: | | + | |tag=2065: 123 |/******************| + | | |"NAMEijkl" has been| + | | |moved to "DDxyz" | + | | |******************/| + | | SCN------>| | + | |dest:(tag=32)"mgmt.example.com" | + | |time:(tag=4): <current time> | + | |tag=35: <MGT-SCN, DD/DDS-MBR-ADD> | + | |tag=2065: 123 | | + | |tag=2068: "NAMEijkl" | + | | | | + | | |<------SCNRsp | + | | |SUCCESS | + | | tag=32:"mgmt.example.com" + | |<-----SCN | | + | |dest:(tag=32)"NAMEijkl" | + | |time:(tag=4): <current time> | + + + +Tseng, et al. Standards Track [Page 119] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + | |tag=35: <TARG&SELF, OBJ-ADD> | + | |tag=32: "NAMEijkl"| | + | SCNRsp------> | | | + |SUCCESS | | | + |tag=32:"NAMEijkl" | | | + | | | | + | |/*****************| | + | |Note that NAMEabcd| | + | |also receives an | | + | |SCN that NAMEijkl | | + | |is in the same DD | | + | |*****************/| | + | (to "NAMEabcd")|<-----SCN | | + | |dest:(tag=32)"NAMEabcd" | + | |time:(tag=4): <current time> | + | |tag=35: <INIT&SELF, OBJ-ADD> | + | |tag=32: "NAMEijkl"| | + | SCNRsp------> | | | + |SUCCESS | | | + |tag=32:"NAMEabcd" | | | + | | | | + | DevAttrQry----------->| | | + |Src: | | | + |tag=32: "NAMEijkl" | | | + |Msg Key: | | | + |tag=33: "Target" | | | + |Oper Attrs: | | | + |tag=16: <0-length> | | | + |tag=17: <0-length> | | | + |tag=32: <0-length> | | | + |tag=34: <0-length> | | | + |tag=43: <0-length> | | | + |tag=48: <0-length> | | | + |tag=49: <0-length> | | | + |tag=50: <0-length> | | | + |tag=51: <0-length> | | | + | |<--DevAttrQryRsp | | + | |SUCCESS | | + | |Msg Key: | | + | |tag=33:"Target" | | + | |Oper Attrs: | | + | |tag=16: 192.0.2.4 | | + | |tag=17: 5001 | | + | |tag=32: "NAMEabcd"| | + | |tag=34: "Storage Array 1" | + | |tag=16: 192.0.2.5 | | + | |tag=17: 5001 | | + | |tag=32: "NAMEabcd"| | + + + +Tseng, et al. Standards Track [Page 120] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + + | |tag=34: "Storage Array 1" | + | |tag=43: X.509 cert| | + | |tag=48: "NAMEabcd"| | + | |tag=49: 192.0.2.4 | | + | |tag=50: 5001 | | + | |tag=51: 10 | | + | |tag=48: "NAMEabcd"| | + | |tag=49: 192.0.2.5 | | + | |tag=50: 5001 | | + | |tag=51: 10 | | + | | | | + |/***The initiator has discovered | | + |the target, and has everything | | + |needed to complete iSCSI login | | + |The same process occurs on the | | + |target side; the SCN prompts the | | + |target to download the list of | | + |authorized initiators from the | | + |iSNS (i.e., those initiators in the | | + |same DD as the target.************/ | | + +--------------------------+------------------+-------------------+ + +Acknowledgements + + Numerous individuals contributed to the creation of this document + through their careful review and submissions of comments and + recommendations. We acknowledge the following persons for their + technical contributions to this document: Mark Bakke (Cisco), John + Hufferd (IBM), Julian Satran (IBM), Kaladhar Voruganti(IBM), Joe Czap + (IBM), John Dowdy (IBM), Tom McSweeney (IBM), Jim Hafner (IBM), Chad + Gregory (Intel), Yaron Klein (Sanrad), Larry Lamers (Adaptec), Jack + Harwood (EMC), David Black (EMC), David Robinson (Sun), Alan Warwick + (Microsoft), Bob Snead (Microsoft), Fa Yoeu (Intransa), Joe White + (McDATA), Charles Monia (McDATA), Larry Hofer (McDATA), Ken Hirata + (Vixel), Howard Hall (Pirus), Malikarjun Chadalapaka (HP), Marjorie + Krueger (HP), Siva Vaddepuri (McDATA), and Vinai Singh (American + Megatrends). + + + + + + + + + + + + + + +Tseng, et al. Standards Track [Page 121] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +Authors' Addresses + + Josh Tseng + Riverbed Technology + 501 2nd Street, Suite 410 + San Francisco, CA 94107 + + Phone: (650)274-2109 + EMail: joshtseng@yahoo.com + + + Kevin Gibbons + McDATA Corporation + 4555 Great America Parkway + Santa Clara, CA 95054-1208 + + Phone: (408) 567-5765 + EMail: kevin.gibbons@mcdata.com + + + Franco Travostino + Nortel + 600 Technology Park Drive + Billerica, MA 01821 USA + + Phone: (978) 288-7708 + EMail: travos@nortel.com + + + Curt du Laney + Rincon Research Corporation + 101 North Wilmot Road, Suite 101 + Tucson AZ 85711 + + Phone: (520) 519-4409 + EMail: cdl@rincon.com + + + Joe Souza + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 + + Phone: (425) 706-3135 + EMail: joes@exmsft.com + + + + + + +Tseng, et al. Standards Track [Page 122] + +RFC 4171 Internet Storage Name Service (iSNS) September 2005 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2005). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET + ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE + INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at ietf- + ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + +Tseng, et al. Standards Track [Page 123] + diff --git a/utils/open-isns/domain.c b/utils/open-isns/domain.c new file mode 100644 index 0000000..3b848ac --- /dev/null +++ b/utils/open-isns/domain.c @@ -0,0 +1,208 @@ +/* + * iSNS object model - discovery domain specific code + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "objects.h" +#include "util.h" + +static int +__isns_default_dd_rebuild(isns_object_t *obj, isns_db_t *db) +{ + isns_object_list_t list = ISNS_OBJECT_LIST_INIT; + unsigned int i; + + isns_object_prune_attrs(obj); + + isns_db_get_domainless(db, &isns_iscsi_node_template, &list); + for (i = 0; i < list.iol_count; ++i) { + isns_object_t *node = list.iol_data[i]; + const char *name; + uint32_t type; + + if (!isns_object_get_uint32(node, + ISNS_TAG_ISCSI_NODE_TYPE, + &type)) + continue; + if (type & ISNS_ISCSI_CONTROL_MASK) + continue; + if (!isns_object_get_string(node, + ISNS_TAG_ISCSI_NAME, + &name)) + continue; + isns_object_set_string(obj, + ISNS_TAG_DD_MEMBER_ISCSI_NAME, + name); + } + + return ISNS_SUCCESS; +} + +/* + * Create the default domain + */ +isns_object_t * +isns_create_default_domain(void) +{ + isns_object_t *obj; + + obj = isns_create_object(&isns_dd_template, NULL, NULL); + if (!obj) + return NULL; + + isns_object_set_uint32(obj, ISNS_TAG_DD_ID, 0); + obj->ie_rebuild = __isns_default_dd_rebuild; + return obj; +} + +/* + * Check object type + */ +int +isns_object_is_dd(const isns_object_t *obj) +{ + return ISNS_IS_DD(obj); +} + +int +isns_object_is_ddset(const isns_object_t *obj) +{ + return ISNS_IS_DDSET(obj); +} + +/* + * Keep track of DD membership through a bit vector + */ +int +isns_object_mark_membership(isns_object_t *obj, uint32_t id) +{ + if (!obj->ie_membership) + obj->ie_membership = isns_bitvector_alloc(); + + return isns_bitvector_set_bit(obj->ie_membership, id); +} + +int +isns_object_test_membership(const isns_object_t *obj, uint32_t id) +{ + if (!obj->ie_membership) + return 0; + + return isns_bitvector_test_bit(obj->ie_membership, id); +} + +int +isns_object_clear_membership(isns_object_t *obj, uint32_t id) +{ + if (!obj->ie_membership) + return 0; + + return isns_bitvector_clear_bit(obj->ie_membership, id); +} + +/* + * Check whether the two objects share a discovery domain, + * and if so, return the DD_ID. + * Returns -1 otherwise. + */ +int +isns_object_test_visibility(const isns_object_t *a, const isns_object_t *b) +{ + /* The admin can tell isnsd to put all nodes which are *not* + * in any discovery domain, into the so-called default domain */ + if (isns_config.ic_use_default_domain + && a->ie_template == b->ie_template + && isns_bitvector_is_empty(a->ie_membership) + && isns_bitvector_is_empty(b->ie_membership)) + return 1; + + return isns_bitvector_intersect(a->ie_membership, b->ie_membership, NULL) >= 0; +} + +/* + * Return all visible nodes and portals + */ +static int +__isns_object_vis_callback(uint32_t dd_id, void *ptr) +{ + isns_object_list_t *list = ptr; + + /* Get all active members */ + isns_dd_get_members(dd_id, list, 1); + return 0; +} + +void +isns_object_get_visible(const isns_object_t *obj, + isns_db_t *db, + isns_object_list_t *result) +{ + if (isns_bitvector_is_empty(obj->ie_membership)) { + /* Get all other nodes not in any DD */ + if (isns_config.ic_use_default_domain) + isns_db_get_domainless(db, + obj->ie_template, + result); + return; + } + + isns_bitvector_foreach(obj->ie_membership, + __isns_object_vis_callback, + result); +} + +/* + * Object templates + */ +static uint32_t discovery_domain_attrs[] = { + ISNS_TAG_DD_ID, + ISNS_TAG_DD_SYMBOLIC_NAME, + ISNS_TAG_DD_MEMBER_ISCSI_INDEX, + ISNS_TAG_DD_MEMBER_ISCSI_NAME, + ISNS_TAG_DD_MEMBER_FC_PORT_NAME, + ISNS_TAG_DD_MEMBER_PORTAL_INDEX, + ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR, + ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT, + ISNS_TAG_DD_FEATURES, +}; + +static uint32_t discovery_domain_key_attrs[] = { + ISNS_TAG_DD_ID, +}; + +isns_object_template_t isns_dd_template = { + .iot_name = "Discovery Domain", + .iot_handle = ISNS_OBJECT_TYPE_DD, + .iot_attrs = discovery_domain_attrs, + .iot_num_attrs = array_num_elements(discovery_domain_attrs), + .iot_keys = discovery_domain_key_attrs, + .iot_num_keys = array_num_elements(discovery_domain_key_attrs), + .iot_index = ISNS_TAG_DD_ID, + .iot_next_index = ISNS_TAG_DD_NEXT_ID, +}; + +static uint32_t dd_set_attrs[] = { + ISNS_TAG_DD_SET_ID, + ISNS_TAG_DD_SET_SYMBOLIC_NAME, + ISNS_TAG_DD_SET_STATUS, +}; + +static uint32_t dd_set_key_attrs[] = { + ISNS_TAG_DD_SET_ID, +}; + +isns_object_template_t isns_ddset_template = { + .iot_name = "Discovery Domain Set", + .iot_handle = ISNS_OBJECT_TYPE_DDSET, + .iot_attrs = dd_set_attrs, + .iot_num_attrs = array_num_elements(dd_set_attrs), + .iot_keys = dd_set_key_attrs, + .iot_num_keys = array_num_elements(dd_set_key_attrs), + .iot_next_index = ISNS_TAG_DD_SET_NEXT_ID, +}; + diff --git a/utils/open-isns/entity.c b/utils/open-isns/entity.c new file mode 100644 index 0000000..cd45e1f --- /dev/null +++ b/utils/open-isns/entity.c @@ -0,0 +1,127 @@ +/* + * iSNS object model - network entity specific code + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "objects.h" +#include "util.h" + +/* + * Create a network entity + */ +isns_object_t * +isns_create_entity(int protocol, const char *name) +{ + isns_object_t *obj; + + obj = isns_create_object(&isns_entity_template, NULL, NULL); + isns_object_set_string(obj, + ISNS_TAG_ENTITY_IDENTIFIER, + name); + isns_object_set_uint32(obj, + ISNS_TAG_ENTITY_PROTOCOL, + protocol); + + return obj; +} + +isns_object_t * +isns_create_entity_for_source(const isns_source_t *source, + const char *eid) +{ + switch (isns_source_type(source)) { + case ISNS_TAG_ISCSI_NAME: + return isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, eid); + + case ISNS_TAG_FC_PORT_NAME_WWPN: + return isns_create_entity(ISNS_ENTITY_PROTOCOL_IFCP, eid); + } + + return NULL; +} + +const char * +isns_entity_name(const isns_object_t *node) +{ + const isns_attr_t *attr; + + if (node->ie_attrs.ial_count == 0) + return NULL; + attr = node->ie_attrs.ial_data[0]; + if (attr->ia_value.iv_type != &isns_attr_type_string + || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER) + return NULL; + + return attr->ia_value.iv_string; + +} + +int +isns_object_is_entity(const isns_object_t *obj) +{ + return ISNS_IS_ENTITY(obj); +} + +/* + * 6.2.4. Entity Registration Timestamp + * + * This field indicates the most recent time when the Network Entity + * registration occurred or when an associated object attribute was + * updated or queried by the iSNS client registering the Network Entity. + * The time format is, in seconds, the update period since the standard + * base time of 00:00:00 GMT on January 1, 1970. This field cannot be + * explicitly registered. This timestamp TLV format is also used in + * the SCN and ESI messages. + * + * Implementer's note: we consider any kind of activity from + * the client an indication that it is still alive. + * Only exception is the pseudo-entity that holds the access control + * information; we never assign it a timestamp so it is never subject + * to expiry. + */ +void +isns_entity_touch(isns_object_t *obj) +{ + /* Do not add a timestamp to entity CONTROL */ + if (obj == NULL + || (obj->ie_flags & ISNS_OBJECT_PRIVATE) + || obj->ie_template != &isns_entity_template) + return; + isns_object_set_uint64(obj, ISNS_TAG_TIMESTAMP, time(NULL)); +} + +/* + * Object template + */ +static uint32_t entity_attrs[] = { + ISNS_TAG_ENTITY_IDENTIFIER, + ISNS_TAG_ENTITY_PROTOCOL, + ISNS_TAG_MGMT_IP_ADDRESS, + ISNS_TAG_TIMESTAMP, + ISNS_TAG_PROTOCOL_VERSION_RANGE, + ISNS_TAG_REGISTRATION_PERIOD, + ISNS_TAG_ENTITY_INDEX, + ISNS_TAG_ENTITY_ISAKMP_PHASE_1, + ISNS_TAG_ENTITY_CERTIFICATE, +}; + +static uint32_t entity_key_attrs[] = { + ISNS_TAG_ENTITY_IDENTIFIER, +}; + +isns_object_template_t isns_entity_template = { + .iot_name = "Network Entity", + .iot_handle = ISNS_OBJECT_TYPE_ENTITY, + .iot_attrs = entity_attrs, + .iot_num_attrs = array_num_elements(entity_attrs), + .iot_keys = entity_key_attrs, + .iot_num_keys = array_num_elements(entity_key_attrs), + .iot_index = ISNS_TAG_ENTITY_INDEX, + .iot_next_index = ISNS_TAG_ENTITY_NEXT_INDEX, +}; + diff --git a/utils/open-isns/error.c b/utils/open-isns/error.c new file mode 100644 index 0000000..0d365e8 --- /dev/null +++ b/utils/open-isns/error.c @@ -0,0 +1,65 @@ +/* + * iSNS error strings etc. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include "isns.h" + +const char * +isns_strerror(enum isns_status status) +{ + switch (status) { + case ISNS_SUCCESS: + return "Success"; + case ISNS_UNKNOWN_ERROR: + return "Unknown error"; + case ISNS_MESSAGE_FORMAT_ERROR: + return "Message format error"; + case ISNS_INVALID_REGISTRATION: + return "Invalid registration"; + case ISNS_INVALID_QUERY: + return "Invalid query"; + case ISNS_SOURCE_UNKNOWN: + return "Source unknown"; + case ISNS_SOURCE_ABSENT: + return "Source absent"; + case ISNS_SOURCE_UNAUTHORIZED: + return "Source unauthorized"; + case ISNS_NO_SUCH_ENTRY: + return "No such entry"; + case ISNS_VERSION_NOT_SUPPORTED: + return "Version not supported"; + case ISNS_INTERNAL_ERROR: + return "Internal error"; + case ISNS_BUSY: + return "Busy"; + case ISNS_OPTION_NOT_UNDERSTOOD: + return "Option not understood"; + case ISNS_INVALID_UPDATE: + return "Invalid update"; + case ISNS_MESSAGE_NOT_SUPPORTED: + return "Message not supported"; + case ISNS_SCN_EVENT_REJECTED: + return "SCN event rejected"; + case ISNS_SCN_REGISTRATION_REJECTED: + return "SCN registration rejected"; + case ISNS_ATTRIBUTE_NOT_IMPLEMENTED: + return "Attribute not implemented"; + case ISNS_FC_DOMAIN_ID_NOT_AVAILABLE: + return "FC domain id not available"; + case ISNS_FC_DOMAIN_ID_NOT_ALLOCATED: + return "FC domain id not allocated"; + case ISNS_ESI_NOT_AVAILABLE: + return "ESI not available"; + case ISNS_INVALID_DEREGISTRATION: + return "Invalid deregistration"; + case ISNS_REGISTRATION_FEATURE_NOT_SUPPORTED: + return "Registration feature not supported"; + default: + break; + } + + return "Unknown iSNS status code"; +} + diff --git a/utils/open-isns/esi.c b/utils/open-isns/esi.c new file mode 100644 index 0000000..3fe62ac --- /dev/null +++ b/utils/open-isns/esi.c @@ -0,0 +1,575 @@ +/* + * Handle ESI events + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "attrs.h" +#include "objects.h" +#include "message.h" +#include "security.h" +#include "util.h" +#include "db.h" + +#define ESI_RETRANS_TIMEOUT 60 + +typedef struct isns_esi isns_esi_t; +typedef struct isns_esi_portal isns_esi_portal_t; + +struct isns_esi { + isns_list_t esi_list; + isns_object_t * esi_object; + isns_list_t esi_portals; + + unsigned int esi_update : 1; +}; + +struct isns_esi_portal { + isns_list_t esp_list; + isns_object_t * esp_object; + isns_portal_info_t esp_portal; + unsigned int esp_interval; + isns_portal_info_t esp_dest; + + isns_socket_t * esp_socket; + unsigned int esp_retries; + unsigned int esp_timeout; + time_t esp_start; + time_t esp_next_xmit; + uint32_t esp_xid; +}; + +int isns_esi_enabled = 0; +static isns_server_t * isns_esi_server = NULL; +static ISNS_LIST_DECLARE(isns_esi_list); + +static void isns_esi_transmit(void *); +static void isns_esi_sendto(isns_esi_t *, isns_esi_portal_t *); +static void isns_process_esi_response(uint32_t, isns_simple_t *); +static void isns_esi_disconnect(isns_esi_portal_t *); +static void isns_esi_restart(isns_esi_portal_t *); +static void isns_esi_drop_portal(isns_esi_portal_t *, isns_db_t *, int); +static void isns_esi_drop_entity(isns_esi_t *, isns_db_t *, int); +static int isns_esi_update(isns_esi_t *); +static void isns_esi_schedule(int); +static void isns_esi_callback(const isns_db_event_t *, void *); + +void +isns_esi_init(isns_server_t *srv) +{ + if (isns_config.ic_esi_retries == 0) { + isns_debug_esi("ESI disabled by administrator\n"); + } else { + unsigned int max_interval; + + isns_register_callback(isns_esi_callback, NULL); + isns_esi_schedule(0); + + max_interval = isns_config.ic_registration_period / 2; + if (isns_config.ic_esi_max_interval > max_interval) { + isns_warning("Max ESI interval adjusted to %u sec " + "to match registration period\n", + max_interval); + isns_config.ic_esi_max_interval = max_interval; + if (isns_config.ic_esi_min_interval > max_interval) + isns_config.ic_esi_min_interval = max_interval; + } + isns_esi_server = srv; + isns_esi_enabled = 1; + } +} + +/* + * Timer callback to send out ESI messages. + */ +void +isns_esi_transmit(void *ptr) +{ + isns_db_t *db = isns_esi_server->is_db; + isns_list_t *esi_pos, *esi_next; + time_t now; + isns_object_t *obj; + time_t next_timeout; + + now = time(NULL); + next_timeout = now + 3600; + + isns_list_foreach(&isns_esi_list, esi_pos, esi_next) { + isns_list_t *esp_pos, *esp_next; + isns_esi_t *esi = isns_list_item(isns_esi_t, esi_list, esi_pos); + + if (esi->esi_update) { + esi->esi_update = 0; + if (!isns_esi_update(esi)) + continue; + } + + isns_list_foreach(&esi->esi_portals, esp_pos, esp_next) { + isns_esi_portal_t *esp = isns_list_item(isns_esi_portal_t, + esp_list, esp_pos); + + /* Check whether the portal object still exist */ + obj = esp->esp_object; + if (obj->ie_state != ISNS_OBJECT_STATE_MATURE) { + isns_esi_drop_portal(esp, db, 0); + continue; + } + + if (esp->esp_next_xmit <= now) { + if (esp->esp_retries == 0) { + isns_debug_esi("No ESI response from %s - dropping\n", + isns_portal_string(&esp->esp_dest)); + isns_esi_drop_portal(esp, db, 1); + continue; + } + + esp->esp_retries -= 1; + esp->esp_next_xmit = now + esp->esp_timeout; + isns_esi_sendto(esi, esp); + } + if (esp->esp_next_xmit < next_timeout) + next_timeout = esp->esp_next_xmit; + } + + if (isns_list_empty(&esi->esi_portals)) + isns_esi_drop_entity(esi, db, 1); + } + + isns_debug_esi("Next ESI message in %d seconds\n", next_timeout - now); + isns_esi_schedule(next_timeout - now); +} + +/* + * Send an ESI message + */ +void +isns_esi_sendto(isns_esi_t *esi, isns_esi_portal_t *esp) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + isns_socket_t *sock; + isns_simple_t *msg; + + /* For TCP portals, kill the TCP socket every time. */ + if (esp->esp_dest.proto == IPPROTO_TCP) + isns_esi_disconnect(esp); + + if (esp->esp_socket == NULL) { + sock = isns_connect_to_portal(&esp->esp_dest); + if (sock == NULL) + return; + + isns_socket_set_security_ctx(sock, + isns_default_security_context(0)); + /* sock->is_disconnect_fatal = 1; */ + esp->esp_socket = sock; + } + + isns_attr_list_append_uint64(&attrs, + ISNS_TAG_TIMESTAMP, + time(NULL)); + /* The following will extract the ENTITY IDENTIFIER */ + isns_object_extract_keys(esi->esi_object, &attrs); + isns_portal_to_attr_list(&esp->esp_portal, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + &attrs); + + msg = isns_simple_create(ISNS_ENTITY_STATUS_INQUIRY, + NULL, &attrs); + if (msg == NULL) + return; + + isns_debug_esi("*** Sending ESI message to %s (xid=0x%x); %u retries left\n", + isns_portal_string(&esp->esp_dest), + msg->is_xid, esp->esp_retries); + isns_simple_transmit(esp->esp_socket, msg, + NULL, esp->esp_timeout - 1, + isns_process_esi_response); + esp->esp_xid = msg->is_xid; + isns_simple_free(msg); +} + +/* + * A new entity was added. See if it uses ESI, and create + * portals and such. + */ +static void +isns_esi_add_entity(isns_object_t *obj) +{ + isns_esi_t *esi; + + isns_debug_esi("Enable ESI monitoring for entity %u\n", obj->ie_index); + esi = isns_calloc(1, sizeof(*esi)); + esi->esi_object = isns_object_get(obj); + esi->esi_update = 1; + isns_list_init(&esi->esi_list); + isns_list_init(&esi->esi_portals); + + isns_list_append(&isns_esi_list, &esi->esi_list); +} + +/* + * Given an entity, see if we can find ESI state for it. + */ +static isns_esi_t * +isns_esi_find(isns_object_t *obj) +{ + isns_list_t *pos, *next; + + isns_list_foreach(&isns_esi_list, pos, next) { + isns_esi_t *esi = isns_list_item(isns_esi_t, esi_list, pos); + + if (esi->esi_object == obj) + return esi; + } + return NULL; +} + +/* + * Update the ESI state after an entity has changed + */ +static int +isns_esi_update(isns_esi_t *esi) +{ + isns_object_t *entity = esi->esi_object; + ISNS_LIST_DECLARE(hold); + isns_esi_portal_t *esp; + unsigned int i; + + isns_debug_esi("Updating ESI state for entity %u\n", entity->ie_index); + + isns_list_move(&hold, &esi->esi_portals); + for (i = 0; i < entity->ie_children.iol_count; ++i) { + isns_object_t *child = entity->ie_children.iol_data[i]; + isns_portal_info_t esi_portal, portal_info; + uint32_t esi_interval; + isns_list_t *pos, *next; + int changed = 0; + + if (!ISNS_IS_PORTAL(child)) + continue; + + if (!isns_portal_from_object(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + child) + || !isns_portal_from_object(&esi_portal, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_ESI_PORT, + child) + || !isns_object_get_uint32(child, + ISNS_TAG_ESI_INTERVAL, + &esi_interval)) + continue; + + isns_list_foreach(&hold, pos, next) { + esp = isns_list_item(isns_esi_portal_t, esp_list, pos); + + if (esp->esp_object == child) { + isns_debug_esi("Updating ESI state for %s\n", + isns_portal_string(&portal_info)); + isns_list_del(&esp->esp_list); + goto update; + } + } + + isns_debug_esi("Creating ESI state for %s\n", + isns_portal_string(&portal_info)); + esp = isns_calloc(1, sizeof(*esp)); + esp->esp_object = isns_object_get(child); + isns_list_init(&esp->esp_list); + changed = 1; + +update: + if (!isns_portal_equal(&esp->esp_portal, &portal_info)) { + esp->esp_portal = portal_info; + changed++; + } + if (!isns_portal_equal(&esp->esp_dest, &esi_portal)) { + isns_esi_disconnect(esp); + esp->esp_dest = esi_portal; + changed++; + } + if (esp->esp_interval != esi_interval) { + esp->esp_interval = esi_interval; + changed++; + } + + isns_esi_restart(esp); + + isns_list_append(&esi->esi_portals, &esp->esp_list); + } + + /* Destroy any old ESI portals */ + while (!isns_list_empty(&hold)) { + esp = isns_list_item(isns_esi_portal_t, esp_list, hold.next); + + isns_esi_drop_portal(esp, NULL, 0); + } + + /* If the client explicitly unregistered all ESI portals, + * stop monitoring it but *without* destroying the entity. */ + if (isns_list_empty(&esi->esi_portals)) { + isns_esi_drop_entity(esi, NULL, 0); + return 0; + } + + return 1; +} + +void +isns_esi_restart(isns_esi_portal_t *esp) +{ + unsigned int timeo; + + isns_esi_disconnect(esp); + + esp->esp_start = time(NULL); + esp->esp_retries = isns_config.ic_esi_retries; + esp->esp_next_xmit = esp->esp_start + esp->esp_interval; + esp->esp_xid = 0; + + timeo = esp->esp_interval / esp->esp_retries; + if (timeo == 0) + timeo = 1; + else if (timeo > ESI_RETRANS_TIMEOUT) + timeo = ESI_RETRANS_TIMEOUT; + esp->esp_timeout = timeo; +} + +void +isns_esi_disconnect(isns_esi_portal_t *esp) +{ + if (esp->esp_socket) + isns_socket_free(esp->esp_socket); + esp->esp_socket = NULL; +} + +/* + * Generic wrapper to dropping an object + */ +static inline void +__isns_esi_drop_object(isns_db_t *db, isns_object_t *obj, unsigned int dead) +{ + if (db && obj && obj->ie_state == ISNS_OBJECT_STATE_MATURE && dead) + isns_db_remove(db, obj); + isns_object_release(obj); +} + +/* + * Portal did not respond in time. Drop it + */ +void +isns_esi_drop_portal(isns_esi_portal_t *esp, isns_db_t *db, int dead) +{ + isns_debug_esi("ESI: dropping portal %s\n", + isns_portal_string(&esp->esp_portal)); + + isns_list_del(&esp->esp_list); + isns_esi_disconnect(esp); + __isns_esi_drop_object(db, esp->esp_object, dead); + isns_free(esp); +} + +/* + * We ran out of ESI portals for this entity. + */ +void +isns_esi_drop_entity(isns_esi_t *esi, isns_db_t *db, int dead) +{ + isns_debug_esi("ESI: dropping entity %u\n", + esi->esi_object->ie_index); + + isns_list_del(&esi->esi_list); + __isns_esi_drop_object(db, esi->esi_object, dead); + + while (!isns_list_empty(&esi->esi_portals)) { + isns_esi_portal_t *esp; + + esp = isns_list_item(isns_esi_portal_t, esp_list, + esi->esi_portals.next); + isns_esi_drop_portal(esp, db, dead); + } + isns_free(esi); +} + +/* + * When receiving an ESI response, find the portal we sent the + * original message to. + */ +static isns_esi_portal_t * +isns_esi_get_msg_portal(uint32_t xid, isns_esi_t **esip) +{ + isns_list_t *esi_pos, *esi_next; + + isns_list_foreach(&isns_esi_list, esi_pos, esi_next) { + isns_esi_t *esi = isns_list_item(isns_esi_t, esi_list, esi_pos); + isns_list_t *esp_pos, *esp_next; + + isns_list_foreach(&esi->esi_portals, esp_pos, esp_next) { + isns_esi_portal_t *esp = isns_list_item(isns_esi_portal_t, + esp_list, esp_pos); + + if (esp->esp_xid == xid) { + *esip = esi; + return esp; + } + } + } + + return NULL; +} + +/* + * Handle incoming ESI request + */ +int +isns_process_esi(isns_server_t *srv, isns_simple_t *call, isns_simple_t **reply) +{ + const isns_attr_list_t *attrs = &call->is_message_attrs; + isns_object_t *portal = NULL; + + /* We just echo back the attributes sent to us by the server, + * without further checking. */ + *reply = isns_simple_create(ISNS_ENTITY_STATUS_INQUIRY, + srv->is_source, attrs); + + /* Look up the portal and update its mtime. + * This can help the application find out if a portal has + * seen ESIs recently, and react. + */ + if (srv->is_db && attrs->ial_count == 4) { + const isns_attr_t *addr_attr, *port_attr; + + addr_attr = attrs->ial_data[2]; + port_attr = attrs->ial_data[3]; + if (addr_attr->ia_tag_id == ISNS_TAG_PORTAL_IP_ADDRESS + && port_attr->ia_tag_id == ISNS_TAG_PORTAL_TCP_UDP_PORT) { + isns_attr_list_t key; + + key.ial_count = 2; + key.ial_data = attrs->ial_data + 2; + portal = isns_db_lookup(srv->is_db, + &isns_portal_template, + &key); + } + + if (portal) + portal->ie_mtime = time(NULL); + } + return ISNS_SUCCESS; +} + +void +isns_process_esi_response(uint32_t xid, isns_simple_t *msg) +{ + isns_portal_info_t portal_info; + isns_esi_portal_t *esp; + isns_esi_t *esi; + + if (msg == NULL) { + isns_debug_esi("ESI call 0x%x timed out\n", xid); + return; + } + + /* FIXME: As a matter of security, we should probably + * verify that the ESI response originated from the + * portal we sent it to; or at least that it was authenticated + * by the client we think we're talking to. */ + + /* Get the portal */ + if (!isns_portal_from_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + &msg->is_message_attrs)) { + isns_debug_esi("Ignoring unintelligible ESI response\n"); + return; + } + + if (!(esp = isns_esi_get_msg_portal(xid, &esi))) { + isns_debug_esi("Ignoring unmatched ESI reply\n"); + return; + } + + if (!isns_portal_equal(&esp->esp_portal, &portal_info)) { + isns_warning("Faked ESI response for portal %s\n", + isns_portal_string(&portal_info)); + return; + } + + isns_debug_esi("Good ESI response from %s\n", + isns_portal_string(&portal_info)); + isns_esi_restart(esp); + + /* Refresh the entity's registration timestamp */ + isns_object_set_uint64(esi->esi_object, + ISNS_TAG_TIMESTAMP, + time(NULL)); + isns_db_sync(isns_esi_server->is_db); +} + +/* + * Helper function to schedule the next timeout + */ +static void +isns_esi_schedule(int timeout) +{ + isns_cancel_timer(isns_esi_transmit, NULL); + isns_add_oneshot_timer(timeout, isns_esi_transmit, NULL); +} + +/* + * Register an entity for ESI monitoring. + * This is called when reloading the database. + */ +void +isns_esi_register(isns_object_t *obj) +{ + if (!isns_esi_find(obj)) + isns_esi_add_entity(obj); + /* We do not call esi_schedule(0) here; that happens in + * isns_esi_init already. */ +} + +/* + * This callback is invoked whenever an object is added/removed/modified. + * We use this to keep track of ESI portals and such. + */ +void +isns_esi_callback(const isns_db_event_t *ev, void *ptr) +{ + isns_object_t *obj, *entity; + isns_esi_t *esi; + uint32_t event; + + obj = ev->ie_object; + event = ev->ie_bits; + + if (obj->ie_flags & ISNS_OBJECT_PRIVATE) + return; + + isns_debug_esi("isns_esi_callback(%p, 0x%x)\n", obj, event); + + if (ISNS_IS_ENTITY(obj) + && (event & ISNS_SCN_OBJECT_ADDED_MASK)) { + if (!isns_esi_find(obj)) + isns_esi_add_entity(obj); + /* Schedule an immediate ESI timer run */ + isns_esi_schedule(0); + return; + } + + if (!(entity = isns_object_get_entity(obj))) + return; + + esi = isns_esi_find(entity); + if (esi != NULL) + esi->esi_update = 1; + + /* Schedule an immediate ESI timer run */ + isns_esi_schedule(0); +} diff --git a/utils/open-isns/etc/isnsadm.conf b/utils/open-isns/etc/isnsadm.conf new file mode 100644 index 0000000..e7ee681 --- /dev/null +++ b/utils/open-isns/etc/isnsadm.conf @@ -0,0 +1,73 @@ +# +# Sample iSNS client configuration file +# + +# The source name. This is an iSCSI qualified name, +# and identifies the client uniquely. +# +# If left empty, the source name is derived from +# the client's hostname. +# +#SourceName = iqn.2006-01.com.example.host1 + +# Name and port of the iSNS server. +# Possible formats: +# foo.example.com +# foo.example.com:3205 +# 192.168.1.7:isns +# [2001:4e5f::1]:isns +# SLP: +# If the special string "SLP:" is given, Open-iSNS will +# query the SLP directory service to find the iSNS server. +#ServerAddress = isns.example.com + + +# Authentication enable/disable. +# When set to 1, the client will sign +# all messages, and expect all server messages +# to be signed. +# +# Authentication requires a valid private DSA +# key in AuthKeyFile, and the server's DSA public +# key in ServerKeyFile. +# +# The default is to use authentication if the +# requires keys are installed, and use unauthenticated +# iSNS otherwise. +#Security = 1 + +# Location of the client's private key. +# The file must contain a PEM encoded DSA key. +# The default is /etc/isns/auth_key +#AuthKeyFile = /etc/isns/auth_key + +# Location of the servers's public key. +# The file must contain a PEM encoded DSA key. +# The default is /etc/isns/server_key.pub +#ServerKeyFile = /etc/isns/server_key.pub + +# In order to prevent replay attacks, the +# authentication blocks carried by iSNS +# include a time stamp. The following two +# parameters control how we verify the +# time stamp +Auth.ReplayWindow = 2m +Auth.TimeStampJitter = 1s + +# Maximum number of incoming connections +# accepted. This usually applies to server +# side only, but is relevant if you create +# a passive TCP socket for ESI or SCN. +# Network.MaxSockets = 1024 + +# Time to wait for a TCP connection to be +# established. +# Network.ConnectTimeout = 60 + +# When a connection attempt failed, we wait +# before we try connecting again. +# Network.ReonnectTimeout = 10 + +# Total amount of time to wait before timing +# out a call to the iSNS server. +# Network.CallTimeout = 60 diff --git a/utils/open-isns/etc/isnsd.conf b/utils/open-isns/etc/isnsd.conf new file mode 100644 index 0000000..bc90f40 --- /dev/null +++ b/utils/open-isns/etc/isnsd.conf @@ -0,0 +1,129 @@ +# +# Sample iSNS Server configuration file +# + +# The source name. This is an iSCSI qualified name, +# and identifies the client uniquely. +# +# If left empty, the source name is derived from +# the client's hostname. +# +#SourceName = iqn.2006-01.com.example.host1 + +# Where to store the database. +# If you leave this empty, isnsd will keep its +# database in memory. +# Setting this to an absolute path name will +# make isnsd keep its database in a directory +# hierarchy below that directory. +Database = /var/lib/isns + +# The iSNS server can purge registered entities +# after a certain period of inactivity. This is +# called the registration period. +# Clients who register objects are supposed to +# refresh their registration within this period. +# +# The default value is 0, which disables this +# feature. +RegistrationPeriod = 10m + +# iSNS scopes visibility of other nodes using so-called +# Discovery Domains. A storage node A will only "see" +# storage node B, if both are members of the same +# discovery domain. +# +# So if a storage node is registered which is not part of +# any discovery domain, it will not see any other nodes. +# +# By setting DefaultDiscoveryDomain=1, you can tell isnsd to +# create a virtual "default discovery domain", which +# holds all nodes that are not part of any administratively +# configured discovery domain. +DefaultDiscoveryDomain = 1 + +# Make the iSNS server register itself with SLP. +# Clients will be able to discover the server by +# querying for service type "iscsi:sms", and a query +# of "(protocols=isns)" +SLPRegister = 1 + +# Authentication enable/disable. +# When set to 1, the client will sign +# all messages, and expect all server messages +# to be signed. +# +# Authentication requires a valid private DSA +# key in AuthKeyFile, and the server's DSA public +# key in ServerKeyFile. +# +# The default is to use authentication if the +# requires keys are installed, and use unauthenticated +# iSNS otherwise. +#Security = 1 + +# Location of the client's private key. +# The file must contain a PEM encoded DSA key. +# The default is /etc/isns/auth_key +#AuthKeyFile = /etc/isns/auth_key + +# Location of the servers's public key. +# The file must contain a PEM encoded DSA key. +# The default is /etc/isns/server_key.pub +#ServerKeyFile = /etc/isns/server_key.pub + +# This describes where the iSNS server stores +# authentication keys and policy information. +# Two options are currently supported: a +# simple key store (flat directory with public +# keys in PEM encoded files), and the iSNS +# database itself +#ClientKeyStore = /etc/isns/keystores +ClientKeyStore = DB: + +# When transmitting State Change Notification, +# we expect the client to ack them. If the +# ACK doesn't arrive in due time, we retransmit +# for a limited number of attempts, cycling +# through the available portals. +SCNTimeout = 60 +SCNRetries = 3 + +# Configuration of ESI. +# Defaults are +# ESIMaxInterval = 1h +# ESIMinInterval = 60s +# ESIRetries = 3 +# Setting ESIRetries to 0 disables ESI support, and makes +# the server reject any portal registrations that specify +# an ESI portal. +ESIMinInterval = 1m +ESIMaxInterval = 2m +ESIRetries = 3 + +# In order to prevent replay attacks, the +# authentication blocks carried by iSNS +# include a time stamp. The following two +# parameters control how we verify the +# time stamp +Auth.ReplayWindow = 2m +Auth.TimeStampJitter = 1s + +# Maximum number of incoming connections +# accepted. +# Network.MaxSockets = 1024 + +# Time to wait for a TCP connection to be +# established. +# (Client only) +# Network.ConnectTimeout = 60 + +# When a connection attempt failed, we wait +# before we try connecting again. +# (Client only) +# Network.ReonnectTimeout = 10 + +# Total amount of time to wait before timing +# out a call to the iSNS server. +# (Client only) +# Network.CallTimeout = 60 diff --git a/utils/open-isns/etc/isnsdd.conf b/utils/open-isns/etc/isnsdd.conf new file mode 100644 index 0000000..d751c3d --- /dev/null +++ b/utils/open-isns/etc/isnsdd.conf @@ -0,0 +1,72 @@ +# +# Sample iSNS Discovery Daemon configuration file +# + +# The source name. This is an iSCSI qualified name, +# and identifies the client uniquely. +# +# If left empty, the source name is derived from +# the client's hostname. +# +#SourceName = iqn.2006-01.com.example.host1:monitor + +# Name and port of the iSNS server. +# Possible formats: +# foo.example.com +# foo.example.com:3205 +# 192.168.1.7:isns +# [2001:4e5f::1]:isns +# SLP: +# If the special string "SLP:" is given, Open-iSNS will +# query the SLP directory service to find the iSNS server. +#ServerAddress = isns.example.com + +# Authentication enable/disable. +# When set to 1, the client will sign +# all messages, and expect all server messages +# to be signed. +# +# Authentication requires a valid private DSA +# key in AuthKeyFile, and the server's DSA public +# key in ServerKeyFile. +# +# The default is to use authentication if the +# required keys are installed, and use unauthenticated +# iSNS otherwise. +#Security = 1 + +# Location of the client's private key. +# The file must contain a PEM encoded DSA key. +# The default is /etc/isns/auth_key +#AuthKeyFile = /etc/isns/auth_key + +# Location of the servers's public key. +# The file must contain a PEM encoded DSA key. +# The default is /etc/isns/server_key.pub +#ServerKeyFile = /etc/isns/server_key.pub + +# In order to prevent replay attacks, the +# authentication blocks carried by iSNS +# include a time stamp. The following two +# parameters control how we verify the +# time stamp +Auth.ReplayWindow = 2m +Auth.TimeStampJitter = 1s + +# Maximum number of incoming connections +# accepted. This usually applies to server +# side only, but is relevant if you create +# a passive TCP socket for ESI or SCN. +# Network.MaxSockets = 1024 + +# Time to wait for a TCP connection to be +# established. +# Network.ConnectTimeout = 60 + +# When a connection attempt failed, we wait +# before we try connecting again. +# Network.ReonnectTimeout = 10 + +# Total amount of time to wait before timing +# out a call to the iSNS server. +# Network.CallTimeout = 60 diff --git a/utils/open-isns/etc/openisns.init b/utils/open-isns/etc/openisns.init new file mode 100644 index 0000000..7c03778 --- /dev/null +++ b/utils/open-isns/etc/openisns.init @@ -0,0 +1,71 @@ +#!/bin/sh +# +# Init script for Open-iSNS. +# +# Copyright (C) 2007 Albert Pauw +# +# chkconfig: 345 13 89 +# description: Starts and stops the iSCSI isns server +# +# processname: isnsd +# pidfile: /var/run/isnsd.pid +# config: /etc/isns/isnsd.conf + +# Source function library. +. /etc/init.d/functions + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +#OPTIONS="-4 -d all" +CONFIG="-c /etc/isns/isnsd.conf" +RETVAL=0 + +start() +{ + echo -n "Starting iSCSI isns service: " + daemon isnsd $OPTIONS $CONFIG + RETVAL=$? + success + echo + [ $RETVAL -eq 0 ] || return + touch /var/lock/subsys/open-isns +} + +stop() +{ + echo -n "Stopping iSCSI isns service: " + killproc isnsd + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/open-isns + success + echo + +} + +restart() +{ + stop + start +} + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + restart + ;; +status) + status isnsd + RETVAL=$? + ;; +condrestart) + [ -f /var/lock/subsys/open-isns ] && restart + ;; +*) + echo $"Usage: $0 {start|stop|restart|status|condrestart}" + exit 1 +esac + +exit $RETVAL diff --git a/utils/open-isns/export.c b/utils/open-isns/export.c new file mode 100644 index 0000000..fa4c278 --- /dev/null +++ b/utils/open-isns/export.c @@ -0,0 +1,547 @@ +/* + * Helper functions to represent iSNS objects as text, + * and/or to parse objects represented in textual form. + * These functions can be used by command line utilities + * such as isnsadm, as well as applications like iscsid + * or stgtd when talking to the iSNS discovery daemon. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "isns.h" +#include "util.h" +#include "vendor.h" +#include "attrs.h" +#include "security.h" +#include "objects.h" +#include "paths.h" + +#define MAX_ALIASES 4 + +struct isns_tag_prefix { + const char * name; + unsigned int name_len; + isns_object_template_t *context; +}; + +struct tag_name { + const char * name; + uint32_t tag; + struct isns_tag_prefix *prefix; + const char * alias[MAX_ALIASES]; +}; + +static struct isns_tag_prefix all_prefixes[__ISNS_OBJECT_TYPE_MAX] = { +[ISNS_OBJECT_TYPE_ENTITY] = { "entity-", 7, &isns_entity_template }, +[ISNS_OBJECT_TYPE_NODE] = { "iscsi-", 6, &isns_iscsi_node_template }, +[ISNS_OBJECT_TYPE_PORTAL] = { "portal-", 7, &isns_portal_template }, +[ISNS_OBJECT_TYPE_PG] = { "pg-", 3, &isns_iscsi_pg_template }, +[ISNS_OBJECT_TYPE_DD] = { "dd-", 3, &isns_dd_template }, +[ISNS_OBJECT_TYPE_POLICY] = { "policy-", 7, &isns_policy_template }, +}; + +static struct tag_name all_attrs[] = { +{ "id", ISNS_TAG_ENTITY_IDENTIFIER, + .alias = { "eid", }, +}, +{ "prot", ISNS_TAG_ENTITY_PROTOCOL }, +{ "idx", ISNS_TAG_ENTITY_INDEX }, + +{ "name", ISNS_TAG_ISCSI_NAME }, +{ "node-type", ISNS_TAG_ISCSI_NODE_TYPE }, +{ "alias", ISNS_TAG_ISCSI_ALIAS }, +{ "authmethod", ISNS_TAG_ISCSI_AUTHMETHOD }, +{ "idx", ISNS_TAG_ISCSI_NODE_INDEX }, + +{ "addr", ISNS_TAG_PORTAL_IP_ADDRESS }, +{ "port", ISNS_TAG_PORTAL_TCP_UDP_PORT }, +{ "name", ISNS_TAG_PORTAL_SYMBOLIC_NAME }, +{ "esi-port", ISNS_TAG_ESI_PORT }, +{ "esi-interval", ISNS_TAG_ESI_INTERVAL }, +{ "scn-port", ISNS_TAG_SCN_PORT }, +{ "idx", ISNS_TAG_PORTAL_INDEX }, + +{ "name", ISNS_TAG_PG_ISCSI_NAME }, +{ "addr", ISNS_TAG_PG_PORTAL_IP_ADDR }, +{ "port", ISNS_TAG_PG_PORTAL_TCP_UDP_PORT }, +{ "tag", ISNS_TAG_PG_TAG }, +{ "pgt", ISNS_TAG_PG_TAG }, +{ "idx", ISNS_TAG_PG_INDEX }, + +{ "id", ISNS_TAG_DD_ID }, +{ "name", ISNS_TAG_DD_SYMBOLIC_NAME }, +{ "member-name", ISNS_TAG_DD_MEMBER_ISCSI_NAME }, +{ "member-iscsi-idx", ISNS_TAG_DD_MEMBER_ISCSI_INDEX }, +{ "member-fc-name", ISNS_TAG_DD_MEMBER_FC_PORT_NAME }, +{ "member-portal-idx", ISNS_TAG_DD_MEMBER_PORTAL_INDEX }, +{ "member-addr", ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR }, +{ "member-port", ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT }, +{ "features", ISNS_TAG_DD_FEATURES }, + +{ "name", OPENISNS_TAG_POLICY_SPI, + .alias = { "spi" }, +}, +{ "key", OPENISNS_TAG_POLICY_KEY }, +{ "entity", OPENISNS_TAG_POLICY_ENTITY }, +{ "object-type", OPENISNS_TAG_POLICY_OBJECT_TYPE }, +{ "node-type", OPENISNS_TAG_POLICY_NODE_TYPE }, +{ "node-name", OPENISNS_TAG_POLICY_NODE_NAME }, +{ "functions", OPENISNS_TAG_POLICY_FUNCTIONS }, + +{ NULL } +}; + +/* + * Initialize tag array + */ +static void +init_tags(void) +{ + struct tag_name *t; + + for (t = all_attrs; t->name; ++t) { + isns_object_template_t *tmpl; + + tmpl = isns_object_template_for_tag(t->tag); + if (tmpl == NULL) + isns_fatal("Bug: cannot find object type for tag %s\n", + t->name); + t->prefix = &all_prefixes[tmpl->iot_handle]; + } +} + +/* + * Match prefix + */ +static struct isns_tag_prefix * +find_prefix(const char *name) +{ + struct isns_tag_prefix *p; + unsigned int i; + + for (i = 0, p = all_prefixes; i < __ISNS_OBJECT_TYPE_MAX; ++i, ++p) { + if (p->name && !strncmp(name, p->name, p->name_len)) + return p; + } + return NULL; +} + +/* + * Look up the tag for a given attribute name. + * By default, attr names come with a disambiguating + * prefix that defines the object type the attribute applies + * to, such as "entity-" or "portal-". Once a context has + * been established (ie we know the object type subsequent + * attributes apply to), specifying the prefix is optional. + * + * For instance, in a portal context, "addr=10.1.1.1 port=616 name=foo" + * specifies three portal related attributes. Whereas in a portal + * group context, the same string would specify three portal group + * related attributes. To disambiguate, the first attribute in + * this list should be prefixed by "portal-" or "pg-", respectively. + */ +static uint32_t +tag_by_name(const char *name, struct isns_attr_list_parser *st) +{ + const char *orig_name = name; + unsigned int nmatch = 0, i; + struct tag_name *t, *match[8]; + struct isns_tag_prefix *specific = NULL; + + if (all_attrs[0].prefix == NULL) + init_tags(); + + specific = find_prefix(name); + if (specific != NULL) { + if (st->prefix + && st->prefix != specific + && !st->multi_type_permitted) { + isns_error("Cannot mix attributes of different types\n"); + return 0; + } + name += specific->name_len; + st->prefix = specific; + } + + for (t = all_attrs; t->name; ++t) { + if (specific && t->prefix != specific) + continue; + if (!st->multi_type_permitted + && st->prefix && t->prefix != st->prefix) + continue; + if (!strcmp(name, t->name)) + goto match; + for (i = 0; i < MAX_ALIASES && t->alias[i]; ++i) { + if (!strcmp(name, t->alias[i])) + goto match; + } + continue; + +match: + if (nmatch < 8) + match[nmatch++] = t; + } + + if (nmatch > 1) { + char conflict[128]; + unsigned int i; + + conflict[0] = '\0'; + for (i = 0; i < nmatch; ++i) { + if (i) + strcat(conflict, ", "); + t = match[i]; + strcat(conflict, t->prefix->name); + strcat(conflict, t->name); + } + isns_error("tag name \"%s\" not unique in this context " + "(could be one of %s)\n", + orig_name, conflict); + return 0; + } + + if (nmatch == 0) { + isns_error("tag name \"%s\" not known in this context\n", + orig_name); + return 0; + } + + st->prefix = match[0]->prefix; + return match[0]->tag; +} + +static const char * +name_by_tag(uint32_t tag, struct isns_attr_list_parser *st) +{ + struct tag_name *t; + + for (t = all_attrs; t->name; ++t) { + if (st->prefix && t->prefix != st->prefix) + continue; + if (t->tag == tag) + return t->name; + } + return NULL; +} + +static int +parse_one_attr(const char *name, const char *value, + isns_attr_list_t *attrs, + struct isns_attr_list_parser *st) +{ + isns_attr_t *attr; + uint32_t tag; + + /* Special case: "portal=<address:port>" is translated to + * addr=<address> port=<port> + * If no context has been set, assume portal context. + */ + if (!strcasecmp(name, "portal")) { + isns_portal_info_t portal_info; + uint32_t addr_tag, port_tag; + + if (st->prefix == NULL) { + addr_tag = tag_by_name("portal-addr", st); + port_tag = tag_by_name("portal-port", st); + } else { + addr_tag = tag_by_name("addr", st); + port_tag = tag_by_name("port", st); + } + + if (!addr_tag || !port_tag) { + isns_error("portal=... not supported in this context\n"); + return 0; + } + if (value == NULL) { + isns_attr_list_append_nil(attrs, addr_tag); + isns_attr_list_append_nil(attrs, port_tag); + return 1; + } + if (!isns_portal_parse(&portal_info, value, st->default_port)) + return 0; + isns_portal_to_attr_list(&portal_info, addr_tag, port_tag, attrs); + return 1; + } + + if (!(tag = tag_by_name(name, st))) + return 0; + + /* Special handling for key objects */ + if (tag == OPENISNS_TAG_POLICY_KEY) { + if (!value || !strcasecmp(value, "gen")) { + if (st->generate_key == NULL) { + isns_error("Key generation not supported in this context\n"); + return 0; + } + attr = st->generate_key(); + } else { + if (st->load_key == NULL) { + isns_error("Policy-key attribute not supported in this context\n"); + return 0; + } + attr = st->load_key(value); + } + goto append_attr; + } + + if (value == NULL) { + isns_attr_list_append_nil(attrs, tag); + return 1; + } + + attr = isns_attr_from_string(tag, value); + if (!attr) + return 0; + +append_attr: + isns_attr_list_append_attr(attrs, attr); + return 1; +} + +void +isns_attr_list_parser_init(struct isns_attr_list_parser *st, + isns_object_template_t *tmpl) +{ + if (all_attrs[0].prefix == NULL) + init_tags(); + + memset(st, 0, sizeof(*st)); + if (tmpl) + st->prefix = &all_prefixes[tmpl->iot_handle]; +} + +int +isns_attr_list_split(char *line, char **argv, unsigned int argc_max) +{ + char *src = line; + unsigned int argc = 0, quoted = 0; + + if (!line) + return 0; + + while (1) { + char *dst; + + while (isspace(*src)) + ++src; + if (!*src) + break; + + argv[argc] = dst = src; + while (*src) { + char cc = *src++; + + if (cc == '"') { + quoted = !quoted; + continue; + } + if (!quoted && isspace(cc)) { + *dst = '\0'; + break; + } + *dst++ = cc; + } + + if (quoted) { + isns_error("%s: Unterminated quoted string: \"%s\"\n", + __FUNCTION__, argv[argc]); + return -1; + } + argc++; + } + + return argc; +} + +int +isns_parse_attrs(unsigned int argc, char **argv, + isns_attr_list_t *attrs, + struct isns_attr_list_parser *st) +{ + unsigned int i; + + for (i = 0; i < argc; ++i) { + char *name, *value; + + name = argv[i]; + if ((value = strchr(name, '=')) != NULL) + *value++ = '\0'; + + if (!value && !st->nil_permitted) { + isns_error("Missing value for atribute %s\n", name); + return 0; + } + + if (!parse_one_attr(name, value, attrs, st)) { + isns_error("Unable to parse %s=%s\n", name, value); + return 0; + } + } + + return 1; +} + +/* + * Query strings may contain a mix of query keys (foo=bar), + * and requested attributes (?foo). The former are used by + * the server in its object search, whereas the latter instruct + * it which attributes to return. + */ +int +isns_parse_query_attrs(unsigned int argc, char **argv, + isns_attr_list_t *keys, + isns_attr_list_t *requested_attrs, + struct isns_attr_list_parser *st) +{ + struct isns_attr_list_parser query_state; + unsigned int i; + + query_state = *st; + query_state.multi_type_permitted = 1; + + for (i = 0; i < argc; ++i) { + char *name, *value; + + name = argv[i]; + if ((value = strchr(name, '=')) != NULL) + *value++ = '\0'; + + if (name[0] == '?') { + uint32_t tag; + + if (value) { + isns_error("No value allowed for query attribute %s\n", + name); + return 0; + } + + if ((tag = tag_by_name(name + 1, &query_state)) != 0) { + isns_attr_list_append_nil(requested_attrs, tag); + continue; + } + } else { + if (!value && !st->nil_permitted) { + isns_error("Missing value for atribute %s\n", name); + return 0; + } + + if (parse_one_attr(name, value, keys, st)) + continue; + } + + isns_error("Unable to parse %s=%s\n", name, value); + return 0; + } + + return 1; +} + +void +isns_attr_list_parser_help(struct isns_attr_list_parser *st) +{ + isns_object_template_t *tmpl, *current = NULL; + struct tag_name *t; + + if (all_attrs[0].prefix == NULL) + init_tags(); + + for (t = all_attrs; t->name; ++t) { + const isns_tag_type_t *tag_type; + char namebuf[64]; + const char *help; + unsigned int i; + + if (st && !st->multi_type_permitted + && st->prefix && t->prefix != st->prefix) + continue; + + tmpl = t->prefix->context; + if (tmpl != current) { + printf("\nAttributes for object type %s; using prefix %s\n", + tmpl->iot_name, t->prefix->name); + current = tmpl; + } + + snprintf(namebuf, sizeof(namebuf), "%s%s", t->prefix->name, t->name); + printf(" %-20s ", namebuf); + + tag_type = isns_tag_type_by_id(t->tag); + if (tag_type == NULL) { + printf("Unknown\n"); + continue; + } + printf("%s (%s", tag_type->it_name, + tag_type->it_type->it_name); + + if (tag_type->it_readonly) + printf("; readonly"); + if (tag_type->it_multiple) + printf("; multiple instances"); + printf(")"); + + help = NULL; + if (t->tag == OPENISNS_TAG_POLICY_KEY) { + help = "name of key file, or \"gen\" for key generation"; + } else + if (tag_type->it_help) + help = tag_type->it_help(); + + if (help) { + if (strlen(help) < 20) + printf(" [%s]", help); + else + printf("\n%25s[%s]", "", help); + } + printf("\n"); + + if (t->alias[0]) { + printf("%25sAliases:", ""); + for (i = 0; i < MAX_ALIASES && t->alias[i]; ++i) + printf(" %s", t->alias[i]); + printf("\n"); + } + } +} + +isns_object_template_t * +isns_attr_list_parser_context(const struct isns_attr_list_parser *st) +{ + if (st->prefix) + return st->prefix->context; + return NULL; +} + +int +isns_print_attrs(isns_object_t *obj, char **argv, unsigned int argsmax) +{ + struct isns_attr_list_parser st; + unsigned int i, argc = 0; + + isns_attr_list_parser_init(&st, obj->ie_template); + + for (i = 0; i < obj->ie_attrs.ial_count; ++i) { + isns_attr_t *attr = obj->ie_attrs.ial_data[i]; + char argbuf[512], value[512]; + const char *name; + + name = name_by_tag(attr->ia_tag_id, &st); + if (name == NULL) + continue; + if (argc + 1 >= argsmax) + break; + + snprintf(argbuf, sizeof(argbuf), "%s%s=%s", + st.prefix->name, name, + isns_attr_print_value(attr, value, sizeof(value))); + argv[argc++] = isns_strdup(argbuf); + } + + argv[argc] = NULL; + return argc; +} diff --git a/utils/open-isns/getnext.c b/utils/open-isns/getnext.c new file mode 100644 index 0000000..916ee80 --- /dev/null +++ b/utils/open-isns/getnext.c @@ -0,0 +1,257 @@ +/* + * Handle iSNS DevGetNext + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "message.h" +#include "security.h" +#include "objects.h" +#include "db.h" +#include "util.h" + +/* + * Create a GetNext query, and set the source name + */ +static isns_simple_t * +__isns_create_getnext(isns_source_t *source, + const isns_attr_list_t *key, + const isns_attr_list_t *scope) +{ + isns_simple_t *simp; + + simp = isns_simple_create(ISNS_DEVICE_GET_NEXT, source, key); + if (simp && scope) + isns_attr_list_copy(&simp->is_operating_attrs, + scope); + return simp; +} + +isns_simple_t * +isns_create_getnext(isns_client_t *clnt, + isns_object_template_t *tmpl, + const isns_attr_list_t *scope) +{ + isns_simple_t *simp; + unsigned int i; + + simp = __isns_create_getnext(clnt->ic_source, NULL, scope); + if (simp == NULL) + return NULL; + + for (i = 0; i < tmpl->iot_num_keys; ++i) { + isns_attr_list_append_nil(&simp->is_message_attrs, + tmpl->iot_keys[i]); + } + return simp; +} + +isns_simple_t * +isns_create_getnext_followup(isns_client_t *clnt, + const isns_simple_t *resp, + const isns_attr_list_t *scope) +{ + return __isns_create_getnext(clnt->ic_source, + &resp->is_message_attrs, scope); +} + +/* + * Get the list of objects matching this query + */ +static int +isns_getnext_get_object(isns_simple_t *qry, isns_db_t *db, + isns_object_t **result) +{ + isns_scope_t *scope; + isns_attr_list_t *keys = &qry->is_message_attrs, match; + isns_object_template_t *tmpl; + unsigned int i; + + /* + * 5.6.5.3. + * The Message Key Attribute may be an Entity Identifier (EID), + * iSCSI Name, iSCSI Index, Portal IP Address and TCP/UDP Port, + * Portal Index, PG Index, FC Node Name WWNN, or FC Port Name + * WWPN. + * + * Implementer's comment: In other words, it must be the + * key attr(s) of a specific object type, or an index attribute. + */ + if ((tmpl = isns_object_template_for_key_attrs(keys)) != NULL) { + if (keys->ial_count != tmpl->iot_num_keys) + return ISNS_INVALID_QUERY; + } else if (keys->ial_count == 1) { + isns_attr_t *attr = keys->ial_data[0]; + + tmpl = isns_object_template_for_index_tag(attr->ia_tag_id); + } + if (tmpl == NULL) + return ISNS_INVALID_QUERY; + + /* Verify whether the client is permitted to retrieve + * objects of the given type. */ + if (!isns_policy_validate_object_type(qry->is_policy, tmpl, + qry->is_function)) + return ISNS_SOURCE_UNAUTHORIZED; + + /* + * 5.6.5.3. + * The Operating Attributes can be used to specify the scope + * of the DevGetNext request, and to specify the attributes of + * the next object, which are to be returned in the DevGetNext + * response message. All Operating Attributes MUST be attributes + * of the object type identified by the Message Key. + */ + match = qry->is_operating_attrs; + for (i = 0; i < match.ial_count; ++i) { + isns_attr_t *attr = match.ial_data[i]; + + if (tmpl != isns_object_template_for_tag(attr->ia_tag_id)) + return ISNS_INVALID_QUERY; + } + + /* + * 5.6.5.3. + * Non-zero-length TLV attributes in the Operating Attributes + * are used to scope the DevGetNext message. + * [...] + * Zero-length TLV attributes MUST be listed after non-zero-length + * attributes in the Operating Attributes of the DevGetNext + * request message. + */ + for (i = 0; i < match.ial_count; ++i) { + if (ISNS_ATTR_IS_NIL(match.ial_data[i])) { + match.ial_count = i; + break; + } + } + + /* Get the scope for the originating node. */ + scope = isns_scope_for_call(db, qry); + + *result = isns_scope_get_next(scope, tmpl, keys, &match); + + isns_scope_release(scope); + + if (*result == NULL) + return ISNS_NO_SUCH_ENTRY; + return ISNS_SUCCESS; +} + +/* + * Create a Query Response + */ +static isns_simple_t * +isns_create_getnext_response(isns_source_t *source, + const isns_simple_t *qry, isns_object_t *obj) +{ + const isns_attr_list_t *req_attrs = NULL; + isns_attr_list_t requested; + isns_simple_t *resp; + unsigned int i; + + resp = __isns_create_getnext(source, NULL, NULL); + + /* + * 5.7.5.3. Device Get Next Response (DevGetNextRsp) + * The Message Key Attribute field returns the object keys + * for the next object after the Message Key Attribute in the + * original DevGetNext message. + * + * Implementer's note: slightly convoluted English here. + * I *think* this means the key attributes of the object + * we matched. + */ + if (!isns_object_get_key_attrs(obj, &resp->is_message_attrs)) + return NULL; + + /* + * 5.7.5.3. + * The Operating Attribute field returns the Operating Attributes + * of the next object as requested in the original DevGetNext + * message. The values of the Operating Attributes are those + * associated with the object identified by the Message Key + * Attribute field of the DevGetNextRsp message. + * + * Implementer's note: the RFC doesn't say clearly what to + * do when the list of operating attributes does not + * contain any NIL TLVs. Let's default to the same + * behavior as elsewhere, and return all attributes + * in this case. + */ + req_attrs = &qry->is_operating_attrs; + for (i = 0; i < req_attrs->ial_count; ++i) { + if (ISNS_ATTR_IS_NIL(req_attrs->ial_data[i])) + break; + } + requested.ial_count = req_attrs->ial_count - i; + requested.ial_data = req_attrs->ial_data + i; + if (requested.ial_count) + req_attrs = &requested; + else + req_attrs = NULL; + + isns_object_get_attrlist(obj, + &resp->is_operating_attrs, + req_attrs); + return resp; +} + +/* + * Process a GetNext request + */ +int +isns_process_getnext(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_simple_t *reply = NULL; + isns_object_t *obj = NULL; + isns_db_t *db = srv->is_db; + int status; + + /* Get the next object */ + status = isns_getnext_get_object(call, db, &obj); + if (status != ISNS_SUCCESS) + goto done; + + /* If it's a virtual object, rebuild it */ + if (obj->ie_rebuild) + obj->ie_rebuild(obj, srv->is_db); + + /* Success: create a new simple message, and + * send it in our reply. */ + reply = isns_create_getnext_response(srv->is_source, call, obj); + if (reply == NULL) + status = ISNS_INTERNAL_ERROR; + +done: + if (obj) + isns_object_release(obj); + *result = reply; + return status; +} + +/* + * Parse the object in a getnext response + */ +int +isns_getnext_response_get_object(isns_simple_t *qry, + isns_object_t **result) +{ + isns_object_template_t *tmpl; + + tmpl = isns_object_template_for_key_attrs(&qry->is_operating_attrs); + if (tmpl == NULL) { + isns_error("Cannot determine object type in GetNext response\n"); + return ISNS_ATTRIBUTE_NOT_IMPLEMENTED; + } + + *result = isns_create_object(tmpl, + &qry->is_operating_attrs, + NULL); + return ISNS_SUCCESS; +} + diff --git a/utils/open-isns/internal.h b/utils/open-isns/internal.h new file mode 100644 index 0000000..fb80b48 --- /dev/null +++ b/utils/open-isns/internal.h @@ -0,0 +1,16 @@ +/* + * iSNS implementation - internal functions and types + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_INTERNAL_H +#define ISNS_INTERNAL_H + +extern char * isns_slp_build_url(uint16_t); +extern int isns_slp_register(const char *); +extern int isns_slp_unregister(const char *); +extern char * isns_slp_find(void); + +#endif /* ISNS_INTERNAL_H */ + diff --git a/utils/open-isns/isns-proto.h b/utils/open-isns/isns-proto.h new file mode 100644 index 0000000..1e2f682 --- /dev/null +++ b/utils/open-isns/isns-proto.h @@ -0,0 +1,258 @@ +/* + * iSNS protocol definitions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_PROTO_H +#define ISNS_PROTO_H + +#include <stdint.h> + +struct isns_hdr { + uint16_t i_version; + uint16_t i_function; + uint16_t i_length; + uint16_t i_flags; + uint16_t i_xid; + uint16_t i_seq; +}; + +#define ISNS_VERSION 0x0001 +#define ISNS_MAX_PDU_SIZE 65535 + +/* + * Values for the i_flags field: + */ +#define ISNS_F_CLIENT 0x8000 +#define ISNS_F_SERVER 0x4000 +#define ISNS_F_AUTHBLK_PRESENT 0x2000 +#define ISNS_F_REPLACE 0x1000 +#define ISNS_F_LAST_PDU 0x0800 +#define ISNS_F_FIRST_PDU 0x0400 + +/* + * Function values + */ +enum isns_function { + ISNS_DEVICE_ATTRIBUTE_REGISTER = 1, + ISNS_DEVICE_ATTRIBUTE_QUERY = 2, + ISNS_DEVICE_GET_NEXT = 3, + ISNS_DEVICE_DEREGISTER = 4, + ISNS_SCN_REGISTER = 5, + ISNS_SCN_DEREGISTER = 6, + ISNS_SCN_EVENT = 7, + ISNS_STATE_CHANGE_NOTIFICATION = 8, + ISNS_DD_REGISTER = 9, + ISNS_DD_DEREGISTER = 10, + ISNS_DDS_REGISTER = 11, + ISNS_DDS_DEREGISTER = 12, + ISNS_ENTITY_STATUS_INQUIRY = 13, + ISNS_HEARTBEAT = 14, +}; + +/* + * iSNS status codes: + */ +enum isns_status { + ISNS_SUCCESS = 0, + ISNS_UNKNOWN_ERROR, + ISNS_MESSAGE_FORMAT_ERROR, + ISNS_INVALID_REGISTRATION, + __ISNS_RESERVED_STATUS, + ISNS_INVALID_QUERY, + ISNS_SOURCE_UNKNOWN, + ISNS_SOURCE_ABSENT, + ISNS_SOURCE_UNAUTHORIZED, + ISNS_NO_SUCH_ENTRY, + ISNS_VERSION_NOT_SUPPORTED, + ISNS_INTERNAL_ERROR, + ISNS_BUSY, + ISNS_OPTION_NOT_UNDERSTOOD, + ISNS_INVALID_UPDATE, + ISNS_MESSAGE_NOT_SUPPORTED, + ISNS_SCN_EVENT_REJECTED, + ISNS_SCN_REGISTRATION_REJECTED, + ISNS_ATTRIBUTE_NOT_IMPLEMENTED, + ISNS_FC_DOMAIN_ID_NOT_AVAILABLE, + ISNS_FC_DOMAIN_ID_NOT_ALLOCATED, + ISNS_ESI_NOT_AVAILABLE, + ISNS_INVALID_DEREGISTRATION, + ISNS_REGISTRATION_FEATURE_NOT_SUPPORTED, +}; + +enum isns_tag { + ISNS_TAG_DELIMITER = 0, + ISNS_TAG_ENTITY_IDENTIFIER = 1, + ISNS_TAG_ENTITY_PROTOCOL = 2, + ISNS_TAG_MGMT_IP_ADDRESS = 3, + ISNS_TAG_TIMESTAMP = 4, + ISNS_TAG_PROTOCOL_VERSION_RANGE = 5, + ISNS_TAG_REGISTRATION_PERIOD = 6, + ISNS_TAG_ENTITY_INDEX = 7, + ISNS_TAG_ENTITY_NEXT_INDEX = 8, + ISNS_TAG_ENTITY_ISAKMP_PHASE_1 = 11, + ISNS_TAG_ENTITY_CERTIFICATE = 12, + ISNS_TAG_PORTAL_IP_ADDRESS = 16, + ISNS_TAG_PORTAL_TCP_UDP_PORT = 17, + ISNS_TAG_PORTAL_SYMBOLIC_NAME = 18, + ISNS_TAG_ESI_INTERVAL = 19, + ISNS_TAG_ESI_PORT = 20, + ISNS_TAG_PORTAL_INDEX = 22, + ISNS_TAG_SCN_PORT = 23, + ISNS_TAG_PORTAL_NEXT_INDEX = 24, + ISNS_TAG_PORTAL_SECURITY_BITMAP = 27, + ISNS_TAG_PORTAL_ISAKMP_PHASE_1 = 28, + ISNS_TAG_PORTAL_ISAKMP_PHASE_2 = 29, + ISNS_TAG_PORTAL_CERTIFICATE = 31, + ISNS_TAG_ISCSI_NAME = 32, + ISNS_TAG_ISCSI_NODE_TYPE = 33, + ISNS_TAG_ISCSI_ALIAS = 34, + ISNS_TAG_ISCSI_SCN_BITMAP = 35, + ISNS_TAG_ISCSI_NODE_INDEX = 36, + ISNS_TAG_WWNN_TOKEN = 37, + ISNS_TAG_ISCSI_NODE_NEXT_INDEX = 38, + ISNS_TAG_ISCSI_AUTHMETHOD = 42, + ISNS_TAG_PG_ISCSI_NAME = 48, + ISNS_TAG_PG_PORTAL_IP_ADDR = 49, + ISNS_TAG_PG_PORTAL_TCP_UDP_PORT = 50, + ISNS_TAG_PG_TAG = 51, + ISNS_TAG_PG_INDEX = 52, + ISNS_TAG_PG_NEXT_INDEX = 53, + ISNS_TAG_FC_PORT_NAME_WWPN = 64, + ISNS_TAG_PORT_ID = 65, + ISNS_TAG_FC_PORT_TYPE = 66, + ISNS_TAG_SYMBOLIC_PORT_NAME = 67, + ISNS_TAG_FABRIC_PORT_NAME = 68, + ISNS_TAG_HARD_ADDRESS = 69, + ISNS_TAG_PORT_IP_ADDRESS = 70, + ISNS_TAG_CLASS_OF_SERVICE = 71, + ISNS_TAG_FC4_TYPES = 72, + ISNS_TAG_FC4_DESCRIPTOR = 73, + ISNS_TAG_FC4_FEATURES = 74, + ISNS_TAG_IFCP_SCN_BITMAP = 75, + ISNS_TAG_PORT_ROLE = 76, + ISNS_TAG_PERMANENT_PORT_NAME = 77, + ISNS_TAG_FC4_TYPE_CODE = 95, + ISNS_TAG_FC_NODE_NAME_WWNN = 96, + ISNS_TAG_SYMBOLIC_NODE_NAME = 97, + ISNS_TAG_NODE_IP_ADDRESS = 98, + ISNS_TAG_NODE_IPA = 99, + ISNS_TAG_PROXY_ISCSI_NAME = 101, + ISNS_TAG_SWITCH_NAME = 128, + ISNS_TAG_PREFERRED_ID = 129, + ISNS_TAG_ASSIGNED_ID = 130, + ISNS_TAG_VIRTUAL_FABRIC_ID = 131, + ISNS_TAG_SERVER_VENDOR_OUI = 256, + ISNS_TAG_DD_SET_ID = 2049, + ISNS_TAG_DD_SET_SYMBOLIC_NAME = 2050, + ISNS_TAG_DD_SET_STATUS = 2051, + ISNS_TAG_DD_SET_NEXT_ID = 2052, + ISNS_TAG_DD_ID = 2065, + ISNS_TAG_DD_SYMBOLIC_NAME = 2066, + ISNS_TAG_DD_MEMBER_ISCSI_INDEX = 2067, + ISNS_TAG_DD_MEMBER_ISCSI_NAME = 2068, + ISNS_TAG_DD_MEMBER_FC_PORT_NAME = 2069, + ISNS_TAG_DD_MEMBER_PORTAL_INDEX = 2070, + ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR = 2071, + ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT = 2072, + ISNS_TAG_DD_FEATURES = 2078, + ISNS_TAG_DD_NEXT_ID = 2079, + + __ISNS_TAG_MAX, + + ISNS_VENDOR_SPECIFIC_SERVER_BASE = 257, /* end 384 */ + ISNS_VENDOR_SPECIFIC_ENTITY_BASE = 385, /* end 512 */ + ISNS_VENDOR_SPECIFIC_PORTAL_BASE = 513, /* end 640 */ + ISNS_VENDOR_SPECIFIC_NODE_BASE = 641, /* end 768 */ + ISNS_VENDOR_SPECIFIC_DD_BASE = 1024, /* end 1280 */ + ISNS_VENDOR_SPECIFIC_DDSET_BASE = 1281, /* end 1536 */ + ISNS_VENDOR_SPECIFIC_OTHER_BASE = 1537, /* end 2048 */ +}; + +typedef enum isns_entity_protocol { + ISNS_ENTITY_PROTOCOL_NONE = 1, + ISNS_ENTITY_PROTOCOL_ISCSI = 2, + ISNS_ENTITY_PROTOCOL_IFCP = 3, +} isns_entity_protocol_t; + +enum isns_iscsi_node_type_bits { + ISNS_ISCSI_NODE_TYPE_TARGET = 0, + ISNS_ISCSI_NODE_TYPE_INITIATOR = 1, + ISNS_ISCSI_NODE_TYPE_CONTROL = 2, +}; +#define ISNS_ISCSI_INITIATOR_MASK (1 << ISNS_ISCSI_NODE_TYPE_INITIATOR) +#define ISNS_ISCSI_TARGET_MASK (1 << ISNS_ISCSI_NODE_TYPE_TARGET) +#define ISNS_ISCSI_CONTROL_MASK (1 << ISNS_ISCSI_NODE_TYPE_CONTROL) + +enum isns_portal_port_bits { + ISNS_PORTAL_PORT_UDP = 16, +}; +#define ISNS_PORTAL_PORT_UDP_MASK (1 << ISNS_PORTAL_PORT_UDP) + +enum isns_portal_security_bits { + ISNS_PORTAL_SEC_BITMAP_VALID = 0, + ISNS_PORTAL_SEC_IPSEC_ENABLED = 1, + ISNS_PORTAL_SEC_MAIN_MODE_ENABLED = 2, + ISNS_PORTAL_SEC_AGGR_MODE_ENABLED = 3, + ISNS_PORTAL_SEC_PFS_ENABLED = 4, + ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED = 5, + ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED = 6, +}; +#define ISNS_PORTAL_SEC_BITMAP_VALID_MASK (1 << ISNS_PORTAL_SEC_BITMAP_VALID) +#define ISNS_PORTAL_SEC_IPSEC_ENABLED_MASK (1 << ISNS_PORTAL_SEC_IPSEC_ENABLED) +#define ISNS_PORTAL_SEC_MAIN_MODE_ENABLED_MASK (1 << ISNS_PORTAL_SEC_MAIN_MODE_ENABLED) +#define ISNS_PORTAL_SEC_AGGR_MODE_ENABLED_MASK (1 << ISNS_PORTAL_SEC_AGGR_MODE_ENABLED) +#define ISNS_PORTAL_SEC_PFS_ENABLED_MASK (1 << ISNS_PORTAL_SEC_PFS_ENABLED) +#define ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED_MASK (1 << ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED) +#define ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED_MASK (1 << ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED) + +enum isns_scn_bits { + ISNS_SCN_DD_MEMBER_ADDED = 0, + ISNS_SCN_DD_MEMBER_REMOVED = 1, + ISNS_SCN_OBJECT_UPDATED = 2, + ISNS_SCN_OBJECT_ADDED = 3, + ISNS_SCN_OBJECT_REMOVED = 4, + ISNS_SCN_MANAGEMENT_REGISTRATION = 5, + ISNS_SCN_TARGET_AND_SELF_ONLY = 6, + ISNS_SCN_INITIATOR_AND_SELF_ONLY = 7, +}; +#define ISNS_SCN_DD_MEMBER_ADDED_MASK (1 << ISNS_SCN_DD_MEMBER_ADDED) +#define ISNS_SCN_DD_MEMBER_REMOVED_MASK (1 << ISNS_SCN_DD_MEMBER_REMOVED) +#define ISNS_SCN_OBJECT_UPDATED_MASK (1 << ISNS_SCN_OBJECT_UPDATED) +#define ISNS_SCN_OBJECT_ADDED_MASK (1 << ISNS_SCN_OBJECT_ADDED) +#define ISNS_SCN_OBJECT_REMOVED_MASK (1 << ISNS_SCN_OBJECT_REMOVED) +#define ISNS_SCN_MANAGEMENT_REGISTRATION_MASK (1 << ISNS_SCN_MANAGEMENT_REGISTRATION) +#define ISNS_SCN_TARGET_AND_SELF_ONLY_MASK (1 << ISNS_SCN_TARGET_AND_SELF_ONLY) +#define ISNS_SCN_INITIATOR_AND_SELF_ONLY_MASK (1 << ISNS_SCN_INITIATOR_AND_SELF_ONLY) + +enum isns_dds_status_bits { + ISNS_DDS_ENABLED = 0, +}; +#define ISNS_DDS_ENABLED_MASK (1 << ISNS_DDS_ENABLED) + +enum isns_dd_feature_bits { + ISNS_DD_BOOT_LIST_ENABLED = 0, +}; +#define ISNS_DD_BOOT_LIST_ENABLED_MASK (1 << ISN_BOOT_LIST_DDS_ENABLED) + +#define ISNS_PAD(len) (((len) + 3) & ~3UL) + +/* + * iSNS auth block + */ +#define ISNS_AUTHBLK_SIZE 20 +struct isns_authblk { + uint32_t iab_bsd; /* 16bit in SLP */ + uint32_t iab_length; /* 16bit in SLP */ + uint64_t iab_timestamp; /* 32bit in SLP */ + uint32_t iab_spi_len; /* 16bit in SLP */ + + char * iab_spi; + void * iab_sig; + uint32_t iab_sig_len; +} __attribute__((packed)); + +#define ISNS_AUTH_TYPE_SHA1_DSA 0x0002 + +#endif /* ISNS_PROTO_H */ diff --git a/utils/open-isns/isns.h b/utils/open-isns/isns.h new file mode 100644 index 0000000..73d4a45 --- /dev/null +++ b/utils/open-isns/isns.h @@ -0,0 +1,670 @@ +/* + * iSNS implementation - library header file. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + * + * This file contains all declarations and definitions + * commonly required by users of libisns. + */ + +#ifndef ISNS_H +#define ISNS_H + +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> + +#include <isns-proto.h> +#include "types.h" + +#define ISNS_MAX_BUFFER 8192 +#define ISNS_MAX_MESSAGE 8192 + + +/* + * Client handle + */ +typedef struct isns_client isns_client_t; +struct isns_client { + isns_source_t * ic_source; + isns_socket_t * ic_socket; +}; + +/* + * Server operations + */ +typedef int isns_service_fn_t(isns_server_t *, isns_simple_t *, isns_simple_t **); +typedef void isns_scn_callback_fn_t(isns_db_t *, uint32_t scn_bits, + isns_object_template_t *node_type, + const char *node_name, + const char *recipient); +struct isns_service_ops { + isns_service_fn_t * process_registration; + isns_service_fn_t * process_query; + isns_service_fn_t * process_getnext; + isns_service_fn_t * process_deregistration; + isns_service_fn_t * process_scn_registration; + isns_service_fn_t * process_scn_deregistration; + isns_service_fn_t * process_scn_event; + isns_service_fn_t * process_scn; + isns_service_fn_t * process_dd_registration; + isns_service_fn_t * process_dd_deregistration; + isns_service_fn_t * process_esi; + isns_service_fn_t * process_heartbeat; +}; + +extern struct isns_service_ops isns_default_service_ops; +extern struct isns_service_ops isns_callback_service_ops; + +/* + * Output function + */ +void isns_print_stdout(const char *, ...); + +/* + * Database events + */ +struct isns_db_event { + isns_object_t * ie_recipient; /* Recipient node or NULL */ + isns_object_t * ie_object; /* Affected object */ + isns_object_t * ie_trigger; /* Triggering object */ + unsigned int ie_bits; /* SCN bitmask */ +}; +typedef void isns_db_callback_t(const isns_db_event_t *, + void *user_data); + +/* + * Handling of client objects + */ +extern isns_client_t * isns_create_default_client(isns_security_t *); +extern isns_client_t * isns_create_client(isns_security_t *, + const char *source_name); +extern isns_client_t * isns_create_local_client(isns_security_t *, + const char *source_name); +extern int isns_client_call(isns_client_t *, + isns_simple_t **inout); +extern void isns_client_destroy(isns_client_t *); +extern int isns_client_get_local_address(const isns_client_t *, + isns_portal_info_t *); + +/* + * Handling of server objects + */ +extern isns_server_t * isns_create_server(isns_source_t *, + isns_db_t *, + struct isns_service_ops *); +extern void isns_server_set_scn_callback(isns_server_t *, + isns_scn_callback_fn_t *); + + +/* + * Handling of source names + */ +extern int isns_init_names(void); +extern const char * isns_default_source_name(void); +extern isns_source_t * isns_source_create(isns_attr_t *); +extern isns_source_t * isns_source_create_iscsi(const char *name); +extern isns_source_t * isns_source_create_ifcp(const char *name); +extern uint32_t isns_source_type(const isns_source_t *); +extern const char * isns_source_name(const isns_source_t *); +extern isns_attr_t * isns_source_attr(const isns_source_t *); +extern isns_source_t * isns_source_get(isns_source_t *); +extern isns_source_t * isns_source_from_object(const isns_object_t *); +extern void isns_source_release(isns_source_t *); +extern int isns_source_match(const isns_source_t *, + const isns_source_t *); + +extern void isns_server_set_source(isns_source_t *); +extern isns_message_t * isns_process_message(isns_server_t *, isns_message_t *); + +extern void isns_simple_print(isns_simple_t *, + isns_print_fn_t *); +extern int isns_simple_call(isns_socket_t *, + isns_simple_t **); +extern int isns_simple_transmit(isns_socket_t *, + isns_simple_t *, + const isns_portal_info_t *, + unsigned int, + void (*callback)(uint32_t, isns_simple_t *)); +extern void isns_simple_free(isns_simple_t *); +extern const isns_attr_list_t *isns_simple_get_attrs(isns_simple_t *); + +extern isns_simple_t * isns_create_query(isns_client_t *clnt, + const isns_attr_list_t *query_key); +extern isns_simple_t * isns_create_query2(isns_client_t *clnt, + const isns_attr_list_t *query_key, + isns_source_t *source); +extern int isns_query_request_attr_tag(isns_simple_t *, + uint32_t); +extern int isns_query_request_attr(isns_simple_t *, + isns_attr_t *); +extern int isns_query_response_get_objects(isns_simple_t *qry, + isns_object_list_t *result); + +extern isns_simple_t * isns_create_registration(isns_client_t *clnt, + isns_object_t *key_object); +extern isns_simple_t * isns_create_registration2(isns_client_t *clnt, + isns_object_t *key_object, + isns_source_t *source); +extern void isns_registration_set_replace(isns_simple_t *, int); +extern void isns_registration_add_object(isns_simple_t *, + isns_object_t *object); +extern void isns_registration_add_object_list(isns_simple_t *, + isns_object_list_t *); +extern int isns_registration_response_get_objects(isns_simple_t *, + isns_object_list_t *); + +extern isns_simple_t * isns_create_getnext(isns_client_t *, + isns_object_template_t *, + const isns_attr_list_t *); +extern int isns_getnext_response_get_object(isns_simple_t *, + isns_object_t **); +extern isns_simple_t * isns_create_getnext_followup(isns_client_t *, + const isns_simple_t *, + const isns_attr_list_t *); + +extern isns_simple_t * isns_create_deregistration(isns_client_t *clnt, + const isns_attr_list_t *); + +extern isns_simple_t * isns_create_scn_registration(isns_client_t *clnt, + unsigned int); +extern isns_simple_t * isns_create_scn_registration2(isns_client_t *clnt, + unsigned int, + isns_source_t *); + +extern int isns_dd_load_all(isns_db_t *); +extern void isns_dd_get_members(uint32_t, isns_object_list_t *, int); +extern isns_simple_t * isns_create_dd_registration(isns_client_t *, + const isns_attr_list_t *); +extern isns_simple_t * isns_create_dd_deregistration(isns_client_t *, + uint32_t, const isns_attr_list_t *); + +extern isns_object_t * isns_create_object(isns_object_template_t *, + const isns_attr_list_t *, + isns_object_t *); +extern isns_object_t * isns_create_entity(int, const char *); +extern isns_object_t * isns_create_entity_for_source(const isns_source_t *, + const char *); +extern const char * isns_entity_name(const isns_object_t *); +extern isns_object_t * isns_create_portal(const isns_portal_info_t *, + isns_object_t *parent); +extern isns_object_t * isns_create_storage_node(const char *name, + uint32_t type_mask, + isns_object_t *parent); +extern isns_object_t * isns_create_storage_node2(const isns_source_t *, + uint32_t type_mask, + isns_object_t *parent); +extern isns_object_t * isns_create_iscsi_initiator(const char *name, + isns_object_t *parent); +extern isns_object_t * isns_create_iscsi_target(const char *name, + isns_object_t *parent); +extern const char * isns_storage_node_name(const isns_object_t *); +extern isns_attr_t * isns_storage_node_key_attr(const isns_object_t *); +extern isns_object_t * isns_create_portal_group(isns_object_t *portal, + isns_object_t *iscsi_node, uint32_t pg_tag); +extern isns_object_t * isns_create_default_portal_group(isns_db_t *, + isns_object_t *portal, + isns_object_t *node); +extern void isns_get_portal_groups(isns_object_t *portal, + isns_object_t *node, + isns_object_list_t *result); + +extern const char * isns_object_template_name(isns_object_template_t *); +extern int isns_object_set_attr(isns_object_t *, isns_attr_t *); +extern int isns_object_set_attrlist(isns_object_t *, const isns_attr_list_t *); +extern isns_object_t * isns_object_get(isns_object_t *); +extern int isns_object_get_attrlist(isns_object_t *obj, + isns_attr_list_t *result, + const isns_attr_list_t *requested_attrs); +extern int isns_object_get_key_attrs(isns_object_t *, + isns_attr_list_t *); +extern int isns_object_get_attr(const isns_object_t *, uint32_t, + isns_attr_t **); +extern void isns_object_get_related(isns_db_t *, + isns_object_t *, isns_object_list_t *); +extern void isns_object_get_descendants(const isns_object_t *, + isns_object_template_t *, + isns_object_list_t *); +extern void isns_object_release(isns_object_t *); +extern int isns_object_match(const isns_object_t *, + const isns_attr_list_t *); +extern isns_object_t * isns_object_get_entity(isns_object_t *); +extern int isns_object_attr_valid(isns_object_template_t *, uint32_t); +extern int isns_object_contains(const isns_object_t *, const isns_object_t *); +extern int isns_object_delete_attr(isns_object_t *, uint32_t); +extern int isns_object_is(const isns_object_t *, + isns_object_template_t *); +extern int isns_object_is_entity(const isns_object_t *); +extern int isns_object_is_iscsi_node(const isns_object_t *); +extern int isns_object_is_fc_port(const isns_object_t *); +extern int isns_object_is_fc_node(const isns_object_t *); +extern int isns_object_is_portal(const isns_object_t *); +extern int isns_object_is_pg(const isns_object_t *); +extern int isns_object_is_policy(const isns_object_t *); +extern int isns_object_is_dd(const isns_object_t *); +extern int isns_object_is_ddset(const isns_object_t *); +extern void isns_object_print(isns_object_t *, + isns_print_fn_t *); +extern time_t isns_object_last_modified(const isns_object_t *); +extern int isns_object_mark_membership(isns_object_t *, uint32_t); +extern int isns_object_clear_membership(isns_object_t *, uint32_t); +extern int isns_object_test_membership(const isns_object_t *, uint32_t); +extern int isns_object_test_visibility(const isns_object_t *, + const isns_object_t *); +extern void isns_object_get_visible(const isns_object_t *, + isns_db_t *, isns_object_list_t *); +extern void isns_entity_touch(isns_object_t *); +extern int isns_object_extract_keys(const isns_object_t *, + isns_attr_list_t *); +extern int isns_object_extract_all(const isns_object_t *, + isns_attr_list_t *); +extern int isns_object_extract_writable(const isns_object_t *, + isns_attr_list_t *); + + +extern int isns_object_set_nil(isns_object_t *obj, + uint32_t tag); +extern int isns_object_set_string(isns_object_t *obj, + uint32_t tag, + const char *value); +extern int isns_object_set_uint32(isns_object_t *obj, + uint32_t tag, + uint32_t value); +extern int isns_object_set_uint64(isns_object_t *obj, + uint32_t tag, + uint64_t value); +extern int isns_object_set_ipaddr(isns_object_t *obj, + uint32_t tag, + const struct in6_addr *value); + +extern int isns_object_get_string(const isns_object_t *, + uint32_t, + const char **); +extern int isns_object_get_ipaddr(const isns_object_t *, + uint32_t, + struct in6_addr *); +extern int isns_object_get_uint32(const isns_object_t *, + uint32_t, + uint32_t *); +extern int isns_object_get_uint64(const isns_object_t *, + uint32_t, + uint64_t *); +extern int isns_object_get_opaque(const isns_object_t *, + uint32_t, + const void **, size_t *); + + +extern int isns_object_find_descendants(isns_object_t *obj, + isns_object_template_t *, + const isns_attr_list_t *keys, + isns_object_list_t *result); +extern isns_object_t * isns_object_find_descendant(isns_object_t *obj, + const isns_attr_list_t *keys); +extern int isns_object_detach(isns_object_t *); +extern int isns_object_attach(isns_object_t *, isns_object_t *); +extern void isns_object_prune_attrs(isns_object_t *); +extern void isns_mark_object(isns_object_t *, unsigned int); + +extern int isns_get_entity_identifier(isns_object_t *, const char **); +extern int isns_get_entity_protocol(isns_object_t *, isns_entity_protocol_t *); +extern int isns_get_entity_index(isns_object_t *, uint32_t *); + +extern int isns_get_portal_ipaddr(isns_object_t *, struct in6_addr *); +extern int isns_get_portal_tcpudp_port(isns_object_t *, + int *ipprotocol, uint16_t *port); +extern int isns_get_portal_index(isns_object_t *, uint32_t *); + +extern int isns_get_address(struct sockaddr_storage *, + const char *, const char *, int, int, int); +extern char * isns_get_canon_name(const char *); + +extern isns_db_t * isns_db_open(const char *location); +extern isns_db_t * isns_db_open_shadow(isns_object_list_t *); +extern isns_object_t * isns_db_lookup(isns_db_t *, + isns_object_template_t *, + const isns_attr_list_t *); +extern isns_object_t * isns_db_vlookup(isns_db_t *, + isns_object_template_t *, + ...); +extern int isns_db_gang_lookup(isns_db_t *, + isns_object_template_t *, + const isns_attr_list_t *, + isns_object_list_t *); +extern isns_object_t * isns_db_get_next(isns_db_t *, + isns_object_template_t *, + const isns_attr_list_t *current, + const isns_attr_list_t *scope, + const isns_source_t *source); +extern isns_object_t * isns_db_lookup_source_node(isns_db_t *, + const isns_source_t *); +extern void isns_db_get_domainless(isns_db_t *, + isns_object_template_t *, + isns_object_list_t *); +extern uint32_t isns_db_allocate_index(isns_db_t *); +extern void isns_db_insert(isns_db_t *, isns_object_t *); +extern void isns_db_insert_limbo(isns_db_t *, isns_object_t *); +extern int isns_db_remove(isns_db_t *, isns_object_t *); +extern time_t isns_db_expire(isns_db_t *); +extern void isns_db_purge(isns_db_t *); +extern void isns_db_sync(isns_db_t *); +extern const char * isns_db_generate_eid(isns_db_t *, char *, size_t); +extern isns_object_t * isns_db_get_control(isns_db_t *); +extern void isns_db_print(isns_db_t *, + isns_print_fn_t *); + +extern void isns_db_begin_transaction(isns_db_t *); +extern void isns_db_commit(isns_db_t *); +extern void isns_db_rollback(isns_db_t *); + +extern void isns_object_event(isns_object_t *obj, + unsigned int bits, + isns_object_t *trigger); +extern void isns_unicast_event(isns_object_t *dst, + isns_object_t *obj, + unsigned int bits, + isns_object_t *trigger); +extern void isns_register_callback(isns_db_callback_t *, + void *); +extern void isns_flush_events(void); +extern const char * isns_event_string(unsigned int); + +extern void isns_add_timer(unsigned int, + isns_timer_callback_t *, void *); +extern void isns_add_oneshot_timer(unsigned int, + isns_timer_callback_t *, void *); +extern void isns_cancel_timer(isns_timer_callback_t *, void *); +extern time_t isns_run_timers(void); + +extern void isns_object_list_init(isns_object_list_t *); +extern void isns_object_list_destroy(isns_object_list_t *); +extern int isns_object_list_contains(const isns_object_list_t *, + isns_object_t *); +extern void isns_object_list_append(isns_object_list_t *, + isns_object_t *); +extern void isns_object_list_append_list(isns_object_list_t *, + const isns_object_list_t *); +extern isns_object_t * isns_object_list_lookup(const isns_object_list_t *, + isns_object_template_t *, + const isns_attr_list_t *); +extern int isns_object_list_gang_lookup(const isns_object_list_t *, + isns_object_template_t *, + const isns_attr_list_t *, + isns_object_list_t *); +extern int isns_object_list_remove(isns_object_list_t *, + isns_object_t *); +extern void isns_object_list_uniq(isns_object_list_t *); +extern void isns_object_list_print(const isns_object_list_t *, + isns_print_fn_t *); + +isns_object_template_t *isns_object_template_for_key_attrs(const isns_attr_list_t *); +isns_object_template_t *isns_object_template_for_tag(uint32_t); +isns_object_template_t *isns_object_template_for_index_tag(uint32_t); +isns_object_template_t *isns_object_template_find(uint32_t); + +extern int isns_attr_set(isns_attr_t *, const void *); +extern isns_attr_t * isns_attr_get(isns_attr_t *); +extern void isns_attr_release(isns_attr_t *); +extern void isns_attr_print(const isns_attr_t *, + isns_print_fn_t *); +extern char * isns_attr_print_value(const isns_attr_t *, + char *, size_t); +extern int isns_attr_match(const isns_attr_t *, + const isns_attr_t *); +extern int isns_attr_compare(const isns_attr_t *, + const isns_attr_t *); +extern isns_attr_t * isns_attr_from_string(uint32_t, const char *); + +extern void isns_attr_list_print(const isns_attr_list_t *, + isns_print_fn_t *); + +extern void isns_attr_list_init(isns_attr_list_t *); +extern void isns_attr_list_copy(isns_attr_list_t *, + const isns_attr_list_t *); +extern void isns_attr_list_destroy(isns_attr_list_t *); +extern int isns_attr_list_remove_tag(isns_attr_list_t *, + uint32_t); + +extern void isns_attr_list_append_attr(isns_attr_list_t *, + isns_attr_t *); +extern void isns_attr_list_append_list(isns_attr_list_t *, + const isns_attr_list_t *); +extern int isns_attr_list_replace_attr(isns_attr_list_t *, + isns_attr_t *); +/* Warning: this does *NOT* return a reference to the attribute */ +extern int isns_attr_list_get_attr(const isns_attr_list_t *, + uint32_t tag, + isns_attr_t **); + +extern void isns_attr_list_append_nil(isns_attr_list_t *, + uint32_t tag); +extern void isns_attr_list_append_string(isns_attr_list_t *, + uint32_t tag, const char *value); +extern void isns_attr_list_append_uint32(isns_attr_list_t *, + uint32_t tag, uint32_t value); +extern void isns_attr_list_append_uint64(isns_attr_list_t *, + uint32_t, int64_t); +extern void isns_attr_list_append_int32(isns_attr_list_t *, + uint32_t tag, int32_t value); +extern void isns_attr_list_append_opaque(isns_attr_list_t *, + uint32_t tag, const void *ptr, size_t len); +extern void isns_attr_list_append_ipaddr(isns_attr_list_t *, + uint32_t tag, const struct in6_addr *); + +extern int isns_attr_list_append(isns_attr_list_t *, + uint32_t tag, const void *); +extern int isns_attr_list_update(isns_attr_list_t *, + uint32_t tag, const void *); + +extern int isns_attr_list_contains(const isns_attr_list_t *, + uint32_t tag); +extern int isns_attr_list_compare(const isns_attr_list_t *, + const isns_attr_list_t *); + +/* + * Helper macros + */ +#define ISNS_ATTR_TYPE_CHECK(attr, type) \ + ((attr)->ia_value.iv_type == &isns_attr_type_##type) +#define ISNS_ATTR_IS_NIL(attr) \ + ISNS_ATTR_TYPE_CHECK(attr, nil) +#define ISNS_ATTR_IS_STRING(attr) \ + ISNS_ATTR_TYPE_CHECK(attr, string) +#define ISNS_ATTR_IS_IPADDR(attr) \ + ISNS_ATTR_TYPE_CHECK(attr, ipaddr) +#define ISNS_ATTR_IS_UINT32(attr) \ + ISNS_ATTR_TYPE_CHECK(attr, uint32) +#define ISNS_ATTR_IS_UINT64(attr) \ + ISNS_ATTR_TYPE_CHECK(attr, uint64) +#define ISNS_ATTR_IS_OPAQUE(attr) \ + ISNS_ATTR_TYPE_CHECK(attr, opaque) + + + +extern isns_socket_t * isns_create_server_socket(const char *hostname, const char *portname, + int af_hint, int sock_type); +extern isns_socket_t * isns_create_client_socket(const char *hostname, const char *portname, + int af_hint, int sock_type); +extern isns_socket_t * isns_create_bound_client_socket(const char *myaddr, + const char *hostname, const char *portname, + int af_hint, int sock_type); +extern isns_socket_t * isns_connect_to_portal(const isns_portal_info_t *); +extern void isns_socket_set_disconnect_fatal(isns_socket_t *); +extern int isns_socket_get_local_addr(const isns_socket_t *, + struct sockaddr_storage *); +extern int isns_socket_get_portal_info(const isns_socket_t *, + isns_portal_info_t *); +extern void isns_socket_set_security_ctx(isns_socket_t *, + isns_security_t *); +extern isns_message_t * isns_recv_message(struct timeval *timeout); +extern isns_message_t * isns_socket_call(isns_socket_t *, isns_message_t *, long); +extern int isns_socket_send(isns_socket_t *, isns_message_t *); +extern void isns_socket_free(isns_socket_t *); +extern int isns_addr_get_port(const struct sockaddr *); +extern void isns_addr_set_port(struct sockaddr *, unsigned int); +extern isns_socket_t * isns_socket_find_server(const isns_portal_info_t *); + +extern isns_message_t * isns_create_message(uint16_t function, uint16_t flags); +extern isns_message_t * isns_create_reply(const isns_message_t *); +extern int isns_message_init(isns_message_t *, + uint16_t, uint16_t, size_t); +extern int isns_message_status(isns_message_t *); +extern void isns_message_release(isns_message_t *); +extern unsigned int isns_message_function(const isns_message_t *); +extern isns_socket_t * isns_message_socket(const isns_message_t *); +extern void isns_message_set_error(isns_message_t *, uint32_t); + +extern const char * isns_strerror(enum isns_status); +extern const char * isns_function_name(unsigned int); + +/* + * Security related functions + */ +extern int isns_security_init(void); +extern isns_principal_t *isns_security_load_privkey(isns_security_t *, + const char *filename); +extern isns_principal_t *isns_security_load_pubkey(isns_security_t *, + const char *filename); +extern isns_security_t *isns_default_security_context(int server_only); +extern isns_security_t *isns_control_security_context(int server_only); +extern isns_security_t *isns_create_dsa_context(void); +extern void isns_security_set_identity(isns_security_t *, isns_principal_t *); +extern void isns_principal_free(isns_principal_t *); +extern void isns_add_principal(isns_security_t *, isns_principal_t *); +extern isns_keystore_t *isns_create_keystore(const char *); +extern void isns_security_set_keystore(isns_security_t *, + isns_keystore_t *); +extern void isns_principal_set_name(isns_principal_t *, const char *); +extern const char * isns_principal_name(const isns_principal_t *); + +extern isns_object_template_t isns_entity_template; +extern isns_object_template_t isns_portal_template; +extern isns_object_template_t isns_iscsi_node_template; +extern isns_object_template_t isns_fc_port_template; +extern isns_object_template_t isns_fc_node_template; +extern isns_object_template_t isns_iscsi_pg_template; +extern isns_object_template_t isns_dd_template; +extern isns_object_template_t isns_ddset_template; + +/* + * Config file parser + */ +struct isns_config { + char * ic_host_name; + char * ic_auth_name; + char * ic_source_name; + char * ic_source_suffix; + char * ic_entity_name; + + char * ic_server_name; + char * ic_bind_address; + char * ic_database; + char * ic_auth_key_file; + char * ic_server_key_file; + char * ic_client_keystore; + char * ic_control_socket; + char * ic_pidfile; + char * ic_local_registry_file; + int ic_security; + int ic_slp_register; + + char * ic_control_name; + char * ic_control_key_file; + + unsigned int ic_registration_period; + unsigned int ic_scn_timeout; + unsigned int ic_scn_retries; + char * ic_scn_callout; + + unsigned int ic_esi_max_interval; + unsigned int ic_esi_min_interval; + unsigned int ic_esi_retries; + + unsigned int ic_use_default_domain; + + struct { + unsigned int policy; + unsigned int replay_window; + unsigned int timestamp_jitter; + int allow_unknown_peers; + } ic_auth; + struct { + unsigned int max_sockets; + unsigned int connect_timeout; + unsigned int reconnect_timeout; + unsigned int call_timeout; + unsigned int udp_retrans_timeout; + unsigned int tcp_retrans_timeout; + unsigned int idle_timeout; + } ic_network; + struct { + char * param_file; + unsigned int key_bits; + } ic_dsa; + +}; + +extern struct isns_config isns_config; +extern int isns_read_config(const char *); +extern int isns_config_set(const char *, char *); + +/* + * Reserved entity name for Policy information + */ +#define ISNS_ENTITY_CONTROL "CONTROL" + + +/* + * Helpers to deal with portal information + */ +struct isns_portal_info { + struct sockaddr_in6 addr; + int proto; +}; + +extern void isns_portal_init(isns_portal_info_t *, + const struct sockaddr *, int); +extern int isns_portal_parse(isns_portal_info_t *portal, + const char *addr_spec, + const char *default_port); +extern int isns_portal_from_attr_list(isns_portal_info_t *, + uint32_t addr_tag, uint32_t port_tag, + const isns_attr_list_t *); +extern int isns_portal_from_attr_pair(isns_portal_info_t *, + const isns_attr_t *, + const isns_attr_t *); +extern int isns_portal_from_object(isns_portal_info_t *, + uint32_t addr_tag, uint32_t port_tag, + const isns_object_t *); +extern int isns_portal_from_sockaddr(isns_portal_info_t *, + const struct sockaddr_storage *); +extern int isns_portal_to_sockaddr(const isns_portal_info_t *, + struct sockaddr_storage *); +extern int isns_portal_to_attr_list(const isns_portal_info_t *, + uint32_t addr_tag, uint32_t port_tag, + isns_attr_list_t *); +extern int isns_portal_to_object(const isns_portal_info_t *, + uint32_t addr_tag, uint32_t port_tag, + isns_object_t *); +extern int isns_portal_is_wildcard(const isns_portal_info_t *); +extern uint32_t isns_portal_tcpudp_port(const isns_portal_info_t *); +extern const char * isns_portal_string(const isns_portal_info_t *); +extern int isns_portal_equal(const isns_portal_info_t *, + const isns_portal_info_t *); +extern int isns_enumerate_portals(isns_portal_info_t *, + unsigned int); + +/* Local registry stuff */ +extern int isns_local_registry_load(const char *, pid_t, isns_object_list_t *); +extern int isns_local_registry_store(const char *, pid_t, const isns_object_list_t *); +extern int isns_local_registry_purge(const char *, pid_t); + +/* Should go somwhere else .*/ +extern int isns_esi_enabled; + +extern void isns_esi_init(isns_server_t *); +extern void isns_esi_register(isns_object_t *); + +extern void isns_scn_init(isns_server_t *); +extern time_t isns_scn_transmit_all(void); + +#endif /* ISNS_H */ diff --git a/utils/open-isns/isnsadm.c b/utils/open-isns/isnsadm.c new file mode 100644 index 0000000..fadd87d --- /dev/null +++ b/utils/open-isns/isnsadm.c @@ -0,0 +1,1149 @@ +/* + * isnsadm - helper utility + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +#include "isns.h" +#include "util.h" +#include "vendor.h" +#include "attrs.h" +#include "security.h" +#include "objects.h" +#include "paths.h" +#include "config.h" + +#define ISNS_DEFAULT_PORT_INITIATOR 860 +#define ISNS_DEFAULT_PORT_TARGET 3260 + + +enum { + DO_REGISTER = 1024, + DO_QUERY, + DO_QUERY_EID, + DO_LIST, + DO_DEREGISTER, + DO_DD_REGISTER, + DO_DD_DEREGISTER, + DO_ENROLL, + DO_EDIT_POLICY, + DO_DELETE_POLICY, +}; + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "config", required_argument, NULL, 'c' }, + { "debug", required_argument, NULL, 'd' }, + { "keyfile", required_argument, NULL, 'K', }, + { "key", required_argument, NULL, 'k', }, + { "local", no_argument, NULL, 'l' }, + { "control", no_argument, NULL, 'C' }, + { "replace", no_argument, NULL, 'r' }, + { "query", no_argument, NULL, DO_QUERY }, + { "query-eid", no_argument, NULL, DO_QUERY_EID }, + { "list", no_argument, NULL, DO_LIST }, + { "register", no_argument, NULL, DO_REGISTER }, + { "deregister", no_argument, NULL, DO_DEREGISTER }, + { "dd-register", no_argument, NULL, DO_DD_REGISTER }, + { "dd-deregister", no_argument, NULL, DO_DD_DEREGISTER}, + + { "enroll", no_argument, NULL, DO_ENROLL }, + { "edit-policy", no_argument, NULL, DO_EDIT_POLICY }, + { "delete-policy", no_argument, NULL, DO_DELETE_POLICY }, + + { "version", no_argument, NULL, 'V' }, + { NULL } +}; + + +static const char * opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG; +static int opt_af = AF_UNSPEC; +static int opt_action = 0; +static int opt_local = 0; +static int opt_control = 0; +static int opt_replace = 0; +static const char * opt_keyfile = NULL; +static char * opt_key = NULL; +static struct sockaddr_storage opt_myaddr; + +static void usage(int, const char *); + +static int register_objects(isns_client_t *, int, char **); +static int query_objects(isns_client_t *, int, char **); +static int query_entity_id(isns_client_t *, int, char **); +static int list_objects(isns_client_t *, int, char **); +static int deregister_objects(isns_client_t *, int, char **); +static int register_domain(isns_client_t *, int, char **); +static int deregister_domain(isns_client_t *, int, char **); +static int enroll_client(isns_client_t *, int, char **); +static int edit_policy(isns_client_t *, int, char **); + +static isns_attr_t * load_key_callback(const char *); +static isns_attr_t * generate_key_callback(void); + +int +main(int argc, char **argv) +{ + isns_client_t *clnt; + isns_security_t *security = NULL; + int c, status; + + while ((c = getopt_long(argc, argv, "46Cc:d:hK:k:l", options, NULL)) != -1) { + switch (c) { + case '4': + opt_af = AF_INET; + break; + + case '6': + opt_af = AF_INET6; + break; + + case 'C': + opt_control = 1; + break; + + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + case 'h': + usage(0, NULL); + break; + + case 'K': + opt_keyfile = optarg; + break; + + case 'k': + opt_key = optarg; + break; + + case 'l': + opt_local = 1; + break; + + case 'r': + opt_replace = 1; + break; + + case 'V': + printf("Open-iSNS version %s\n" + "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n", + OPENISNS_VERSION_STRING); + return 0; + + case DO_REGISTER: + case DO_QUERY: + case DO_QUERY_EID: + case DO_LIST: + case DO_DEREGISTER: + case DO_DD_REGISTER: + case DO_DD_DEREGISTER: + case DO_ENROLL: + case DO_EDIT_POLICY: + case DO_DELETE_POLICY: + if (opt_action) + usage(1, "You cannot specify more than one mode\n"); + opt_action = c; + break; + + default: + usage(1, "Unknown option"); + } + } + + isns_read_config(opt_configfile); + + if (!isns_config.ic_source_name) + usage(1, "Please specify an iSNS source name"); + if (!isns_config.ic_server_name) + usage(1, "Please specify an iSNS server name"); + if (!opt_action) + usage(1, "Please specify an operating mode"); + + if (opt_control) { + if (!isns_config.ic_security) + isns_fatal("Cannot use control mode, security disabled\n"); + security = isns_control_security_context(0); + if (!security) + isns_fatal("Unable to create control security context\n"); + + /* Create a networked client, using isns.control as + * the source name */ + clnt = isns_create_client(security, isns_config.ic_control_name); + } else if (opt_local) { + /* Create a local client, using isns.control as + * the source name */ + clnt = isns_create_local_client(security, + isns_config.ic_control_name); + } else { + /* Create a networked client, using the configured + * source name */ + clnt = isns_create_default_client(security); + } + + if (clnt == NULL) + return 1; + + /* We're an interactive app, and don't want to retry + * forever if the server refuses us. */ + isns_socket_set_disconnect_fatal(clnt->ic_socket); + + /* Get the IP address we use to talk to the iSNS server */ + if (opt_myaddr.ss_family == AF_UNSPEC && !opt_local) { + if (!isns_socket_get_local_addr(clnt->ic_socket, &opt_myaddr)) + isns_fatal("Unable to obtain my IP address\n"); + isns_addr_set_port((struct sockaddr *) &opt_myaddr, 860); + } + + argv += optind; argc -= optind; + switch (opt_action) { + case DO_REGISTER: + status = register_objects(clnt, argc, argv); + break; + + case DO_QUERY: + status = query_objects(clnt, argc, argv); + break; + + case DO_QUERY_EID: + status = query_entity_id(clnt, argc, argv); + break; + + case DO_LIST: + status = list_objects(clnt, argc, argv); + break; + + case DO_DEREGISTER: + status = deregister_objects(clnt, argc, argv); + break; + + case DO_DD_REGISTER: + status = register_domain(clnt, argc, argv); + break; + + case DO_DD_DEREGISTER: + status = deregister_domain(clnt, argc, argv); + break; + + + case DO_ENROLL: + status = enroll_client(clnt, argc, argv); + break; + + case DO_EDIT_POLICY: + status = edit_policy(clnt, argc, argv); + break; + + // case DO_DELETE_POLICY: + + default: + isns_fatal("Not yet implemented\n"); + status = 1; /* compiler food */ + } + + return status != ISNS_SUCCESS; +} + +void +usage(int exval, const char *msg) +{ + if (msg) + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, + "Usage: isnsadm [options] --action ...\n" + " --config Specify alternative config fille\n" + " --debug Enable debugging (list of debug flags)\n" + " --keyfile Where to store newly generated private key\n" + " --local Use local Unix socket to talk to isnsd\n" + " --control Assume control node identity for authentication\n" + " --replace Use replace mode (--register only)\n" + "\nThe following actions are supported:\n" + " --register Register one or more objects\n" + " --deregister Deregister an object (and children)\n" + " --query Query iSNS server for objects\n" + " --list List all objects of a given type\n" + " --enroll Create a new policy object for a client\n" + " --edit-policy Edit a policy object\n" + " --delete-policy Edit a policy object\n" + " --help Display this message\n" + "\nUse \"--query help\" to get help on e.g. the query action\n" + ); + exit(exval); +} + +int +parse_registration(char **argv, int argc, isns_object_list_t *objs, isns_object_t *key_obj) +{ + struct sockaddr_storage def_addr; + isns_object_t *entity = NULL, *last_portal = NULL, *last_node = NULL; + const char *def_port = NULL; + int i; + + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("Object registration:\n" + " isnsadm [-key attr=value] --register type,attr=value,... type,attr=value,...\n" + "Where type can be one of:\n" + " entity create/update network entity\n" + " initiator create iSCSI initiator storage node\n" + " target create iSCSI target storage node\n" + " control create control node\n" + " portal create portal\n" + " pg create portal group\n" + "\nThe following attributes are recognized:\n"); + + isns_attr_list_parser_help(NULL); + exit(0); + } + + if (argc == 0) + usage(1, "Missing object list\n"); + + if (key_obj) { + //isns_object_list_append(objs, key_obj); + if (isns_object_is_entity(key_obj)) + entity = key_obj; + } + + def_addr = opt_myaddr; + + for (i = 0; i < argc; ++i) { + isns_attr_list_t attrlist = ISNS_ATTR_LIST_INIT; + struct isns_attr_list_parser state; + isns_object_t *obj; + char *type, *name, *value, *next_attr; + char *attrs[128]; + unsigned int nattrs = 0; + + name = argv[i]; + + if ((next_attr = strchr(name, ',')) != NULL) + *next_attr++ = '\0'; + + while (next_attr && *next_attr) { + if (nattrs > 128) + isns_fatal("Too many attributes\n"); + + /* Show mercy with fat fingered + * people,,,,who,cannot,,,type,properly */ + if (next_attr[0] != ',') + attrs[nattrs++] = next_attr; + if ((next_attr = strchr(next_attr, ',')) != NULL) + *next_attr++ = '\0'; + } + + if ((value = strchr(name, '=')) != NULL) + *value++ = '\0'; + + type = name; + if (!strcmp(name, "entity")) { + if (entity == NULL) { + isns_error("Cannot create entity object " + "within this key object\n"); + return 0; + } + + if (value != NULL) + isns_object_set_string(entity, + ISNS_TAG_ENTITY_IDENTIFIER, + value); + obj = isns_object_get(entity); + goto handle_attributes; + } else + if (!strcmp(name, "node") + || !strcmp(name, "initiator")) { + const char *node_name; + + node_name = isns_config.ic_source_name; + if (value) + node_name = value; + + obj = isns_create_storage_node(node_name, + ISNS_ISCSI_INITIATOR_MASK, + entity); + last_node = obj; + + isns_addr_set_port((struct sockaddr *) &def_addr, + ISNS_DEFAULT_PORT_INITIATOR); + def_port = "iscsi"; + } else + if (!strcmp(name, "target")) { + const char *node_name; + + node_name = isns_config.ic_source_name; + if (value) + node_name = value; + obj = isns_create_storage_node(node_name, + ISNS_ISCSI_TARGET_MASK, + entity); + last_node = obj; + + isns_addr_set_port((struct sockaddr *) &def_addr, + ISNS_DEFAULT_PORT_TARGET); + def_port = "iscsi-target"; + } else + if (!strcmp(name, "control")) { + const char *node_name; + + node_name = isns_config.ic_control_name; + if (value) + node_name = value; + obj = isns_create_storage_node(node_name, + ISNS_ISCSI_CONTROL_MASK, + entity); + last_node = obj; + + def_port = NULL; + } else + if (!strcmp(name, "portal")) { + isns_portal_info_t portal_info; + + if (value == NULL) { + if (def_port == NULL) + isns_fatal("portal must follow initiator or target\n"); + isns_portal_init(&portal_info, + (struct sockaddr *) &def_addr, + IPPROTO_TCP); + } else + if (!isns_portal_parse(&portal_info, value, def_port)) + isns_fatal("Unable to parse portal=%s\n", value); + obj = isns_create_portal(&portal_info, entity); + last_portal = obj; + } else + if (!strcmp(name, "pg")) { + if (value) + isns_fatal("Unexpected value for portal group\n"); + if (!last_portal || !last_node) + isns_fatal("Portal group registration must follow portal and node\n"); + obj = isns_create_portal_group(last_portal, last_node, 10); + } else { + isns_error("Unknown object type \"%s\"\n", name); + return 0; + } + + if (obj == NULL) { + isns_error("Failure to create %s object\n", name); + return 0; + } + isns_object_list_append(objs, obj); + +handle_attributes: + isns_attr_list_parser_init(&state, obj->ie_template); + state.default_port = def_port; + + if (!isns_parse_attrs(nattrs, attrs, &attrlist, &state) + || !isns_object_set_attrlist(obj, &attrlist)) { + isns_error("Failure to set all %s attributes\n", name); + isns_attr_list_destroy(&attrlist); + return 0; + } + + isns_attr_list_destroy(&attrlist); + isns_object_release(obj); + } + + return 1; +} + +static int +__register_objects(isns_client_t *clnt, + isns_object_t *key_obj, + const isns_object_list_t *objects) +{ + isns_source_t *source = NULL; + isns_simple_t *reg; + uint32_t status; + unsigned int i; + + for (i = 0; i < objects->iol_count && !source; ++i) { + isns_object_t *obj = objects->iol_data[i]; + + if (!isns_object_is_iscsi_node(obj)) + continue; + source = isns_source_from_object(obj); + } + + reg = isns_create_registration2(clnt, key_obj, source); + isns_registration_set_replace(reg, opt_replace); + + /* Add all objects to be registered */ + for (i = 0; i < objects->iol_count; ++i) + isns_registration_add_object(reg, objects->iol_data[i]); + + status = isns_client_call(clnt, ®); + isns_simple_free(reg); + + if (status == ISNS_SUCCESS) + printf("Successfully registered object(s)\n"); + else + isns_error("Failed to register object(s): %s\n", + isns_strerror(status)); + + if (source) + isns_source_release(source); + return status; +} + +int +register_objects(isns_client_t *clnt, + int argc, char **argv) +{ + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_object_t *key_obj = NULL; + uint32_t status; + + if (opt_key != NULL) { + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + struct isns_attr_list_parser state; + + isns_attr_list_parser_init(&state, NULL); + + if (!isns_parse_attrs(1, &opt_key, &key_attrs, &state)) { + isns_error("Cannot parse registration key \"%s\"\n", + opt_key); + return 0; + } + + key_obj = isns_create_object(isns_attr_list_parser_context(&state), + &key_attrs, NULL); + isns_attr_list_destroy(&key_attrs); + + if (!key_obj) { + isns_error("Cannot create registration key object\n"); + return 0; + } + } else { + /* If the user does not provide a key object, + * create/update an entity. + */ + key_obj = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, NULL); + } + + if (!parse_registration(argv, argc, &objects, key_obj)) + isns_fatal("Unable to parse registration\n"); + + status = __register_objects(clnt, key_obj, &objects); + isns_object_list_destroy(&objects); + + isns_object_release(key_obj); + return status; +} + +/* + * Parse the query string given by the user + * + * 5.6.5.2 + * The Message Key may contain key or non-key attributes or no + * attributes at all. If multiple attributes are used as the + * Message Key, then they MUST all be from the same object type + * (e.g., IP address and TCP/UDP Port are attributes of the + * Portal object type). + */ +int +parse_query(char **argv, int argc, isns_attr_list_t *keys, isns_attr_list_t *query) +{ + struct isns_attr_list_parser state; + + isns_attr_list_parser_init(&state, NULL); + state.nil_permitted = 1; + + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("Object query:\n" + " isnsadm --query attr=value attr=value ... ?query-attr ?query-attr ...\n" + "All key attributes must refer to a common object type.\n" + "Query attributes specify the attributes the server should return," + "and can refer to any object type.\n" + "The following attributes are recognized:\n"); + isns_attr_list_parser_help(&state); + exit(0); + } + + if (argc == 0) + isns_fatal("Missing query attributes\n"); + + return isns_parse_query_attrs(argc, argv, keys, query, &state); +} + +int +query_objects(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT; + isns_attr_list_t oper_attrs = ISNS_ATTR_LIST_INIT; + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + uint32_t status; + isns_simple_t *qry; + unsigned int i; + + if (!parse_query(argv, argc, &query_key, &oper_attrs)) + isns_fatal("Unable to parse query\n"); + + qry = isns_create_query(clnt, &query_key); + isns_attr_list_destroy(&query_key); + + /* Add the list of attributes we request */ + for (i = 0; i < oper_attrs.ial_count; ++i) + isns_query_request_attr(qry, oper_attrs.ial_data[i]); + isns_attr_list_destroy(&oper_attrs); + + status = isns_client_call(clnt, &qry); + if (status != ISNS_SUCCESS) { + isns_error("Query failed: %s\n", isns_strerror(status)); + return status; + } + + status = isns_query_response_get_objects(qry, &objects); + if (status) { + isns_error("Unable to extract object list from query response: %s\n", + isns_strerror(status), status); + return status; + } + + isns_object_list_print(&objects, isns_print_stdout); + isns_object_list_destroy(&objects); + isns_simple_free(qry); + + return status; +} + +int +query_entity_id(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT; + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + uint32_t status; + isns_simple_t *qry; + const char *eid; + + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("Query iSNS for own entity ID.\n" + "No arguments allowed\n"); + exit(0); + } + if (argc != 0) + isns_fatal("EID query - no arguments accepted\n"); + + isns_attr_list_append_string(&query_key, + ISNS_TAG_ISCSI_NAME, + isns_config.ic_source_name); + qry = isns_create_query(clnt, &query_key); + isns_attr_list_destroy(&query_key); + + isns_query_request_attr_tag(qry, ISNS_TAG_ENTITY_IDENTIFIER); + + status = isns_client_call(clnt, &qry); + if (status != ISNS_SUCCESS) { + isns_error("Query failed: %s\n", isns_strerror(status)); + return status; + } + + status = isns_query_response_get_objects(qry, &objects); + if (status) { + isns_error("Unable to extract object list from query response: %s\n", + isns_strerror(status), status); + return status; + } + + status = ISNS_NO_SUCH_ENTRY; + if (objects.iol_count == 0) { + isns_error("Node %s not registered with iSNS\n", + isns_config.ic_source_name); + } else + if (!isns_object_get_string(objects.iol_data[0], + ISNS_TAG_ENTITY_IDENTIFIER, &eid)) { + isns_error("Query for %s returned an object without EID\n", + isns_config.ic_source_name); + } else { + printf("%s\n", eid); + status = ISNS_SUCCESS; + } + + isns_object_list_destroy(&objects); + isns_simple_free(qry); + + return status; +} + +/* + * Parse the list query string given by the user + */ +int +parse_list(int argc, char **argv, isns_object_template_t **type_p, isns_attr_list_t *keys) +{ + struct isns_attr_list_parser state; + isns_object_template_t *query_type = NULL; + char *type_name; + + if (argc == 0) + usage(1, "Missing object type"); + + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("Object query:\n" + " isnsadm --list type attr=value attr=value ...\n" + "Possible value for <type>:\n" + " entities - list all network entites\n" + " nodes - list all storage nodes\n" + " portals - list all portals\n" + " portal-groups - list all portal groups\n" + " dds - list all discovery domains\n" + " ddsets - list all discovery domains sets\n" + " policies - list all policies (privileged)\n" + "Additional attributes can be specified to scope the\n" + "search. They must match the specified object type.\n" + "\nThe following attributes are recognized:\n"); + isns_attr_list_parser_help(NULL); + exit(0); + } + + type_name = *argv++; --argc; + if (!strcasecmp(type_name, "entities")) + query_type = &isns_entity_template; + else + if (!strcasecmp(type_name, "nodes")) + query_type = &isns_iscsi_node_template; + else + if (!strcasecmp(type_name, "portals")) + query_type = &isns_portal_template; + else + if (!strcasecmp(type_name, "portal-groups")) + query_type = &isns_iscsi_pg_template; + else + if (!strcasecmp(type_name, "dds")) + query_type = &isns_dd_template; + else + if (!strcasecmp(type_name, "ddsets")) + query_type = &isns_ddset_template; + else + if (!strcasecmp(type_name, "policies")) + query_type = &isns_policy_template; + else { + isns_error("Unknown object type \"%s\"\n", + type_name); + return 0; + } + + *type_p = query_type; + + isns_attr_list_parser_init(&state, query_type); + state.nil_permitted = 1; + + return isns_parse_attrs(argc, argv, keys, &state); +} + +int +list_objects(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t query_keys = ISNS_ATTR_LIST_INIT; + isns_object_template_t *query_type = NULL; + isns_simple_t *simp; + int status, count = 0; + + if (!parse_list(argc, argv, &query_type, &query_keys)) + isns_fatal("Unable to parse parameters\n"); + + simp = isns_create_getnext(clnt, query_type, &query_keys); + while (1) { + isns_object_t *obj = NULL; + isns_simple_t *followup; + + status = isns_client_call(clnt, &simp); + if (status) + break; + + status = isns_getnext_response_get_object(simp, &obj); + if (status) + break; + + printf("Object %u:\n", count++); + isns_object_print(obj, isns_print_stdout); + isns_object_release(obj); + + followup = isns_create_getnext_followup(clnt, + simp, &query_keys); + isns_simple_free(simp); + simp = followup; + } + + if (status == ISNS_SOURCE_UNAUTHORIZED + && query_type == &isns_policy_template + && !opt_local) + isns_warning("Please use --local trying to list policies\n"); + + if (status != ISNS_NO_SUCH_ENTRY) { + isns_error("GetNext call failed: %s\n", + isns_strerror(status)); + return status; + } + return ISNS_SUCCESS; +} + +/* + * Parse the deregistration string given by the user + * + * 5.6.5.2 + * The Message Key may contain key or non-key attributes or no + * attributes at all. If multiple attributes are used as the + * Message Key, then they MUST all be from the same object type + * (e.g., IP address and TCP/UDP Port are attributes of the + * Portal object type). + */ +int +parse_deregistration(char **argv, int argc, isns_attr_list_t *keys) +{ + struct isns_attr_list_parser state; + + isns_attr_list_parser_init(&state, NULL); + state.multi_type_permitted = 1; + state.nil_permitted = 1; + + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("Object deregistration:\n" + " isnsadm --deregister attr=value attr=value ...\n" + "All attributes must refer to a common object type.\n" + "\nThe following attributes are recognized:\n"); + isns_attr_list_parser_help(&state); + exit(0); + } + + return isns_parse_attrs(argc, argv, keys, &state); +} + +int +deregister_objects(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT; + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_simple_t *dereg; + uint32_t status; + + if (!parse_deregistration(argv, argc, &query_key)) + isns_fatal("Unable to parse unregistration\n"); + + dereg = isns_create_deregistration(clnt, &query_key); + isns_attr_list_destroy(&query_key); + + status = isns_client_call(clnt, &dereg); + if (status != ISNS_SUCCESS) { + isns_error("Deregistration failed: %s\n", + isns_strerror(status)); + return status; + } + +#if 0 + status = isns_dereg_msg_response_get_objects(dereg, &objects); + if (status) { + isns_error("Unable to extract object list from deregistration response: %s\n", + isns_strerror(status), status); + goto done; + } + isns_object_list_print(&objects, isns_print_stdout); +#endif + + isns_object_list_destroy(&objects); + isns_simple_free(dereg); + + return status; +} + +/* + * Handle discovery domain registration/deregistration + */ +int +parse_dd_registration(char **argv, int argc, isns_attr_list_t *keys) +{ + struct isns_attr_list_parser state; + + isns_attr_list_parser_init(&state, &isns_dd_template); + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("Object query:\n" + " isnsadm --dd-register attr=value attr=value ...\n" + "You cannot specify more than one domain.\n" + "If you want to modify an existing domain, you must specify its ID.\n" + "The following attributes are recognized:\n"); + isns_attr_list_parser_help(&state); + exit(0); + } + + return isns_parse_attrs(argc, argv, keys, &state); +} + +int +register_domain(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + isns_simple_t *msg; + uint32_t status; + + if (!parse_dd_registration(argv, argc, &attrs)) + isns_fatal("Unable to parse DD registration\n"); + + msg = isns_create_dd_registration(clnt, &attrs); + isns_attr_list_destroy(&attrs); + + if (msg == NULL) { + isns_error("Cannot create message\n"); + return ISNS_INTERNAL_ERROR; + } + + status = isns_client_call(clnt, &msg); + if (status != ISNS_SUCCESS) { + isns_error("Registration failed: %s\n", + isns_strerror(status)); + return status; + } + + if (status == ISNS_SUCCESS) { + printf("Registered DD:\n"); + isns_attr_list_print( + isns_simple_get_attrs(msg), + isns_print_stdout); + } + isns_simple_free(msg); + + return status; +} + +int +parse_dd_deregistration(char **argv, int argc, + uint32_t *dd_id, isns_attr_list_t *keys) +{ + struct isns_attr_list_parser state; + + isns_attr_list_parser_init(&state, &isns_dd_template); + if (argc == 0 || (argc == 1 && !strcmp(argv[0], "help"))) { + printf("DD deregistration:\n" + " isnsadm --dd-deregister dd-id attr=value attr=value ...\n" + "You cannot specify more than one domain.\n" + "The following attributes are recognized:\n"); + isns_attr_list_parser_help(&state); + exit(0); + } + + *dd_id = parse_count(argv[0]); + + return isns_parse_attrs(argc - 1, argv + 1, keys, &state); +} + +int +deregister_domain(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + isns_simple_t *msg; + uint32_t dd_id, status; + + if (!parse_dd_deregistration(argv, argc, &dd_id, &attrs)) + isns_fatal("Unable to parse DD registration\n"); + + msg = isns_create_dd_deregistration(clnt, dd_id, &attrs); + isns_attr_list_destroy(&attrs); + + if (msg == NULL) { + isns_error("Cannot create message\n"); + return ISNS_INTERNAL_ERROR; + } + + status = isns_client_call(clnt, &msg); + if (status != ISNS_SUCCESS) { + isns_error("Deregistration failed: %s\n", + isns_strerror(status)); + return status; + } + + isns_simple_free(msg); + return status; +} + +/* + * Parse a policy + */ +int +parse_policy(int argc, char **argv, isns_attr_list_t *attrs, + const char *help_title, const char *help_action) +{ + struct isns_attr_list_parser state; + + isns_attr_list_parser_init(&state, &isns_policy_template); + state.nil_permitted = 0; + state.load_key = load_key_callback; + state.generate_key = generate_key_callback; + + if (argc == 1 && !strcmp(argv[0], "help")) { + printf("%s:\n" + " isnsadm %s attr=value attr=value ...\n" + "Specifying a Security Policy Index is mandatory.\n" + "\nThe following attributes are recognized:\n", + help_title, help_action); + isns_attr_list_parser_help(&state); + exit(0); + } + + return isns_parse_attrs(argc, argv, attrs, &state); +} + +static int +__create_policy(isns_client_t *clnt, const isns_attr_list_t *attrs) +{ + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_object_t *obj; + int status; + + obj = isns_create_object(&isns_policy_template, attrs, NULL); + if (!obj) + isns_fatal("Cannot create policy object\n"); + isns_object_list_append(&objects, obj); + + status = __register_objects(clnt, NULL, &objects); + isns_object_list_destroy(&objects); + return status; +} + +/* + * Enroll a new client + */ +int +enroll_client(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + const char *client_name; + int status; + + if (argc == 0) + usage(1, "Missing client name"); + + client_name = *argv++; --argc; + + isns_attr_list_append_string(&attrs, + OPENISNS_TAG_POLICY_SPI, + client_name); +#if 0 + isns_attr_list_append_string(&attrs, + OPENISNS_TAG_POLICY_SOURCE_NAME, + client_name); +#endif + + if (!opt_keyfile) { + static char namebuf[PATH_MAX]; + + snprintf(namebuf, sizeof(namebuf), "%s.key", client_name); + opt_keyfile = namebuf; + } + + if (argc && !parse_policy(argc, argv, &attrs, + "Enroll an iSNS client", + "--enroll hostname")) + isns_fatal("Cannot parse policy\n"); + + /* If no key is given, generate one */ + if (!isns_attr_list_contains(&attrs, OPENISNS_TAG_POLICY_KEY)) { + printf("No key given, generating one\n"); + isns_attr_list_append_attr(&attrs, + generate_key_callback()); + } + + status = __create_policy(clnt, &attrs); + isns_attr_list_destroy(&attrs); + return status; +} + + +/* + * Create a new policy + */ +int +edit_policy(isns_client_t *clnt, int argc, char **argv) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + int status; + + if (!parse_policy(argc, argv, &attrs, + "Edit an existing policy", + "--edit-policy")) + isns_fatal("Cannot parse policy\n"); + + status = __create_policy(clnt, &attrs); + isns_attr_list_destroy(&attrs); + + return status; +} + +#ifdef WITH_SECURITY +static isns_attr_t * +__key_to_attr(EVP_PKEY *pkey) +{ + struct __isns_opaque key; + isns_value_t value; + isns_attr_t *attr = NULL; + + if (!isns_dsa_encode_public(pkey, &key.ptr, &key.len)) + goto out; + + /* Must pad key. This means we may end up encoding a few + * bytes of trash. Oh well. */ + key.len = ISNS_PAD(key.len); + + value = ISNS_VALUE_INIT(opaque, key); + attr = isns_attr_alloc(OPENISNS_TAG_POLICY_KEY, NULL, &value); + + isns_free(key.ptr); + +out: + EVP_PKEY_free(pkey); + return attr; +} + +isns_attr_t * +generate_key_callback(void) +{ + EVP_PKEY *pkey; + + if (opt_keyfile == NULL) + isns_fatal("Key generation requires --keyfile option\n"); + + if (!(pkey = isns_dsa_generate_key())) + isns_fatal("Key generation failed\n"); + + if (!isns_dsa_store_private(opt_keyfile, pkey)) + isns_fatal("Unable to write private key to %s\n", + opt_keyfile); + + printf("Stored DSA private key in %s\n", opt_keyfile); + return __key_to_attr(pkey); +} + +isns_attr_t * +load_key_callback(const char *pathname) +{ + EVP_PKEY *pkey; + + if (!(pkey = isns_dsa_load_public(pathname))) + isns_fatal("Unable to load public key from file %s\n", pathname); + + return __key_to_attr(pkey); +} + +#else /* WITH_SECURITY */ +isns_attr_t * +generate_key_callback(void) +{ + isns_fatal("Authentication disabled in this build\n"); + return NULL; +} + +isns_attr_t * +load_key_callback(const char *pathname) +{ + isns_fatal("Authentication disabled in this build\n"); + return NULL; +} + +#endif diff --git a/utils/open-isns/isnsd.c b/utils/open-isns/isnsd.c new file mode 100644 index 0000000..3f983d6 --- /dev/null +++ b/utils/open-isns/isnsd.c @@ -0,0 +1,299 @@ +/* + * isnsd - the iSNS Daemon + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#ifdef MTRACE +# include <mcheck.h> +#endif + +#include <isns.h> +#include "security.h" +#include "util.h" +#include "paths.h" +#include "internal.h" + +enum { + MODE_NORMAL, + MODE_DUMP_DB, + MODE_INIT, +}; + +static const char * opt_configfile = ISNS_DEFAULT_ISNSD_CONFIG; +static int opt_af = AF_UNSPEC; +static int opt_mode = MODE_NORMAL; +static int opt_foreground = 0; + +static char * slp_url; + +static int init_server(void); +static void run_server(isns_server_t *, isns_db_t *); +static void usage(int, const char *); +static void cleanup(int); + +static struct option options[] = { + { "config", required_argument, NULL, 'c' }, + { "debug", required_argument, NULL, 'd' }, + { "foreground", no_argument, NULL, 'f' }, + { "init", no_argument, NULL, MODE_INIT }, + { "dump-db", no_argument, NULL, MODE_DUMP_DB }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } +}; + +int +main(int argc, char **argv) +{ + isns_server_t *server; + isns_source_t *source; + isns_db_t *db; + int c; + +#ifdef MTRACE + mtrace(); +#endif + + while ((c = getopt_long(argc, argv, "46c:d:fh", options, NULL)) != -1) { + switch (c) { + case '4': + opt_af = AF_INET; + break; + + case '6': + opt_af = AF_INET6; + break; + + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + case 'f': + opt_foreground = 1; + break; + + case MODE_DUMP_DB: + case MODE_INIT: + opt_mode = c; + break; + + case 'h': + usage(0, NULL); + + case 'V': + printf("Open-iSNS version %s\n" + "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n", + OPENISNS_VERSION_STRING); + return 0; + + default: + usage(1, "Unknown option"); + } + } + + if (optind != argc) + usage(1, NULL); + + isns_read_config(opt_configfile); + + if (!isns_config.ic_source_name) + usage(1, "Please specify an iSNS source name"); + source = isns_source_create_iscsi(isns_config.ic_source_name); + + if (opt_mode == MODE_INIT) + return !init_server(); + + if (opt_mode == MODE_NORMAL) + isns_write_pidfile(isns_config.ic_pidfile); + + db = isns_db_open(isns_config.ic_database); + if (db == NULL) + isns_fatal("Unable to open database\n"); + + if (opt_mode == MODE_DUMP_DB) { + isns_db_print(db, isns_print_stdout); + exit(0); + } + + if (!opt_foreground) { + if (daemon(0, 0) < 0) + isns_fatal("Unable to background server process\n"); + isns_log_background(); + isns_update_pidfile(isns_config.ic_pidfile); + } + + signal(SIGTERM, cleanup); + signal(SIGINT, cleanup); + + server = isns_create_server(source, db, &isns_default_service_ops); + + run_server(server, db); + return 0; +} + +void +usage(int exval, const char *msg) +{ + if (msg) + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, + "Usage: isnsd [options]\n\n" + " --config Specify alternative config fille\n" + " --foreground Do not put daemon in the background\n" + " --debug Enable debugging (list of debug flags)\n" + " --init Initialize the server (key generation etc)\n" + " --dump-db Display the database contents and exit\n" + " --help Print this message\n" + ); + exit(exval); +} + +void +cleanup(int sig) +{ + isns_remove_pidfile(isns_config.ic_pidfile); + exit(1); +} + +static void +slp_cleanup(void) +{ + char *url = slp_url; + + slp_url = NULL; + if (url) { + isns_slp_unregister(url); + isns_free(url); + } +} + +/* + * Initialize server + */ +int +init_server(void) +{ + if (!isns_security_init()) + return 0; + + /* Anything else? */ + + return 1; +} + +/* + * Server main loop + */ +void +run_server(isns_server_t *server, isns_db_t *db) +{ + isns_socket_t *sock; + isns_security_t *ctx = NULL; + isns_message_t *msg, *resp; + int status; + + if (isns_config.ic_security) { + const char *ksname; + isns_keystore_t *ks; + + ctx = isns_default_security_context(1); + if (!(ksname = isns_config.ic_client_keystore)) + isns_fatal("config problem: no key store specified\n"); + if (!strcasecmp(ksname, "db:")) + ks = isns_create_db_keystore(db); + else + ks = isns_create_keystore(ksname); + if (ks == NULL) + isns_fatal("Unable to create keystore %s\n", ksname); + isns_security_set_keystore(ctx, ks); + } + + status = isns_dd_load_all(db); + if (status != ISNS_SUCCESS) + isns_fatal("Problem loading Discovery Domains from database\n"); + + if (isns_config.ic_control_socket) { + sock = isns_create_server_socket(isns_config.ic_control_socket, + NULL, AF_UNSPEC, SOCK_STREAM); + if (sock == NULL) + isns_fatal("Unable to create control socket\n"); + /* + isns_socket_set_security_ctx(sock, ctx); + */ + } + + sock = isns_create_server_socket(isns_config.ic_bind_address, + "isns", opt_af, SOCK_STREAM); + if (sock == NULL) + isns_fatal("Unable to create server socket\n"); + isns_socket_set_security_ctx(sock, ctx); + + if (isns_config.ic_slp_register) { + slp_url = isns_slp_build_url(0); + isns_slp_register(slp_url); + + atexit(slp_cleanup); + } + + isns_esi_init(server); + isns_scn_init(server); + + while (1) { + struct timeval timeout = { 0, 0 }; + time_t now, then, next_timeout = time(NULL) + 3600; + + /* Expire entities that haven't seen any activity + * for a while. */ + if (isns_config.ic_registration_period) { + then = isns_db_expire(db); + if (then && then < next_timeout) + next_timeout = then; + } + + /* Run any timers (eg for ESI) */ + then = isns_run_timers(); + if (then && then < next_timeout) + next_timeout = then; + + /* There may be pending SCNs, push them out now */ + then = isns_scn_transmit_all(); + if (then && then < next_timeout) + next_timeout = then; + + /* Purge any objects that have been marked for removal + * from the DB (deleting them, or moving them to limbo + * state). */ + isns_db_purge(db); + + /* Determine how long we can sleep before working + * the ESI queues and DB expiry again. */ + now = time(NULL); + if (next_timeout <= now) + continue; + timeout.tv_sec = next_timeout - now; + + if ((msg = isns_recv_message(&timeout)) == NULL) + continue; + + if ((resp = isns_process_message(server, msg)) != NULL) { + isns_socket_t *sock = isns_message_socket(msg); + + isns_socket_send(sock, resp); + isns_message_release(resp); + } + + isns_message_release(msg); + } +} diff --git a/utils/open-isns/isnsdd.c b/utils/open-isns/isnsdd.c new file mode 100644 index 0000000..e4e212d --- /dev/null +++ b/utils/open-isns/isnsdd.c @@ -0,0 +1,1153 @@ +/* + * isnsdd - the iSNS Discovery Daemon + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + * + * The way isnsdd communicates with local services (initiator, + * target) is via a set of files and signals. That sounds rather + * awkward, but it's a lot simpler to add to these services + * than another socket based communication mechanism I guess. + */ + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <sys/wait.h> + +#ifdef MTRACE +# include <mcheck.h> +#endif + +#include <isns.h> +#include "security.h" +#include "util.h" +#include "isns-proto.h" +#include "paths.h" +#include "attrs.h" + +enum { + ROLE_INITIATOR = 1, + ROLE_MONITOR = 2, +}; + +#define ISNSDD_REG_NAME "isns" +#define ISNSDD_PGT_OFFSET 10000 +#define MAX_RETRY_TIMEOUT 300 + +typedef struct isns_proxy isns_proxy_t; +struct isns_proxy { + isns_list_t ip_list; + char * ip_eid; + isns_object_t * ip_entity; + isns_client_t * ip_client; + isns_object_list_t ip_objects; + time_t ip_last_registration; +}; + +static const char * opt_configfile = ISNS_DEFAULT_ISNSDD_CONFIG; +static int opt_af = AF_INET6; +static int opt_foreground = 0; +static int opt_role = ROLE_INITIATOR; +static int opt_scn_bits = ISNS_SCN_OBJECT_UPDATED_MASK | + ISNS_SCN_OBJECT_ADDED_MASK | + ISNS_SCN_OBJECT_REMOVED_MASK | + ISNS_SCN_TARGET_AND_SELF_ONLY_MASK; +static unsigned int opt_retry_timeout = 10; +static int opt_esi = 1; + +static isns_socket_t * server_socket; +static ISNS_LIST_DECLARE(proxies); +static isns_object_list_t local_registry = ISNS_OBJECT_LIST_INIT; +static isns_object_list_t local_portals = ISNS_OBJECT_LIST_INIT; +static isns_object_list_t visible_nodes = ISNS_OBJECT_LIST_INIT; +static unsigned int esi_interval; +static int should_reexport; + +static void run_discovery(isns_server_t *srv); +static void scn_callback(isns_db_t *, uint32_t, + isns_object_template_t *, + const char *, const char *); +static void refresh_registration(void *); +static void retry_registration(void *); +static void load_exported_objects(void); +static void store_imported_objects(void); +static void usage(int, const char *); + +static void install_sighandler(int, void (*func)(int)); +static void sig_cleanup(int); +static void sig_reread(int); + +static struct option options[] = { + { "config", required_argument, NULL, 'c' }, + { "debug", required_argument, NULL, 'd' }, + { "foreground", no_argument, NULL, 'f' }, + { "role", required_argument, NULL, 'r' }, + { "no-esi", no_argument, NULL, 'E' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } +}; + +int +main(int argc, char **argv) +{ + isns_server_t *server; + isns_source_t *source; + isns_db_t *db; + int c; + +#ifdef MTRACE + mtrace(); +#endif + + while ((c = getopt_long(argc, argv, "46c:d:Efhr:", options, NULL)) != -1) { + switch (c) { + case '4': + opt_af = AF_INET; + break; + + case '6': + opt_af = AF_INET6; + break; + + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + case 'E': + opt_esi = 0; + break; + + case 'f': + opt_foreground = 1; + break; + + case 'h': + usage(0, NULL); + + case 'r': + if (!strcasecmp(optarg, "initiator")) + opt_role = ROLE_INITIATOR; + else + if (!strcasecmp(optarg, "control") + || !strcasecmp(optarg, "monitor")) + opt_role = ROLE_MONITOR; + else { + isns_error("Unknown role \"%s\"\n", optarg); + usage(1, NULL); + } + break; + + case 'V': + printf("Open-iSNS version %s\n" + "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n", + OPENISNS_VERSION_STRING); + return 0; + + default: + usage(1, "Unknown option"); + } + } + + if (optind != argc) + usage(1, NULL); + + /* If the config code derives the source name + * automatically, we want it to be distinct from + * any other source name (chosen by eg the iSCSI + * initiator). Adding a suffix of ":isns" is a + * somewhat lame attempt. + */ + isns_config.ic_source_suffix = "isns"; + + isns_read_config(opt_configfile); + + if (!isns_config.ic_source_name) + usage(1, "Please specify an iSNS source name"); + source = isns_source_create_iscsi(isns_config.ic_source_name); + + isns_write_pidfile(isns_config.ic_pidfile); + + if (!opt_foreground) { + if (daemon(0, 0) < 0) + isns_fatal("Unable to background server process\n"); + isns_log_background(); + isns_update_pidfile(isns_config.ic_pidfile); + } + + install_sighandler(SIGTERM, sig_cleanup); + install_sighandler(SIGINT, sig_cleanup); + install_sighandler(SIGUSR2, sig_reread); + + /* Create a DB object that shadows our portal list. This is for ESI - + * when an ESI comes in, the library will look up the portal in this + * database, and update its mtime. By checking the mtime at regular + * intervals, we can verify whether the server's ESIs actually + * reach us. + */ + db = isns_db_open_shadow(&local_portals); + + server = isns_create_server(source, db, &isns_callback_service_ops); + isns_server_set_scn_callback(server, scn_callback); + + run_discovery(server); + return 0; +} + +void +usage(int exval, const char *msg) +{ + if (msg) + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, + "Usage: isnsdd [options]\n\n" + " --role <role> Specify role (one of initiator, control)\n" + " --config Specify alternative config fille\n" + " --foreground Do not put daemon in the background\n" + " --no-esi Do not try to register an portals for ESI status inquiries\n" + " --debug Enable debugging (list of debug flags)\n" + " --help Print this message\n" + ); + exit(exval); +} + +void +install_sighandler(int signo, void (*func)(int)) +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = func; + sigaction(signo, &act, NULL); +} + +void +sig_reread(int sig) +{ + should_reexport = 1; +} + +void +sig_cleanup(int sig) +{ + isns_remove_pidfile(isns_config.ic_pidfile); + exit(1); +} + +/* + * Proxy handling functions + */ +static isns_proxy_t * +isns_create_proxy(const char *eid) +{ + isns_proxy_t *proxy; + + proxy = calloc(1, sizeof(*proxy)); + isns_list_init(&proxy->ip_list); + proxy->ip_eid = strdup(eid); + return proxy; +} + +static isns_proxy_t * +__isns_proxy_find(isns_list_t *head, const char *eid) +{ + isns_list_t *pos, *next; + + isns_list_foreach(head, pos, next) { + isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos); + + if (!strcmp(proxy->ip_eid, eid)) + return proxy; + } + return NULL; +} + +static isns_proxy_t * +isns_proxy_by_entity(const isns_object_t *entity) +{ + isns_list_t *pos, *next; + + isns_list_foreach(&proxies, pos, next) { + isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos); + + if (proxy->ip_entity == entity) + return proxy; + } + return NULL; +} + +static void +isns_proxy_erase(isns_proxy_t *proxy) +{ + isns_object_list_destroy(&proxy->ip_objects); + if (proxy->ip_client) { + isns_client_destroy(proxy->ip_client); + proxy->ip_client = NULL; + } + if (proxy->ip_entity) { + isns_object_release(proxy->ip_entity); + proxy->ip_entity = NULL; + } + isns_cancel_timer(refresh_registration, proxy); +} + +static void +isns_proxy_free(isns_proxy_t *proxy) +{ + isns_proxy_erase(proxy); + isns_list_del(&proxy->ip_list); + free(&proxy->ip_eid); + free(proxy); +} + +/* + * Force a re-registration of the whole object set. + */ +static void +force_reregistration(isns_proxy_t *proxy) +{ + isns_cancel_timer(refresh_registration, proxy); + isns_add_oneshot_timer(0, retry_registration, proxy); +} + +/* + * Refresh the registration by calling DevAttrQry + */ +static void +refresh_registration(void *ptr) +{ + isns_proxy_t *proxy = ptr; + isns_client_t *clnt = proxy->ip_client; + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT; + isns_simple_t *qry = NULL; + uint32_t status; + + isns_debug_state("Refreshing registration for %s\n", proxy->ip_eid); + isns_attr_list_append_string(&query_key, + ISNS_TAG_ENTITY_IDENTIFIER, + proxy->ip_eid); + + qry = isns_create_query(clnt, &query_key); + isns_attr_list_destroy(&query_key); + + /* We should have an async call mechanism. If the server + * is wedged, we'll block here, unable to service any other + * functions. + */ + status = isns_simple_call(clnt->ic_socket, &qry); + if (status != ISNS_SUCCESS) { + isns_error("Query failed: %s\n", isns_strerror(status)); + goto re_register; + } + + status = isns_query_response_get_objects(qry, &objects); + isns_simple_free(qry); + + if (status == ISNS_SUCCESS) { + if (objects.iol_count != 0) + return; + } else { + isns_error("Unable to parse query response\n"); + } + +re_register: + isns_warning("Lost registration, trying to re-register\n"); + force_reregistration(proxy); +} + +/* + * Check if all portals have seen ESI messages from the server + */ +static void +check_portal_registration(void *ptr) +{ + isns_object_list_t bad_portals = ISNS_OBJECT_LIST_INIT; + unsigned int i, need_reregister = 0, good_portals = 0; + time_t now; + + isns_debug_state("%s()\n", __FUNCTION__); + now = time(NULL); + for (i = 0; i < local_portals.iol_count; ++i) { + isns_object_t *obj = local_portals.iol_data[i]; + isns_portal_info_t portal_info; + isns_proxy_t *proxy; + time_t last_modified; + uint32_t interval; + + if (!isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval)) + continue; + + last_modified = isns_object_last_modified(obj); + if (last_modified + 2 * interval > now) { + good_portals++; + continue; + } + + isns_portal_from_object(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + obj); + + isns_notice("Portal %s did not receive ESIs within %u seconds - " + "server may have lost us.\n", + isns_portal_string(&portal_info), + now - last_modified); + + proxy = isns_proxy_by_entity(isns_object_get_entity(obj)); + if (!proxy) + continue; + + /* If we haven't received ANY ESIs, ever, the portal + * may be using a non-routable IP */ + if (last_modified <= proxy->ip_last_registration) + isns_object_list_append(&bad_portals, obj); + + force_reregistration(proxy); + need_reregister++; + } + + for (i = 0; i < bad_portals.iol_count; ++i) + isns_object_list_remove(&local_portals, bad_portals.iol_data[i]); + isns_object_list_destroy(&bad_portals); + + if (need_reregister && local_portals.iol_count == 0) { + /* Force a re-registration from scratch. + * This time without ESI. + */ + isns_notice("Suspiciously little ESI traffic - server may be broken\n"); + isns_notice("Disabling ESI\n"); + opt_esi = 0; + } +} + +static void +setup_esi_watchdog(void) +{ + unsigned int i; + + isns_cancel_timer(check_portal_registration, NULL); + esi_interval = 0; + + for (i = 0; i < local_portals.iol_count; ++i) { + isns_object_t *obj = local_portals.iol_data[i]; + uint32_t interval; + + /* should always succeed */ + if (isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval)) + continue; + + if (!esi_interval || interval < esi_interval) + esi_interval = interval; + } + + if (esi_interval) { + isns_debug_state("Setting up timer to check for ESI reachability\n"); + isns_add_timer(esi_interval * 4 / 5, + check_portal_registration, + NULL); + } +} + +static void +load_exported_objects(void) +{ + isns_debug_state("Reading list of exported objects\n"); + isns_object_list_destroy(&local_registry); + if (!isns_local_registry_load("!" ISNSDD_REG_NAME, 0, &local_registry)) { + isns_warning("Unable to obtain locally registered objects\n"); + return; + } +} + +static void +store_imported_objects(void) +{ + if (!isns_local_registry_store(ISNSDD_REG_NAME, 0, &visible_nodes)) + isns_warning("Unable to store discovered objects\n"); +} + +/* + * Given the DevAttrReg response, extract the entity ID we + * have been assigned. + */ +static int +extract_entity_id(isns_proxy_t *proxy, isns_simple_t *resp) +{ + isns_object_list_t resp_objects = ISNS_OBJECT_LIST_INIT; + isns_object_t *entity = NULL; + int status; + unsigned int i; + + status = isns_query_response_get_objects(resp, &resp_objects); + if (status) { + isns_error("Unable to extract object list from " + "registration response: %s\n", + isns_strerror(status), status); + goto out; + } + + for (i = 0; i < resp_objects.iol_count; ++i) { + isns_object_t *obj = resp_objects.iol_data[i]; + uint32_t interval; + + if (!isns_object_is_entity(obj)) + continue; + + if (entity) { + isns_error("Server returns more than one entity " + "in registration response. What a weirdo.\n"); + continue; + } + entity = obj; + + if (!isns_object_get_uint32(obj, + ISNS_TAG_REGISTRATION_PERIOD, + &interval)) + continue; + + if (interval == 0) { + isns_error("Server returns a registration period of 0\n"); + continue; + } + + isns_debug_state("Setting up timer for registration refresh\n"); + isns_add_timer(interval / 2, + refresh_registration, + proxy); + } + + for (i = 0; i < resp_objects.iol_count; ++i) { + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + isns_object_t *obj = resp_objects.iol_data[i]; + uint32_t interval; + + if (!isns_object_is_portal(obj) + || !isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval)) + continue; + + if (interval == 0) { + isns_error("Server returns an ESI interval of 0\n"); + continue; + } + + isns_object_get_key_attrs(obj, &key_attrs); + if (!(obj = isns_object_list_lookup(&proxy->ip_objects, NULL, &key_attrs))) { + isns_error("Server response includes a portal we never registered\n"); + continue; + } + + isns_object_set_uint32(obj, ISNS_TAG_ESI_INTERVAL, interval); + + /* Server enabled ESI for this portal, so add it to + * the list of local portals we regularly check for + * incoming ESI messages. */ + isns_object_list_append(&local_portals, obj); + } + + proxy->ip_last_registration = time(NULL); +out: + isns_object_list_destroy(&resp_objects); + return status; +} + +static inline void +__add_release_object(isns_object_list_t *objects, isns_object_t *cur) +{ + if (cur == NULL) + return; + isns_object_list_append(objects, cur); + isns_object_release(cur); +} + +/* + * Rebuild the list of proxies given the set of entities + */ +void +rebuild_proxy_list(isns_object_list_t *entities, isns_list_t *old_list) +{ + isns_proxy_t *proxy; + unsigned int i; + + isns_list_move(old_list, &proxies); + + for (i = 0; i < entities->iol_count; ++i) { + isns_object_t *entity = entities->iol_data[i]; + isns_object_t *node; + const char *eid; + + eid = isns_entity_name(entity); + if (eid == NULL) { + isns_error("Whoopee, entity without name\n"); + continue; + } + + proxy = __isns_proxy_find(old_list, eid); + if (proxy == NULL) { + proxy = isns_create_proxy(eid); + } else { + isns_proxy_erase(proxy); + } + + isns_object_list_append(&proxy->ip_objects, entity); + isns_object_get_descendants(entity, NULL, &proxy->ip_objects); + + node = isns_object_list_lookup(&proxy->ip_objects, + &isns_iscsi_node_template, + NULL); + if (node == NULL) { + isns_warning("Service %s did not register any " + "storage nodes - skipped\n", eid); + continue; + } + + proxy->ip_client = isns_create_client(NULL, + isns_storage_node_name(node)); + proxy->ip_entity = isns_object_get(entity); + + isns_list_del(&proxy->ip_list); + isns_list_append(&proxies, &proxy->ip_list); + } +} + +/* + * Unregister old proxies + */ +static void +unregister_entities(isns_list_t *list) +{ + while (!isns_list_empty(list)) { + isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, list->next); + + /* XXX send a DevDereg */ + isns_proxy_free(proxy); + } +} + +/* + * The local registry creates fake entities to group objects + * registered by the same service. We use this to perform + * several registration calls, each with a different EID + */ +static int +register_entity(isns_proxy_t *proxy) +{ + isns_client_t *clnt = proxy->ip_client; + isns_simple_t *call = NULL; + int status; + + call = isns_create_registration(clnt, proxy->ip_entity); + isns_registration_set_replace(call, 1); + isns_registration_add_object_list(call, &proxy->ip_objects); + + status = isns_simple_call(clnt->ic_socket, &call); + if (status == ISNS_SUCCESS) { + /* Extract the EID and registration period */ + extract_entity_id(proxy, call); + } + + isns_simple_free(call); + return status; +} + +static int +register_exported_entities(void) +{ + int status = ISNS_SUCCESS; + isns_list_t *pos, *next; + + isns_list_foreach(&proxies, pos, next) { + isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos); + + status = register_entity(proxy); + if (status != ISNS_SUCCESS) + break; + } + + setup_esi_watchdog(); + return status; +} + +static void +all_objects_set(isns_object_list_t *list, uint32_t tag, uint32_t value) +{ + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + isns_object_set_uint32(obj, tag, value); + } +} + +static void +all_objects_unset(isns_object_list_t *list, uint32_t tag) +{ + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + isns_object_delete_attr(obj, tag); + } +} + +static int +register_exported_objects(isns_client_t *clnt) +{ + isns_portal_info_t portal_info; + isns_object_list_t entities = ISNS_OBJECT_LIST_INIT; + isns_object_list_t portals = ISNS_OBJECT_LIST_INIT; + isns_simple_t *call = NULL; + int status, with_esi; + unsigned int i, my_port; + isns_list_t old_proxies; + + if (!isns_socket_get_portal_info(server_socket, &portal_info)) + isns_fatal("Unable to get portal info\n"); + my_port = isns_portal_tcpudp_port(&portal_info); + + /* Look up all entites and portals */ + isns_object_list_gang_lookup(&local_registry, + &isns_entity_template, NULL, + &entities); + isns_object_list_gang_lookup(&local_registry, + &isns_portal_template, NULL, + &portals); + + isns_list_init(&old_proxies); + rebuild_proxy_list(&entities, &old_proxies); + unregister_entities(&old_proxies); + + /* Enable SCN on all portals we're about to register */ + all_objects_set(&portals, ISNS_TAG_SCN_PORT, my_port); + + /* Try ESI first. If the server doesn't support it, or doesn't + * have the resources to serve us, fall back to normal + * registration refresh. */ + if (opt_esi) { + all_objects_set(&portals, + ISNS_TAG_ESI_INTERVAL, + isns_config.ic_esi_min_interval); + all_objects_set(&portals, + ISNS_TAG_ESI_PORT, + my_port); + } + + for (with_esi = opt_esi; 1; with_esi--) { + status = register_exported_entities(); + + /* At some point, we need to add these portals + * to the local_portals list so that ESI works + * properly. + * Right now, we extract the portals from the response + * and add those. The down side of this is that we no + * longer use the same object (pointer) to refer to the + * same thing. The up side is that the information returned + * by the server reflects the correct ESI interval. + */ + if (status == ISNS_SUCCESS) + break; + + if (status != ISNS_ESI_NOT_AVAILABLE || with_esi == 0) { + isns_error("Failed to register object(s): %s\n", + isns_strerror(status)); + goto out; + } + + /* Continue and retry without ESI */ + all_objects_unset(&portals, ISNS_TAG_ESI_INTERVAL); + all_objects_unset(&portals, ISNS_TAG_ESI_PORT); + } + + for (i = 0; i < local_registry.iol_count; ++i) { + isns_object_t *obj = local_registry.iol_data[i]; + isns_source_t *source; + int status; + + if (!isns_object_is_iscsi_node(obj) + && !isns_object_is_fc_port(obj)) + continue; + + if (!(source = isns_source_from_object(obj))) + continue; + call = isns_create_scn_registration2(clnt, opt_scn_bits, source); + status = isns_simple_call(clnt->ic_socket, &call); + if (status != ISNS_SUCCESS) { + isns_error("SCN registration for %s failed: %s\n", + isns_storage_node_name(obj), + isns_strerror(status)); + } + isns_source_release(source); + } + +out: + if (call) + isns_simple_free(call); + isns_object_list_destroy(&entities); + isns_object_list_destroy(&portals); + return status; +} + +static void +retry_registration(void *ptr) +{ + isns_proxy_t *proxy = ptr; + static unsigned int timeout = 0; + int status; + + status = register_exported_objects(proxy->ip_client); + if (status) { + if (timeout == 0) + timeout = opt_retry_timeout; + else if (timeout >= MAX_RETRY_TIMEOUT) + timeout = MAX_RETRY_TIMEOUT; + + isns_debug_state("Retrying to register in %u seconds\n", timeout); + isns_add_oneshot_timer(timeout, retry_registration, proxy); + + /* Exponential backoff */ + timeout <<= 1; + } +} + +/* + * Get a list of all visible storage nodes + */ +static int +get_objects_from_query(isns_simple_t *resp) +{ + isns_object_list_t resp_objects = ISNS_OBJECT_LIST_INIT; + unsigned int i; + int status; + + status = isns_query_response_get_objects(resp, &resp_objects); + if (status) { + isns_error("Unable to extract object list from " + "query response: %s\n", + isns_strerror(status)); + return status; + } + + isns_debug_state("Initial query returned %u object(s)\n", resp_objects.iol_count); + for (i = 0; i < resp_objects.iol_count; ++i) { + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + isns_object_t *obj = resp_objects.iol_data[i]; + isns_object_t *found; + + if (!isns_object_extract_keys(obj, &key_attrs)) + continue; + + /* Don't add an object twice, and don't add objects + * that *we* registered. + * This still leaves any default PGs created by the server, + * but we cannot help that (for now). + */ + found = isns_object_list_lookup(&visible_nodes, NULL, &key_attrs); + if (!found) + found = isns_object_list_lookup(&local_registry, NULL, &key_attrs); + if (found) { + isns_object_release(found); + } else { + isns_object_list_append(&visible_nodes, obj); + } + isns_attr_list_destroy(&key_attrs); + } + + isns_object_list_destroy(&resp_objects); + return status; +} + +static int +query_storage_node(isns_source_t *source, const char *name) +{ + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + isns_simple_t *call; + uint32_t tag; + int status; + isns_client_t *clnt; + + if (isns_source_type(source) != ISNS_TAG_ISCSI_NAME) { + isns_error("FC source node - doesn't work yet\n"); + return ISNS_SUCCESS; + } + clnt = isns_create_client(NULL, isns_source_name(source)); + + tag = isns_source_type(source); + if (name) { + isns_attr_list_append_string(&key_attrs, tag, name); + } else { + /* Query for visible nodes */ + isns_attr_list_append_nil(&key_attrs, tag); + } + + call = isns_create_query2(clnt, &key_attrs, source); + isns_attr_list_destroy(&key_attrs); + + isns_query_request_attr_tag(call, tag); + switch (tag) { + case ISNS_TAG_ISCSI_NAME: + isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_NODE_TYPE); + isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_ALIAS); + isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_NODE_INDEX); + + isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_IP_ADDRESS); + isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_TCP_UDP_PORT); + isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_INDEX); + + isns_query_request_attr_tag(call, ISNS_TAG_PG_ISCSI_NAME); + isns_query_request_attr_tag(call, ISNS_TAG_PG_PORTAL_IP_ADDR); + isns_query_request_attr_tag(call, ISNS_TAG_PG_PORTAL_TCP_UDP_PORT); + isns_query_request_attr_tag(call, ISNS_TAG_PG_TAG); + isns_query_request_attr_tag(call, ISNS_TAG_PG_INDEX); + break; + + default: ; + } + + status = isns_simple_call(clnt->ic_socket, &call); + if (status == ISNS_SUCCESS) + status = get_objects_from_query(call); + + isns_simple_free(call); + isns_client_destroy(clnt); + return status; +} + +/* + * Query for visible iscsi nodes + */ +static int +query_visible(void) +{ + unsigned int i; + + for (i = 0; i < local_registry.iol_count; ++i) { + isns_object_t *obj = local_registry.iol_data[i]; + isns_source_t *source; + int status; + + if (!isns_object_is_iscsi_node(obj) + && !isns_object_is_fc_port(obj)) + continue; + + if (isns_object_is_fc_port(obj)) { + isns_error("FC source node - sorry, won't work yet\n"); + continue; + } + + if (!(source = isns_source_from_object(obj))) + continue; + status = query_storage_node(source, NULL); + if (status != ISNS_SUCCESS) { + isns_warning("Unable to run query on behalf of %s: %s\n", + isns_storage_node_name(obj), + isns_strerror(status)); + } + isns_source_release(source); + } + return ISNS_SUCCESS; +} + +/* + * Invoke the registered callout program + */ +static void +callout(const char *how, isns_object_t *obj, unsigned int bitmap) +{ + char *argv[128]; + int fargc, argc = 0; + pid_t pid; + + if (!isns_config.ic_scn_callout) + return; + + argv[argc++] = isns_config.ic_scn_callout; + argv[argc++] = (char *) how; + fargc = argc; + + argc += isns_print_attrs(obj, argv + argc, 128 - argc); + + pid = fork(); + if (pid == 0) { + execv(argv[0], argv); + isns_fatal("Cannot execute %s: %m\n", argv[0]); + } + + while (fargc < argc) + isns_free(argv[fargc++]); + + if (pid < 0) { + isns_error("fork: %m\n"); + return; + } + + while (waitpid(pid, NULL, 0) < 0) + ; +} + +/* + * This is called when we receive a State Change Notification + */ +static void +scn_callback(isns_db_t *db, uint32_t bitmap, + isns_object_template_t *node_type, + const char *node_name, + const char *dst_name) +{ + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + uint32_t key_tag; + isns_object_t *node = NULL, *recipient = NULL; + + isns_notice("%s \"%s\" %s\n", + isns_object_template_name(node_type), + node_name, isns_event_string(bitmap)); + + /* This is either an iSCSI node or a FC node - in + both cases the storage node name is the key attr */ + if (node_type == &isns_iscsi_node_template) { + key_tag = ISNS_TAG_ISCSI_NAME; + } else if (node_type == &isns_fc_node_template) { + key_tag = ISNS_TAG_FC_PORT_NAME_WWPN; + } else + return; + + isns_attr_list_append_string(&key_attrs, key_tag, dst_name); + recipient = isns_object_list_lookup(&local_registry, node_type, &key_attrs); + if (recipient == NULL) { + isns_error("Received SCN for unknown recipient \"%s\"\n", + dst_name); + goto out; + } + isns_attr_list_destroy(&key_attrs); + + isns_attr_list_append_string(&key_attrs, key_tag, node_name); + node = isns_object_list_lookup(&visible_nodes, node_type, &key_attrs); + + if (bitmap & (ISNS_SCN_OBJECT_REMOVED_MASK|ISNS_SCN_DD_MEMBER_REMOVED_MASK)) { + if (node) { + isns_object_list_remove(&visible_nodes, node); + /* FIXME: We also want to remove any PGs associated with + * this node. */ + } + store_imported_objects(); + callout("remove", node, bitmap); + } else + if (bitmap & (ISNS_SCN_OBJECT_ADDED_MASK|ISNS_SCN_OBJECT_UPDATED_MASK|ISNS_SCN_DD_MEMBER_ADDED_MASK)) { + const char *how = "add"; + isns_source_t *source; + + if (bitmap & ISNS_SCN_OBJECT_UPDATED_MASK) + how = "update"; + if (!node) { + node = isns_create_object(node_type, &key_attrs, NULL); + if (!node) + goto out; + isns_object_list_append(&visible_nodes, node); + } + + /* Query the server for information on this node */ + source = isns_source_from_object(recipient); + query_storage_node(source, node_name); + isns_source_release(source); + + store_imported_objects(); + callout(how, node, bitmap); + + } + +out: + if (node) + isns_object_release(node); + if (recipient) + isns_object_release(recipient); + isns_attr_list_destroy(&key_attrs); +} + +/* + * Server main loop + */ +void +run_discovery(isns_server_t *server) +{ + isns_client_t *clnt; + isns_security_t *ctx = NULL; + isns_message_t *msg, *resp; + + /* Create the server socket */ + ctx = isns_default_security_context(0); + server_socket = isns_create_server_socket(isns_config.ic_bind_address, + NULL, opt_af, SOCK_DGRAM); + if (server_socket == NULL) + isns_fatal("Unable to create server socket\n"); + isns_socket_set_security_ctx(server_socket, ctx); + + /* Create the client socket */ + clnt = isns_create_default_client(NULL); + if (clnt == NULL) + isns_fatal("Cannot connect to server\n"); + + /* Load all objects registered by local services */ + should_reexport = 1; + + while (1) { + struct timeval timeout = { 0, 0 }; + time_t now, then, next_timeout; + unsigned int function; + + next_timeout = time(NULL) + 3600; + + /* Run timers */ + then = isns_run_timers(); + if (then && then < next_timeout) + next_timeout = then; + + /* Determine how long we can sleep */ + now = time(NULL); + if (next_timeout <= now) + continue; + timeout.tv_sec = next_timeout - now; + + if (should_reexport) { + load_exported_objects(); + + if (register_exported_objects(clnt)) + isns_error("Failed to register exported objects.\n"); + + /* Prime the list of visible storage nodes */ + if (query_visible()) + isns_error("Unable to query list of visible nodes.\n"); + store_imported_objects(); + + should_reexport = 0; + } + + if ((msg = isns_recv_message(&timeout)) == NULL) + continue; + + function = isns_message_function(msg); + if (function != ISNS_STATE_CHANGE_NOTIFICATION + && function != ISNS_ENTITY_STATUS_INQUIRY) { + isns_warning("Discarding unexpected %s message\n", + isns_function_name(function)); + isns_message_release(msg); + continue; + } + + if ((resp = isns_process_message(server, msg)) != NULL) { + isns_socket_t *sock = isns_message_socket(msg); + + isns_socket_send(sock, resp); + isns_message_release(resp); + } + + isns_message_release(msg); + } +} diff --git a/utils/open-isns/isnssetup b/utils/open-isns/isnssetup new file mode 100644 index 0000000..df0bd00 --- /dev/null +++ b/utils/open-isns/isnssetup @@ -0,0 +1,52 @@ +#!/bin/sh +# +# isnssetup - bootstrap open-isns server +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This is a very simple script to bootstrap an iSNS server. +# It creates the necessary keys, enrolls a control node, +# and enrolls the local host as target and initiator. + +hostname=`hostname -f` + +if [ -f isnsd -a -d isnsadm ]; then + PATH=.:$PATH +fi + +# Massage the configuration file +for f in isnsadm.conf isnsdd.conf; do + etcfile=/etc/isns/$f + sed -e 's/^#*\(ServerAddress[[:space:]]*=\).*/\1 localhost/' \ + -e 's/^#*\(Security[[:space:]]*=\).*/\1 1/' \ + $etcfile > $etcfile.tmp + mv $etcfile.tmp $etcfile +done + +echo "*** Initializing server security ***" +isnsd --init +cp /etc/isns/auth_key.pub /etc/isns/server_key.pub + +if ps ax|grep isnsd | grep -qv grep; then + killall -TERM isnsd + sleep 1 +fi +isnsd +sleep 1 + +echo "*** Registering control node policy ***" +rm -f /etc/isns/control.key +isnsadm --local \ + --keyfile=/etc/isns/control.key \ + --enroll isns.control \ + node-type=ALL functions=ALL object-type=ALL + +echo "*** Registering control node ***" +isnsadm --local \ + --register control + +echo "*** Registering policy for server ***" +isnsadm --control \ + --enroll $hostname \ + key=/etc/isns/auth_key.pub \ + node-type=target+initiator diff --git a/utils/open-isns/local.c b/utils/open-isns/local.c new file mode 100644 index 0000000..4bc1cb1 --- /dev/null +++ b/utils/open-isns/local.c @@ -0,0 +1,353 @@ +/* + * Local iSNS registration + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + * + * The way isnsdd communicates with local services (initiator, + * target) is via a file and signals. That sounds rather + * awkward, but it's a lot simpler to add to these services + * than another socket based communication mechanism I guess. + * + * The file format is simple: + * <object> owner=<owner> + * <object> owner=<owner> + * ... + * + * <owner> identifies the service owning these entries. + * This is a service name, such as iscsid, tgtd, isnsdd, + * optionally followed by a colon and a PID. This allows + * removal of all entries created by one service in one go. + * + * <object> is the description of one iSNS object, using the + * syntax used by all other open-isns apps. + */ + +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> + +#include <isns.h> +#include "security.h" +#include "util.h" +#include "isns-proto.h" +#include "paths.h" +#include "attrs.h" +#include "util.h" + +typedef int __isns_local_registry_cb_fn_t(const char *line, + int argc, char **argv, + void *user_data); + +/* + * Build the owner=<svcname>:<pid> tag + */ +static const char * +__isns_local_registry_make_owner(const char *svcname, pid_t pid) +{ + static char owner[128]; + + if (pid == 0) { + return svcname; + } + snprintf(owner, sizeof(owner), "%s:%u", svcname, pid); + return owner; +} + +/* + * Read the registry file, match each entry against the given owner=<foo> tag, + * and invoke the callback function. + * This is used for both reading the registry, and rewriting it. + */ +static int +__isns_local_registry_read(const char *match_owner, + __isns_local_registry_cb_fn_t handle_matching, + __isns_local_registry_cb_fn_t handle_nonmatching, + void *user_data) +{ + const char *filename = isns_config.ic_local_registry_file; + char *line, *copy = NULL; + FILE *fp; + int rv = 0, owner_len; + + if (!(fp = fopen(filename, "r"))) { + if (errno == ENOENT) { + isns_debug_state("Unable to open %s: %m\n", filename); + return 1; + } + isns_error("Unable to open %s: %m\n", filename); + return 0; + } + + owner_len = match_owner? strlen(match_owner) : 0; + while ((line = parser_get_next_line(fp)) != NULL) { + __isns_local_registry_cb_fn_t *cb; + char *argv[256], *owner; + int argc = 0; + + isns_assign_string(©, line); + + argc = isns_attr_list_split(line, argv, 255); + if (argc <= 0) + continue; + + /* Last attr should be owner */ + if (strncasecmp(argv[argc-1], "owner=", 6)) { + isns_error("%s: syntax error (missing owner field)\n", + filename); + goto out; + } + owner = argv[argc-1] + 6; + + if (!strncasecmp(owner, match_owner, owner_len) + && (owner[owner_len] == '\0' || owner[owner_len] == ':')) + cb = handle_matching; + else + cb = handle_nonmatching; + + if (cb && !cb(copy, argc, argv, user_data)) + goto out; + + } + rv = 1; + +out: + free(copy); + fclose(fp); + return rv; +} + +/* + * Open and lock the registry file for writing. Returns an + * open stream and the name of the lock file. + * Follow up with _finish_write when done. + */ +static FILE * +__isns_local_registry_open_write(char **lock_name) +{ + char lock_path[PATH_MAX]; + FILE *fp; + int fd, retry; + + snprintf(lock_path, sizeof(lock_path), "%s.lock", + isns_config.ic_local_registry_file); + + for (retry = 0; retry < 5; ++retry) { + fd = open(lock_path, O_RDWR|O_CREAT|O_EXCL, 0644); + if (fd >= 0) + break; + if (errno != EEXIST) { + isns_error("Unable to create %s: %m\n", + lock_path); + return NULL; + } + isns_error("Cannot lock %s - retry in 1 sec\n", + isns_config.ic_local_registry_file); + sleep(1); + } + + if (!(fp = fdopen(fd, "w"))) { + isns_error("fdopen failed: %m\n"); + close(fd); + return NULL; + } + isns_assign_string(lock_name, lock_path); + return fp; +} + +/* + * We're done with (re)writing the registry. Commit the changes, + * or discard them. + * Also frees the lock_name returned by registry_open_write. + */ +static int +__isns_local_registry_finish_write(FILE *fp, char *lock_name, int commit) +{ + int rv = 1; + + fclose(fp); + if (!commit) { + if (unlink(lock_name)) + isns_error("Failed to unlink %s: %m\n", lock_name); + } else + if (rename(lock_name, isns_config.ic_local_registry_file)) { + isns_error("Failed to rename %s to %s: %m\n", + lock_name, isns_config.ic_local_registry_file); + rv = 0; + } + + free(lock_name); + return rv; +} + +/* + * Get the entity name for this service + */ +static char * +__isns_local_registry_entity_name(const char *owner) +{ + static char namebuf[1024]; + + snprintf(namebuf, sizeof(namebuf), "%s:%s", + isns_config.ic_entity_name, + owner); + return namebuf; +} + +/* + * Callback function which builds an iSNS object from the + * list of attr=tag values. + */ +static int +__isns_local_registry_load_object(const char *line, + int argc, char **argv, void *user_data) +{ + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + struct isns_attr_list_parser state; + isns_object_list_t *list = user_data; + isns_object_t *obj, *entity = NULL; + + for (; argc > 0; --argc) { + char *attr = argv[argc-1]; + + if (!strncasecmp(attr, "owner=", 6)) { + char *eid = __isns_local_registry_entity_name(attr + 6); + ISNS_QUICK_ATTR_LIST_DECLARE(key_attrs, + ISNS_TAG_ENTITY_IDENTIFIER, + string, eid); + + if (entity) { + isns_error("Duplicate owner entry in registry\n"); + continue; + } + isns_attr_print(&key_attrs.iqa_attr, isns_print_stdout); + entity = isns_object_list_lookup(list, + &isns_entity_template, + &key_attrs.iqa_list); + if (entity != NULL) + continue; + + isns_debug_state("Creating fake entity %s\n", eid); + entity = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, eid); + isns_object_list_append(list, entity); + } else { + break; + } + } + + isns_attr_list_parser_init(&state, NULL); + if (!isns_parse_attrs(argc, argv, &attrs, &state)) { + isns_error("Unable to parse attrs\n"); + isns_attr_list_destroy(&attrs); + return 0; + } + + obj = isns_create_object(isns_attr_list_parser_context(&state), + &attrs, entity); + isns_attr_list_destroy(&attrs); + + if (obj == NULL) { + isns_error("Unable to create object\n"); + return 0; + } + + isns_object_list_append(list, obj); + return 1; +} + +/* + * Callback function that simply writes out the line as-is + */ +static int +__isns_local_registry_rewrite_object(const char *line, + int argc, char **argv, void *user_data) +{ + FILE *ofp = user_data; + + fprintf(ofp, "%s\n", line); + return 1; +} + +/* + * Load all objects owner by a specific service from the local registry. + * If the svcname starts with "!", all entries except those matching this + * particular service are returned. + */ +int +isns_local_registry_load(const char *svcname, pid_t pid, isns_object_list_t *objs) +{ + __isns_local_registry_cb_fn_t *if_matching = NULL, *if_nonmatching = NULL; + + if (svcname == NULL) { + isns_error("%s: no svcname given\n", __FUNCTION__); + return 0; + } + if (*svcname == '!') { + if_nonmatching = __isns_local_registry_load_object; + svcname++; + } else { + if_matching = __isns_local_registry_load_object; + } + + return __isns_local_registry_read( + __isns_local_registry_make_owner(svcname, pid), + if_matching, if_nonmatching, objs); +} + +/* + * Store the given list of objects in the registry. + * This replaces all objects previously registered by this service. + */ +int +isns_local_registry_store(const char *svcname, pid_t pid, const isns_object_list_t *objs) +{ + const char *owner = __isns_local_registry_make_owner(svcname, pid); + char *lock_name = NULL; + FILE *ofp; + + if (!(ofp = __isns_local_registry_open_write(&lock_name))) { + isns_error("%s: could not open registry for writing\n", __FUNCTION__); + return 0; + } + + /* First, purge all entries previously belonging to this owner */ + if (!__isns_local_registry_read(owner, NULL, __isns_local_registry_rewrite_object, ofp)) + goto failed; + + if (objs) { + unsigned int i; + + for (i = 0; i < objs->iol_count; ++i) { + isns_object_t *obj = objs->iol_data[i]; + char *argv[256]; + int i, argc; + + argc = isns_print_attrs(obj, argv, 256); + for (i = 0; i < argc; ++i) + fprintf(ofp, "%s ", argv[i]); + fprintf(ofp, "owner=%s\n", owner); + } + } + + return __isns_local_registry_finish_write(ofp, lock_name, 1); + +failed: + isns_error("%s: error rewriting registry file\n", __FUNCTION__); + __isns_local_registry_finish_write(ofp, lock_name, 0); + return 0; +} + +/* + * Purge the local registry of all objects owned by the + * given service. + */ +int +isns_local_registry_purge(const char *svcname, pid_t pid) +{ + return isns_local_registry_store(svcname, pid, NULL); +} diff --git a/utils/open-isns/logging.c b/utils/open-isns/logging.c new file mode 100644 index 0000000..63ebbef --- /dev/null +++ b/utils/open-isns/logging.c @@ -0,0 +1,228 @@ +/* + * Logging related utility functions. + * + * Copyright (C) 2004-2007 Olaf Kirch <okir@suse.de> + */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "util.h" + +static unsigned int log_stdout = 1; +static unsigned int debugging = 0; + +/* + * When backgrounding, any logging output should + * go to syslog instead of stdout + */ +void +isns_log_background(void) +{ + log_stdout = 0; +} + +/* + * For output to syslog, sanitize the format string + * by removing newlines. + */ +static const char * +sanitize_format(const char *fmt) +{ + static char __fmt[1024]; + unsigned int len; + + /* Don't bother unless there's a newline */ + if (!strchr(fmt, '\n')) + return fmt; + + len = strlen(fmt); + + /* Decline if the buffer would overflow */ + if (len >= sizeof(__fmt)) + return fmt; + + strcpy(__fmt, fmt); + while (len-- && __fmt[len] == '\n') + __fmt[len] = '\0'; + + while (len) { + if (__fmt[len] == '\n') + __fmt[len] = ' '; + --len; + } + + return __fmt; +} + +/* + * Output to stderr or syslog + */ +static void +voutput(int severity, const char *fmt, va_list ap) +{ + if (log_stdout) { + switch (severity) { + case LOG_ERR: + fprintf(stderr, "Error: "); + break; + case LOG_WARNING: + fprintf(stderr, "Warning: "); + break; + case LOG_DEBUG: + fprintf(stderr, " "); + break; + } + vfprintf(stderr, fmt, ap); + } else { + fmt = sanitize_format(fmt); + if (!fmt || !*fmt) + return; + vsyslog(severity, fmt, ap); + } +} + +void +isns_assert_failed(const char *condition, const char *file, unsigned int line) +{ + isns_error("Assertion failed (%s:%d): %s\n", + file, line, condition); + abort(); +} + +void +isns_fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (log_stdout) + fprintf(stderr, "** FATAL ERROR **\n"); + voutput(LOG_ERR, fmt, ap); + va_end(ap); + exit(1); +} + +void +isns_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + voutput(LOG_WARNING, fmt, ap); + va_end(ap); +} + +void +isns_warning(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + voutput(LOG_NOTICE, fmt, ap); + va_end(ap); +} + +void +isns_notice(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + voutput(LOG_INFO, fmt, ap); + va_end(ap); +} + +void +isns_enable_debugging(const char *what) +{ + char *copy, *s, *next; + + if (!strcmp(what, "all")) { + debugging = ~0U; + return; + } + + copy = isns_strdup(what); + + for (s = copy; s; s = next) { + if ((next = strchr(s, ',')) != NULL) + *next++ = '\0'; + + if (!strcmp(s, "general")) + debugging |= (1 << DBG_GENERAL); + else if (!strcmp(s, "socket")) + debugging |= (1 << DBG_SOCKET); + else if (!strcmp(s, "protocol")) + debugging |= (1 << DBG_PROTOCOL); + else if (!strcmp(s, "state")) + debugging |= (1 << DBG_STATE); + else if (!strcmp(s, "message")) + debugging |= (1 << DBG_MESSAGE); + else if (!strcmp(s, "auth")) + debugging |= (1 << DBG_AUTH); + else if (!strcmp(s, "scn")) + debugging |= (1 << DBG_SCN); + else if (!strcmp(s, "esi")) + debugging |= (1 << DBG_ESI); + else { + isns_error("Ignoring unknown isns_debug facility <<%s>>\n", + s); + } + } + isns_free(copy); +} + +#define DEFINE_DEBUG_FUNC(name, NAME) \ +void \ +isns_debug_##name(const char *fmt, ...) \ +{ \ + va_list ap; \ + \ + if (!(debugging & (1 << DBG_##NAME))) \ + return; \ + \ + va_start(ap, fmt); \ + voutput(LOG_DEBUG, fmt, ap); \ + va_end(ap); \ +} +DEFINE_DEBUG_FUNC(general, GENERAL) +DEFINE_DEBUG_FUNC(socket, SOCKET) +DEFINE_DEBUG_FUNC(protocol, PROTOCOL) +DEFINE_DEBUG_FUNC(message, MESSAGE) +DEFINE_DEBUG_FUNC(auth, AUTH) +DEFINE_DEBUG_FUNC(state, STATE) +DEFINE_DEBUG_FUNC(scn, SCN) +DEFINE_DEBUG_FUNC(esi, ESI) + +int +isns_debug_enabled(int fac) +{ + return (debugging & (1 << fac)) != 0; +} + +/* + * Misc isns_print_fn_t implementations + */ +void +isns_print_stdout(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +isns_print_stderr(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} diff --git a/utils/open-isns/mdebug.c b/utils/open-isns/mdebug.c new file mode 100644 index 0000000..90dcaf0 --- /dev/null +++ b/utils/open-isns/mdebug.c @@ -0,0 +1,295 @@ +/* + * Stupid malloc debugger. I think I wrote something like + * this a couple of times already. Where does all the old + * source code go? + */ + +#ifdef MDEBUG + +#include <stdlib.h> +#include <string.h> +#include "util.h" + +static void * isns_malloc_default(size_t, const char *, unsigned int); +static void * isns_calloc_default(unsigned int, size_t, + const char *, unsigned int); +static void * isns_realloc_default(void *, size_t, + const char *, unsigned int); +static char * isns_strdup_default(const char *, const char *, unsigned int); +static void isns_free_default(void *, const char *, unsigned int); + +/* + * These are the function pointers used to redirect malloc and such. + */ +void * (*isns_malloc_fn)(size_t, const char *, unsigned int) = isns_malloc_default; +void * (*isns_calloc_fn)(unsigned int, size_t, + const char *, unsigned int) = isns_calloc_default; +void * (*isns_realloc_fn)(void *, size_t, + const char *, unsigned int) = isns_realloc_default; +char * (*isns_strdup_fn)(const char *, const char *, unsigned int) = isns_strdup_default; +void (*isns_free_fn)(void *, const char *, unsigned int) = isns_free_default; + +#define H_MAGIC 0xfeedbeef +#define T_MAGIC 0xbadf00d +#define CHUNK_OVERHEAD (sizeof(struct m_header) + sizeof(struct m_trailer)) + +struct m_header { + struct isns_list h_list; + uint32_t h_magic; + size_t h_size; + + const char * h_file; + unsigned int h_line; +}; + +struct m_trailer { + uint32_t t_magic[8]; + size_t t_size; +}; + +static ISNS_LIST_DECLARE(m_list); +static void * m_low_addr; +static void * m_high_addr; +static int m_init = 0; + +static void +__isns_check_chunk(const struct m_header *head) +{ + const struct m_trailer *tail; + int i; + + if ((void *) head < m_low_addr + || (void *) head > m_high_addr) { + isns_error("%s: m_list corrupted!\n", __FUNCTION__); + abort(); + } + + if (head->h_magic != H_MAGIC) { + isns_error("%s: m_list item %p with bad header magic %08x\n", + __FUNCTION__, head, head->h_magic); + isns_error(" Allocated from %s:%u\n", + head->h_file, head->h_line); + abort(); + } + + tail = ((void *) head) + sizeof(*head) + head->h_size; + for (i = 0; i < 8; ++i) { + if (tail->t_magic[i] == T_MAGIC) + continue; + + isns_error("%s: m_list item %p with bad trailer magic[%d] %08x\n", + __FUNCTION__, head, i, tail->t_magic[i]); + isns_error(" Allocated from %s:%u\n", + head->h_file, head->h_line); + abort(); + } + + if (tail->t_size != head->h_size) { + isns_error("%s: m_list item %p size mismatch; head=%u tail=%u\n", + __FUNCTION__, head, + head->h_size, tail->t_size); + isns_error(" Allocated from %s:%u\n", + head->h_file, head->h_line); + abort(); + } +} + +static void +__isns_verify_all(void) +{ + struct isns_list *pos, *next; + + isns_list_foreach(&m_list, pos, next) { + __isns_check_chunk(isns_list_item(struct m_header, h_list, pos)); + } +} + +void * +__isns_malloc(size_t size, const char *file, unsigned int line) +{ + struct m_header *head; + struct m_trailer *tail; + size_t true_size; + void *ptr; + int i; + + __isns_verify_all(); + + true_size = size + sizeof(*head) + sizeof(*tail); + isns_assert(size < true_size); + + ptr = malloc(true_size); + if (!ptr) + return NULL; + + if (!m_low_addr) { + m_low_addr = m_high_addr = ptr; + } else if (ptr < m_low_addr) { + m_low_addr = ptr; + } else if (ptr > m_high_addr) { + m_high_addr = ptr; + } + + head = ptr; + head->h_magic = H_MAGIC; + head->h_size = size; + head->h_file = file; + head->h_line = line; + isns_list_append(&m_list, &head->h_list); + + ptr += sizeof(*head); + + tail = ptr + size; + for (i = 0; i < 8; ++i) + tail->t_magic[i] = T_MAGIC; + tail->t_size = size; + + return ptr; +} + +void * +__isns_calloc(unsigned int nele, size_t size, + const char *file, unsigned int line) +{ + void *ptr; + + ptr = __isns_malloc(nele * size, file, line); + if (ptr) + memset(ptr, 0, nele * size); + return ptr; +} + +void * +__isns_realloc(void *old, size_t new_size, + const char *file, unsigned int line) +{ + struct m_header *old_head = NULL; + void *new; + + if (old) { + old_head = (old - sizeof(struct m_header)); + __isns_check_chunk(old_head); + } + + new = __isns_malloc(new_size, file, line); + if (new && old) { + memcpy(new, old, old_head->h_size); + isns_free_fn(old, file, line); + } + + return new; +} + + +char * +__isns_strdup(const char *s, const char *file, unsigned int line) +{ + size_t len; + char *ptr; + + len = s? strlen(s) : 0; + ptr = __isns_malloc(len + 1, file, line); + if (ptr) { + memcpy(ptr, s, len); + ptr[len] = '\0'; + } + return ptr; +} + +void +__isns_free(void *ptr, const char *file, unsigned int line) +{ + struct m_header *head; + size_t true_size; + + if (ptr == NULL) + return; + + head = ptr - sizeof(struct m_header); + __isns_check_chunk(head); + + /* + printf("__isns_free(%u from %s:%u): freed by %s:%u\n", + head->h_size, head->h_file, head->h_line, + file, line); + */ + true_size = head->h_size + CHUNK_OVERHEAD; + isns_list_del(&head->h_list); + + memset(head, 0xa5, true_size); + free(head); + + __isns_verify_all(); +} + +/* + * Enable memory debugging + */ +static void +__isns_mdebug_init(void) +{ + const char *tracefile; + + tracefile = getenv("ISNS_MTRACE"); + if (tracefile) + isns_error("MTRACE not yet supported\n"); + + if (getenv("ISNS_MDEBUG")) { + isns_malloc_fn = __isns_malloc; + isns_calloc_fn = __isns_calloc; + isns_realloc_fn = __isns_realloc; + isns_strdup_fn = __isns_strdup; + isns_free_fn = __isns_free; + isns_notice("Enabled memory debugging\n"); + } + + m_init = 1; +} + +static inline void +isns_mdebug_init(void) +{ + if (!m_init) + __isns_mdebug_init(); +} + +/* + * Default implementations of malloc and friends + */ +static void * +isns_malloc_default(size_t size, const char *file, unsigned int line) +{ + isns_mdebug_init(); + return malloc(size); +} + +static void * +isns_calloc_default(unsigned int nele, size_t size, + const char *file, unsigned int line) +{ + isns_mdebug_init(); + return calloc(nele, size); +} + +static void * +isns_realloc_default(void *old, size_t size, + const char *file, unsigned int line) +{ + isns_mdebug_init(); + return realloc(old, size); +} + +static char * +isns_strdup_default(const char *s, const char *file, unsigned int line) +{ + isns_mdebug_init(); + return strdup(s); +} + +static void +isns_free_default(void *ptr, const char *file, unsigned int line) +{ + isns_mdebug_init(); + return free(ptr); +} +#endif diff --git a/utils/open-isns/message.c b/utils/open-isns/message.c new file mode 100644 index 0000000..4cd40c3 --- /dev/null +++ b/utils/open-isns/message.c @@ -0,0 +1,681 @@ +/* + * iSNS message handling functions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + * + * + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> /* for timercmp */ +#include <unistd.h> /* gethostname */ +#include <ctype.h> +#include "isns.h" +#include "attrs.h" +#include "message.h" +#include "socket.h" +#include "util.h" + +/* iSCSI qualified names include the year and + * month in which the domain was assigned. + * See RFC 3720, section 3.2.6.3.1. + * That's one of these wonderful committee + * type of ideas that makes it hard for everyone, + * from coder to sysadmin. + * Since we have no way of finding out here, + * we fake it by assigning a date before the + * dawn of time. + */ +#define DUMMY_IQN_PREFIX "iqn.1967-12." + +static uint32_t isns_xid = 1; + +/* + * Initialize a message object + */ +isns_message_t * +__isns_alloc_message(uint32_t xid, size_t size, void (*destroy)(isns_message_t *)) +{ + isns_message_t *msg; + + isns_assert(size >= sizeof(*msg)); + msg = isns_calloc(1, size); + + isns_list_init(&msg->im_list); + msg->im_users = 1; + msg->im_xid = xid; + msg->im_destroy = destroy; + + return msg; +} + +static int +__isns_message_init(isns_message_t *msg, + uint16_t function, uint16_t flags, + size_t payload_len) +{ + struct isns_hdr *hdr = &msg->im_header; + + /* Pad to multiple of 4 octets */ + payload_len = (payload_len + 3) & ~3UL; + + /* For now, we don't do segmentation */ + if (payload_len > ISNS_MAX_PDU_SIZE) + return 0; + + /* msg->im_header is in host byte order */ + hdr->i_version = ISNS_VERSION; + hdr->i_function = function; + hdr->i_flags = flags; + hdr->i_length = payload_len; + hdr->i_xid = msg->im_xid; + hdr->i_seq = 0; + + /* Allocate buffer and reserve room for header */ + msg->im_payload = buf_alloc(sizeof(*hdr) + payload_len); + buf_push(msg->im_payload, sizeof(*hdr)); + + return 1; +} + +/* + * Allocate a message object. + */ +static isns_message_t * +__isns_create_message(uint32_t xid, uint16_t function, uint16_t flags) +{ + isns_message_t *msg; + + msg = __isns_alloc_message(xid, sizeof(*msg), NULL); + __isns_message_init(msg, function, flags, ISNS_MAX_MESSAGE); + + return msg; +} + +/* + * Allocate a request message + */ +isns_message_t * +isns_create_message(uint16_t function, uint16_t flags) +{ + return __isns_create_message(isns_xid++, function, flags); +} + +/* + * Allocate a response message + */ +isns_message_t * +isns_create_reply(const isns_message_t *msg) +{ + uint16_t function = msg->im_header.i_function;; + isns_message_t *resp; + + resp = __isns_create_message(msg->im_xid, function | 0x8000, ISNS_F_SERVER); + resp->im_addr = msg->im_addr; + resp->im_addrlen = msg->im_addrlen; + + /* Default to ISNS_SUCCESS */ + buf_put32(resp->im_payload, ISNS_SUCCESS); + + return resp; +} + +/* + * Delete a message + */ +void +isns_message_release(isns_message_t *msg) +{ + if (msg == NULL) + return; + + isns_assert(msg->im_users); + if (--(msg->im_users)) + return; + + if (msg->im_destroy) + msg->im_destroy(msg); + if (msg->im_payload) + buf_free(msg->im_payload); + isns_principal_free(msg->im_security); + + isns_list_del(&msg->im_list); + isns_free(msg); +} + +/* + * Extract the status from a reply message + */ +int +isns_message_status(isns_message_t *msg) +{ + uint32_t status; + + if (!(msg->im_header.i_function & 0x8000) + || !buf_get32(msg->im_payload, &status)) + return ISNS_MESSAGE_FORMAT_ERROR; + return status; +} + +/* + * Obtain the socket on which the message was received. + */ +isns_socket_t * +isns_message_socket(const isns_message_t *msg) +{ + return msg->im_socket; +} + +/* + * Obtain the message's security context + */ +isns_security_t * +isns_message_security(const isns_message_t *msg) +{ + if (!msg->im_socket) + return NULL; + return msg->im_socket->is_security; +} + +unsigned int +isns_message_function(const isns_message_t *msg) +{ + return msg->im_header.i_function; +} + +/* + * Reset the response message, and encode isns_error + * status + */ +void +isns_message_set_error(isns_message_t *msg, uint32_t status) +{ + /* Clear the buffer. This just resets head + tail */ + buf_clear(msg->im_payload); + + /* Now move past the header, and overwrite the + * status word. */ + buf_push(msg->im_payload, sizeof(struct isns_hdr)); + buf_put32(msg->im_payload, status); +} + +/* + * Message queue handling. Most related functions are + * in message.h + */ +void +isns_message_queue_move(isns_message_queue_t *dstq, + isns_message_t *msg) +{ + unsigned int src_ref = 0; + + /* If the message was on a different queue, + * the source queue will hold a reference + * to it. Account for that and fix up the + * refcount after we've appended it to the + * destination queue. */ + if (isns_message_unlink(msg)) + src_ref = 1; + + isns_message_queue_append(dstq, msg); + msg->im_users -= src_ref; +} + +/* + * Insert a messsage into a queue sorted by resend timeout + */ +void +isns_message_queue_insert_sorted(isns_message_queue_t *q, + int sort, isns_message_t *msg) +{ + isns_list_t *pos; + isns_message_t *__m; + + isns_assert(msg->im_queue == NULL); + if (sort == ISNS_MQ_SORT_RESEND_TIMEOUT) { + isns_message_queue_foreach(q, pos, __m) { + if (timercmp(&msg->im_resend_timeout, + &__m->im_resend_timeout, <)) + break; + } + } else { + isns_message_queue_append(q, msg); + return; + } + + /* Insert before pos */ + __isns_list_insert(pos->prev, &msg->im_list, pos); + q->imq_count++; + + msg->im_queue = q; + msg->im_users++; +} + +/* + * Message queue handling + */ +void +isns_message_queue_destroy(isns_message_queue_t *q) +{ + isns_message_t *msg; + + while ((msg = isns_message_dequeue(q)) != NULL) + isns_message_release(msg); +} + +/* + * Find a message with matching xid and address. + * (address, alen) may be NULL. + */ +isns_message_t * +isns_message_queue_find(isns_message_queue_t *q, uint32_t xid, + const struct sockaddr_storage *addr, socklen_t alen) +{ + isns_message_t *msg; + isns_list_t *pos; + + isns_message_queue_foreach(q, pos, msg) { + if (msg->im_xid != xid) + continue; + if (alen == 0) + return msg; + + if (msg->im_addrlen == alen + && !memcmp(&msg->im_addr, addr, alen)) + return msg; + } + + return NULL; +} + +/* + * Convert a hostname into an iSCSI qualified name + * We omit the dismbiguating YYYY-MM infix because + * we have no way of finding out, short of bothering + * whois. + */ +static char * +__revert_fqdn(const char *prefix, const char *__fqdn, const char *suffix) +{ + static char namebuf[1024] = { '\0' }; + char *fqdn, *result = NULL; + int pos, count = 0; + + if (prefix) + strcpy(namebuf, prefix); + pos = strlen(namebuf); + + fqdn = isns_strdup(__fqdn); + while (1) { + char *dot, *comp; + int comp_len; + + if ((dot = strrchr(fqdn, '.')) != NULL) { + *dot++ = '\0'; + comp = dot; + } else { + comp = fqdn; + } + + if (*comp == '\0') + continue; + comp_len = strlen(comp); + if (pos + comp_len + 2 > sizeof(namebuf)) { + isns_error("%s: FQDN too long\n", __FUNCTION__); + goto out; + } + if (count++) + namebuf[pos++] = '.'; + strcpy(namebuf + pos, comp); + pos += comp_len; + + if (dot == NULL) + break; + } + + if (suffix) { + int sfx_len = strlen(suffix); + + if (pos + sfx_len + 2 > sizeof(namebuf)) { + isns_error("%s: name too long\n", __FUNCTION__); + goto out; + } + namebuf[pos++] = ':'; + strcpy(namebuf + pos, suffix); + pos += sfx_len; + } + + result = isns_strdup(namebuf); + +out: isns_free(fqdn); + return result; +} + +/* + * Initialize all names + */ +int +isns_init_names(void) +{ + if (isns_config.ic_host_name == NULL) { + char namebuf[1024], *fqdn; + + if (gethostname(namebuf, sizeof(namebuf)) < 0) { + isns_error("gehostname: %m\n"); + return 0; + } + fqdn = isns_get_canon_name(namebuf); + if (fqdn == NULL) { + /* FIXME: we could get some unique value here + * such as the IP address, and concat that + * with iqn.2005-01.org.open-iscsi.ip for the + * source name. + */ + isns_error("Unable to get fully qualified hostname\n"); + return 0; + } + isns_config.ic_host_name = fqdn; + } + + if (isns_config.ic_auth_name == NULL) { + isns_config.ic_auth_name = isns_config.ic_host_name; + } + + if (isns_config.ic_entity_name == NULL) { + isns_config.ic_entity_name = isns_config.ic_auth_name; + } + + if (isns_config.ic_source_name == NULL) { + isns_config.ic_source_name = __revert_fqdn(DUMMY_IQN_PREFIX, + isns_config.ic_host_name, + isns_config.ic_source_suffix); + if (isns_config.ic_source_name == NULL) { + isns_error("Unable to build source name\n"); + return 0; + } + } + + return 1; +} + +/* + * Match a source name to a pattern (which is really just + * the entity identifier, usually). + * + * If the pattern is of the form "match:rev-fqdn", the + * source name must match + * iqn.[YYYY-MM.]<rev-fqdn> + * optionally followed by dot, colon or hyphen and arbitrary + * text. + * + * If the pattern does not start with "match:", the source name + * must match the pattern literally (case insensitively). + */ +int +isns_source_pattern_match(const char *pattern, const char *source) +{ + unsigned int rev_len; + + isns_debug_message("%s(%s, %s)\n", + __FUNCTION__, pattern, source); + + if (!strcmp(pattern, "*")) + return 1; + + if (strncmp(pattern, "match:", 6)) + return !strcasecmp(pattern, source); + pattern += 6; + + if (strncasecmp(source, "iqn.", 4)) + return 0; + source += 4; + + rev_len = strlen(pattern); + if (strncasecmp(source, pattern, rev_len)) { + /* See if the next component is YYYY-MM */ + if (!(isdigit(source[0]) + && isdigit(source[1]) + && isdigit(source[2]) + && isdigit(source[3]) + && source[4] == '-' + && isdigit(source[5]) + && isdigit(source[6]) + && source[7] == '.')) + return 0; + source += 8; + + if (strncasecmp(source, pattern, rev_len)) + return 0; + } + + source += rev_len; + if (source[0] != '.' + && source[0] != ':' + && source[0] != '-' + && source[0] != '\0') + return 0; + + return 1; +} + +/* + * This really just reverts the FQDN so it can + * be used in isns_source_entity_match + */ +char * +isns_build_source_pattern(const char *fqdn) +{ + return __revert_fqdn("match:", fqdn, NULL); +} + +/* + * Manage source objects + */ +static isns_source_t * +__isns_source_create(isns_attr_t *name_attr) +{ + isns_source_t *source = isns_calloc(1, sizeof(*source)); + + source->is_users = 1; + source->is_attr = name_attr; + return source; +} + +isns_source_t * +isns_source_create(isns_attr_t *name_attr) +{ + if (name_attr->ia_tag_id != ISNS_TAG_ISCSI_NAME + && name_attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) + return NULL; + + name_attr->ia_users++; + return __isns_source_create(name_attr); +} + +isns_source_t * +isns_source_from_object(const isns_object_t *node) +{ + isns_attr_t *attr; + + if (!(attr = isns_storage_node_key_attr(node))) + return NULL; + return isns_source_create(attr); +} + +isns_source_t * +isns_source_create_iscsi(const char *name) +{ + isns_value_t var = ISNS_VALUE_INIT(string, (char *) name); + isns_attr_t *attr; + + attr = isns_attr_alloc(ISNS_TAG_ISCSI_NAME, NULL, &var); + return __isns_source_create(attr); +} + +/* + * This is used to attach a dummy source to iSNS responses + * until I fixed up all the code that relies on msg->is_source + * to be valid all the time. + */ +isns_source_t * +isns_source_dummy(void) +{ + static isns_source_t *dummy = NULL; + + if (!dummy) + dummy = isns_source_create_iscsi(".dummy."); + return isns_source_get(dummy); +} + +uint32_t +isns_source_type(const isns_source_t *source) +{ + return source->is_attr->ia_tag_id; +} + +const char * +isns_source_name(const isns_source_t *source) +{ + return source->is_attr->ia_value.iv_string; +} + +isns_attr_t * +isns_source_attr(const isns_source_t *source) +{ + return source->is_attr; +} + +/* + * Obtain an additional reference on the source object + */ +isns_source_t * +isns_source_get(isns_source_t *source) +{ + if (source) + source->is_users++; + return source; +} + +/* + * Look up the node corresponding to this source name + * When we get here, we have already verified that the + * client is permitted (by policy) to use this source node. + */ +int +isns_source_set_node(isns_source_t *source, isns_db_t *db) +{ + isns_object_t *node, *entity; + uint32_t node_type; + + if (source->is_node) + return 1; + + if (db == NULL) + return 0; + + node = isns_db_lookup_source_node(db, source); + if (node == NULL) + return 0; + + if (!isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type)) + node_type = 0; + + source->is_node = node; + source->is_node_type = node_type; + + if ((entity = isns_object_get_entity(node)) != NULL) + source->is_entity = isns_object_get(entity); + return 1; +} + +void +isns_source_set_entity(isns_source_t *source, isns_object_t *obj) +{ + if (obj) + isns_object_get(obj); + isns_object_release(source->is_entity); + source->is_entity = obj; +} + +/* + * Release a reference on the source object + */ +void +isns_source_release(isns_source_t *source) +{ + if (source && --source->is_users == 0) { + isns_attr_release(source->is_attr); + isns_object_release(source->is_node); + isns_object_release(source->is_entity); + memset(source, 0xa5, sizeof(*source)); + isns_free(source); + } +} + +/* + * Compare two source objects + */ +int +isns_source_match(const isns_source_t *a, + const isns_source_t *b) +{ + if (a && b) + return isns_attr_match(a->is_attr, b->is_attr); + return 0; +} + +/* + * Encode/decode source object + */ +int +isns_source_encode(buf_t *bp, const isns_source_t *source) +{ + if (source == NULL) { + isns_attr_t nil = ISNS_ATTR_INIT(ISNS_TAG_DELIMITER, nil, 0); + + return isns_attr_encode(bp, &nil); + } + return isns_attr_encode(bp, source->is_attr); +} + +int +isns_source_decode(buf_t *bp, isns_source_t **result) +{ + isns_attr_t *attr; + int status; + + status = isns_attr_decode(bp, &attr); + if (status == ISNS_SUCCESS) { + /* + * 5.6.1 + * The Source Attribute uniquely identifies the source of the + * message. Valid Source Attribute types are shown below. + * + * Valid Source Attributes + * ----------------------- + * iSCSI Name + * FC Port Name WWPN + */ + switch (attr->ia_tag_id) { +#if 0 + case ISNS_TAG_DELIMITER: + *result = NULL; + break; +#endif + + case ISNS_TAG_ISCSI_NAME: + *result = __isns_source_create(attr); + break; + + case ISNS_TAG_FC_PORT_NAME_WWPN: + *result = __isns_source_create(attr); + break; + + default: + isns_attr_release(attr); + return ISNS_SOURCE_UNKNOWN; + } + } + return status; +} diff --git a/utils/open-isns/message.h b/utils/open-isns/message.h new file mode 100644 index 0000000..f1f4ed6 --- /dev/null +++ b/utils/open-isns/message.h @@ -0,0 +1,196 @@ +/* + * iSNS message definitions and functions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_MESSAGE_H +#define ISNS_MESSAGE_H + +#include "attrs.h" +#include "source.h" +#include "util.h" + +typedef struct isns_message_queue isns_message_queue_t; + +struct isns_simple { + uint32_t is_function; + isns_source_t * is_source; + isns_policy_t * is_policy; + uint16_t is_xid; + + unsigned int is_replace : 1; + + isns_attr_list_t is_message_attrs; + isns_attr_list_t is_operating_attrs; +}; + +struct isns_message { + unsigned int im_users; + isns_list_t im_list; + struct sockaddr_storage im_addr; + socklen_t im_addrlen; + uint32_t im_xid; + struct isns_hdr im_header; + struct isns_buf * im_payload; + isns_socket_t * im_socket; + isns_principal_t * im_security; + struct ucred * im_creds; + + isns_message_queue_t * im_queue; + + /* When to retransmit */ + struct timeval im_resend_timeout; + struct timeval im_timeout; + + void (*im_destroy)(isns_message_t *); + void (*im_callback)(isns_message_t *, + isns_message_t *); + void * im_calldata; +}; + +enum { + ISNS_MQ_SORT_NONE, + ISNS_MQ_SORT_RESEND_TIMEOUT, +}; + +struct isns_message_queue { + isns_list_t imq_list; + size_t imq_count; +}; + +struct isns_server { + isns_source_t * is_source; + isns_db_t * is_db; + + isns_scn_callback_fn_t *is_scn_callback; + struct isns_service_ops *is_ops; +}; + +extern isns_message_t * __isns_alloc_message(uint32_t, size_t, void (*)(isns_message_t *)); +extern isns_security_t *isns_message_security(const isns_message_t *); + +extern isns_message_t * isns_message_queue_find(isns_message_queue_t *, uint32_t, + const struct sockaddr_storage *, socklen_t); +extern void isns_message_queue_insert_sorted(isns_message_queue_t *, + int, isns_message_t *); +extern void isns_message_queue_move(isns_message_queue_t *, + isns_message_t *); +extern void isns_message_queue_destroy(isns_message_queue_t *); + +extern isns_simple_t * isns_simple_create(uint32_t, + isns_source_t *, + const isns_attr_list_t *); +extern void isns_simple_free(isns_simple_t *); +extern int isns_simple_encode(isns_simple_t *, + isns_message_t **result); +extern int isns_simple_decode(isns_message_t *, + isns_simple_t **); +extern int isns_simple_encode_response(isns_simple_t *, + const isns_message_t *, isns_message_t **); +extern int isns_simple_response_get_objects(isns_simple_t *, + isns_object_list_t *); +extern const char * isns_function_name(uint32_t); + +extern isns_source_t * isns_simple_get_source(isns_simple_t *); + +extern int isns_process_registration(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_query(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_getnext(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_scn_register(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_scn_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_dd_registration(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_dd_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_esi(isns_server_t *, isns_simple_t *, isns_simple_t **); +extern int isns_process_scn(isns_server_t *, isns_simple_t *, isns_simple_t **); + +/* + * Inline functions for message queues. + */ +static inline void +isns_message_queue_init(isns_message_queue_t *q) +{ + isns_list_init(&q->imq_list); + q->imq_count = 0; +} + +static inline isns_message_t * +isns_message_queue_head(const isns_message_queue_t *q) +{ + isns_list_t *pos = q->imq_list.next; + + if (pos == &q->imq_list) + return NULL; + return isns_list_item(isns_message_t, im_list, pos); +} + +static inline void +isns_message_queue_append(isns_message_queue_t *q, isns_message_t *msg) +{ + isns_assert(msg->im_queue == NULL); + isns_list_append(&q->imq_list, &msg->im_list); + q->imq_count++; + + msg->im_queue = q; + msg->im_users++; +} + +static inline isns_message_t * +isns_message_queue_remove(isns_message_queue_t *q, isns_message_t *msg) +{ + isns_assert(msg->im_queue == q); + isns_list_del(&msg->im_list); + msg->im_queue = NULL; + q->imq_count--; + + return msg; +} + +static inline isns_message_t * +isns_message_unlink(isns_message_t *msg) +{ + if (msg->im_queue) + return isns_message_queue_remove(msg->im_queue, msg); + return NULL; +} + +static inline isns_message_t * +isns_message_dequeue(isns_message_queue_t *q) +{ + isns_message_t *msg; + + if ((msg = isns_message_queue_head(q)) != NULL) { + isns_list_del(&msg->im_list); + msg->im_queue = NULL; + q->imq_count--; + } + return msg; +} + +/* + * Iterator for looping over all messages in a queue + */ +static inline void +isns_message_queue_begin(isns_message_queue_t *q, isns_list_t **pos) +{ + *pos = q->imq_list.next; +} + +static inline isns_message_t * +isns_message_queue_next(isns_message_queue_t *q, isns_list_t **pos) +{ + isns_list_t *next = *pos; + + if (next == &q->imq_list) + return NULL; + *pos = next->next; + return isns_list_item(isns_message_t, im_list, next); +} + +#define isns_message_queue_foreach(q, pos, item) \ + for (isns_message_queue_begin(q, &pos); \ + (item = isns_message_queue_next(q, &pos)) != NULL; \ + ) + +#endif /* ISNS_MESSAGE_H */ diff --git a/utils/open-isns/objects.c b/utils/open-isns/objects.c new file mode 100644 index 0000000..1504026 --- /dev/null +++ b/utils/open-isns/objects.c @@ -0,0 +1,1320 @@ +/* + * iSNS object model + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "objects.h" +#include "source.h" +#include "vendor.h" +#include "attrs.h" +#include "util.h" + +/* For relationship stuff - should go */ +#include "db.h" + +static isns_object_template_t * isns_object_templates[] = { + &isns_entity_template, + &isns_portal_template, + &isns_iscsi_node_template, + &isns_fc_port_template, + &isns_fc_node_template, + &isns_iscsi_pg_template, + &isns_dd_template, + &isns_ddset_template, + + /* vendor-specific templates */ + &isns_policy_template, + + NULL +}; + +/* + * Quick lookup of (key) tag to template + */ +#define MAX_QUICK_TAG 2100 +static isns_object_template_t * isns_object_template_key_map[MAX_QUICK_TAG]; +static isns_object_template_t * isns_object_template_any_map[MAX_QUICK_TAG]; +static isns_object_template_t * isns_object_template_idx_map[MAX_QUICK_TAG]; +static int isns_object_maps_inizialized = 0; + + +static void +__isns_object_maps_init(void) +{ + isns_object_template_t *tmpl; + uint32_t i, j, tag; + + isns_object_maps_inizialized = 1; + + for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) { + if (tmpl->iot_vendor_specific) + continue; + + tag = tmpl->iot_keys[0]; + isns_assert(tag < MAX_QUICK_TAG); + isns_object_template_key_map[tag] = tmpl; + + for (j = 0; j < tmpl->iot_num_attrs; ++j) { + tag = tmpl->iot_attrs[j]; + isns_assert(tag < MAX_QUICK_TAG); + isns_object_template_any_map[tag] = tmpl; + } + + if ((tag = tmpl->iot_index) != 0) + isns_object_template_idx_map[tag] = tmpl; + } +} + +static void +isns_object_maps_init(void) +{ + if (!isns_object_maps_inizialized) + __isns_object_maps_init(); +} + +/* + * Based on a given key attribute, find the corresponding + * object type. + */ +isns_object_template_t * +isns_object_template_find(uint32_t key_tag) +{ + isns_object_template_t *tmpl; + unsigned int i; + + isns_object_maps_init(); + if (key_tag < MAX_QUICK_TAG) + return isns_object_template_key_map[key_tag]; + + for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) { + if (tmpl->iot_keys[0] == key_tag) + return tmpl; + } + + return NULL; +} + +/* + * Given a set of attributes, find the corresponding + * object type. + * Any attributes in the list in *addition to* the keys + * attributes are ignored. + */ +isns_object_template_t * +isns_object_template_for_key_attrs(const isns_attr_list_t *attrs) +{ + isns_object_template_t *tmpl; + const isns_attr_t *attr; + unsigned int i; + + if (attrs->ial_count == 0) + return NULL; + attr = attrs->ial_data[0]; + + tmpl = isns_object_template_find(attr->ia_tag_id); + if (tmpl == NULL) + return NULL; + + /* + * 5.6.4. + * + * Some objects are keyed by more than one object key attribute + * value. For example, the Portal object is keyed by attribute + * tags 16 and 17. When describing an object keyed by more than one + * key attribute, every object key attribute of that object MUST be + * listed sequentially by tag value in the message before non-key + * attributes of that object and key attributes of the next object. + * A group of key attributes of this kind is treated as a single + * logical key attribute when identifying an object. + */ + for (i = 1; i < tmpl->iot_num_keys; ++i) { + attr = attrs->ial_data[i]; + + if (attr->ia_tag_id != tmpl->iot_keys[i]) + return NULL; + } + + return tmpl; +} + +isns_object_template_t * +isns_object_template_for_tag(uint32_t tag) +{ + isns_object_template_t *tmpl; + unsigned int i, j; + + isns_object_maps_init(); + if (tag < MAX_QUICK_TAG) + return isns_object_template_any_map[tag]; + + for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) { + for (j = 0; j < tmpl->iot_num_attrs; ++j) { + if (tmpl->iot_attrs[j] == tag) + return tmpl; + } + } + + return NULL; +} + +isns_object_template_t * +isns_object_template_for_index_tag(uint32_t tag) +{ + isns_object_maps_init(); + if (tag >= MAX_QUICK_TAG) + return NULL; + + return isns_object_template_idx_map[tag]; +} + +isns_object_template_t * +isns_object_template_by_name(const char *name) +{ + isns_object_template_t **pp, *tmpl; + + pp = isns_object_templates; + while ((tmpl = *pp++) != NULL) { + if (!strcasecmp(tmpl->iot_name, name)) + return tmpl; + } + return NULL; +} + +const char * +isns_object_template_name(isns_object_template_t *tmpl) +{ + if (!tmpl) + return NULL; + return tmpl->iot_name; +} + +/* + * Notify any listeners that the object has changed, + * and mark it dirty. + * dd_or_dds is used for DD_MEMBER_ADDED and + * DD_MEMBER_REMOVED events, and refers to the + * domain or domain set the object was added to or + * removed from. + */ +void +isns_mark_object(isns_object_t *obj, unsigned int how) +{ + obj->ie_flags |= ISNS_OBJECT_DIRTY; + obj->ie_mtime = time(NULL); + obj->ie_scn_bits |= (1 << how); + isns_object_event(obj, 0, NULL); +} + +static void +__isns_mark_object(isns_object_t *obj) +{ + obj->ie_flags |= ISNS_OBJECT_DIRTY; + obj->ie_mtime = time(NULL); +} + +/* + * Create an object given its object template + */ +isns_object_t * +isns_create_object(isns_object_template_t *tmpl, + const isns_attr_list_t *attrs, + isns_object_t *parent) +{ + isns_object_t *obj; + unsigned int i; + + /* Enforce containment rules. */ + if (parent) + isns_assert(tmpl->iot_container == parent->ie_template); + +#ifdef notdef + /* This check is somewhat costly: */ + if (attrs && tmpl != isns_object_template_for_key_attrs(attrs)) + return NULL; +#endif + + obj = isns_calloc(1, sizeof(*obj)); + + obj->ie_users = 1; + obj->ie_template = tmpl; + isns_attr_list_init(&obj->ie_attrs); + + if (parent) + isns_object_attach(obj, parent); + + if (attrs == NULL) { + /* Make sure that all key attrs are instantiated + * and in sequence. */ + for (i = 0; i < tmpl->iot_num_keys; ++i) + isns_attr_list_append_nil(&obj->ie_attrs, + tmpl->iot_keys[i]); + } else { + /* We rely on the caller to ensure that + * attributes are in proper sequence. */ + isns_attr_list_copy(&obj->ie_attrs, attrs); + } + + /* Just mark it dirty, but do not schedule a + * SCN event. */ + __isns_mark_object(obj); + + return obj; +} + +/* + * Obtain an additional reference on the object + */ +isns_object_t * +isns_object_get(isns_object_t *obj) +{ + if (obj) { + isns_assert(obj->ie_users); + obj->ie_users++; + } + return obj; +} + +/* + * Release a reference on the object + */ +void +isns_object_release(isns_object_t *obj) +{ + unsigned int i; + isns_object_t *child; + + if (!obj) + return; + + isns_assert(obj->ie_users); + if (--(obj)->ie_users != 0) + return; + + /* Must not have any live references to it */ + isns_assert(obj->ie_references == 0); + + /* Must be detached from parent */ + isns_assert(obj->ie_container == NULL); + + /* Release all children. We explicitly clear + * ie_container because the destructor + * checks for this (in order to catch + * refcounting bugs) */ + for (i = 0; i < obj->ie_children.iol_count; ++i) { + child = obj->ie_children.iol_data[i]; + child->ie_container = NULL; + } + isns_object_list_destroy(&obj->ie_children); + + isns_attr_list_destroy(&obj->ie_attrs); + + isns_bitvector_free(obj->ie_membership); + isns_free(obj); +} + +/* + * Get the topmost container (ie Network Entity) + * for the given object + */ +isns_object_t * +isns_object_get_entity(isns_object_t *obj) +{ + if (obj == NULL) + return NULL; + while (obj->ie_container) + obj = obj->ie_container; + if (!ISNS_IS_ENTITY(obj)) + return NULL; + return obj; +} + +int +isns_object_contains(const isns_object_t *ancestor, + const isns_object_t *descendant) +{ + while (descendant) { + if (descendant == ancestor) + return 1; + descendant = descendant->ie_container; + } + return 0; +} + +/* + * Get all children of the specified type + */ +void +isns_object_get_descendants(const isns_object_t *obj, + isns_object_template_t *tmpl, + isns_object_list_t *result) +{ + isns_object_t *child; + unsigned int i; + + for (i = 0; i < obj->ie_children.iol_count; ++i) { + child = obj->ie_children.iol_data[i]; + if (!tmpl || child->ie_template == tmpl) + isns_object_list_append(result, child); + } +} + +/* + * Attach an object to a new container + */ +int +isns_object_attach(isns_object_t *obj, isns_object_t *parent) +{ + isns_assert(obj->ie_container == NULL); + + if (parent) { + /* Copy the owner (ie source) from the parent + * object. + * Make sure the parent object type is a valid + * container for this object. + */ + if (parent->ie_template != obj->ie_template->iot_container) { + isns_error("You are not allowed to add a %s object " + "to a %s!\n", + obj->ie_template->iot_name, + parent->ie_template->iot_name); + return 0; + } + obj->ie_flags = parent->ie_flags & ISNS_OBJECT_PRIVATE; + isns_object_list_append(&parent->ie_children, obj); + } + obj->ie_container = parent; + return 1; +} + +int +isns_object_is_valid_container(const isns_object_t *container, + isns_object_template_t *child_type) +{ + return child_type->iot_container == container->ie_template; +} + +/* + * Detach an object from its container + */ +int +isns_object_detach(isns_object_t *obj) +{ + isns_object_t *parent; + + /* Detach from parent */ + if ((parent = obj->ie_container) != NULL) { + int removed; + + obj->ie_container = NULL; + removed = isns_object_list_remove( + &parent->ie_children, obj); + + isns_assert(removed != 0); + } + + return 0; +} + +/* + * Check the type of an object + */ +int +isns_object_is(const isns_object_t *obj, + isns_object_template_t *tmpl) +{ + return obj->ie_template == tmpl; +} + +int +isns_object_is_iscsi_node(const isns_object_t *obj) +{ + return ISNS_IS_ISCSI_NODE(obj); +} + +int +isns_object_is_fc_port(const isns_object_t *obj) +{ + return ISNS_IS_FC_PORT(obj); +} + +int +isns_object_is_fc_node(const isns_object_t *obj) +{ + return ISNS_IS_FC_NODE(obj); +} + +int +isns_object_is_portal(const isns_object_t *obj) +{ + return ISNS_IS_PORTAL(obj); +} + +int +isns_object_is_pg(const isns_object_t *obj) +{ + return ISNS_IS_PG(obj); +} + +int +isns_object_is_policy(const isns_object_t *obj) +{ + return ISNS_IS_POLICY(obj); +} + +/* + * Match an object against a list of attributes. + */ +int +isns_object_match(const isns_object_t *obj, + const isns_attr_list_t *attrs) +{ + isns_object_template_t *tmpl = obj->ie_template; + isns_attr_t *self, *match; + unsigned int i, j, from = 0; + uint32_t tag; + + /* Fast path: try to compare in-order */ + while (from < attrs->ial_count) { + match = attrs->ial_data[from]; + self = obj->ie_attrs.ial_data[from]; + + if (match->ia_tag_id != self->ia_tag_id) + goto slow_path; + + if (!isns_attr_match(self, match)) + return 0; + + from++; + } + + return 1; + +slow_path: + for (i = from; i < attrs->ial_count; ++i) { + isns_attr_t *found = NULL; + + match = attrs->ial_data[i]; + + /* + * 5.6.5.2 + * A Message Key with zero-length TLV(s) is scoped to + * every object of the type indicated by the zero-length + * TLV(s) + */ + if (match->ia_value.iv_type == &isns_attr_type_nil) { + tag = match->ia_tag_id; + if (isns_object_attr_valid(tmpl, tag)) + continue; + return 0; + } + + for (j = from; j < obj->ie_attrs.ial_count; ++j) { + self = obj->ie_attrs.ial_data[j]; + + if (match->ia_tag_id == self->ia_tag_id) { + found = self; + break; + } + } + + if (found == NULL) + return 0; + + if (!isns_attr_match(self, match)) + return 0; + } + return 1; +} + +/* + * Find descendant object matching the given key + */ +isns_object_t * +isns_object_find_descendant(isns_object_t *obj, const isns_attr_list_t *keys) +{ + isns_object_list_t list = ISNS_OBJECT_LIST_INIT; + isns_object_t *found; + + if (!isns_object_find_descendants(obj, NULL, keys, &list)) + return NULL; + + found = isns_object_get(list.iol_data[0]); + isns_object_list_destroy(&list); + + return found; +} + +int +isns_object_find_descendants(isns_object_t *obj, + isns_object_template_t *tmpl, + const isns_attr_list_t *keys, + isns_object_list_t *result) +{ + isns_object_t *child; + unsigned int i; + + if ((tmpl == NULL || tmpl == obj->ie_template) + && (keys == NULL || isns_object_match(obj, keys))) + isns_object_list_append(result, obj); + + for (i = 0; i < obj->ie_children.iol_count; ++i) { + child = obj->ie_children.iol_data[i]; + isns_object_find_descendants(child, tmpl, keys, result); + } + + return result->iol_count; +} + +/* + * Return the object's modification time stamp + */ +time_t +isns_object_last_modified(const isns_object_t *obj) +{ + return obj->ie_mtime; +} + +/* + * Set the SCN bitmap + */ +void +isns_object_set_scn_mask(isns_object_t *obj, uint32_t bitmap) +{ + obj->ie_scn_mask = bitmap; + __isns_mark_object(obj); +} + +/* + * Debugging utility: print the object + */ +void +isns_object_print(isns_object_t *obj, isns_print_fn_t *fn) +{ + isns_attr_list_print(&obj->ie_attrs, fn); +} + +/* + * Return a string representing the object state + */ +const char * +isns_object_state_string(unsigned int state) +{ + switch (state) { + case ISNS_OBJECT_STATE_LARVAL: + return "larval"; + case ISNS_OBJECT_STATE_MATURE: + return "mature"; + case ISNS_OBJECT_STATE_LIMBO: + return "limbo"; + case ISNS_OBJECT_STATE_DEAD: + return "dead"; + } + return "UNKNOWN"; +} + +/* + * This is needed when deregistering an object. + * Remove all attributes except the key and index attrs. + */ +void +isns_object_prune_attrs(isns_object_t *obj) +{ + isns_object_template_t *tmpl = obj->ie_template; + uint32_t tags[16]; + unsigned int i; + + isns_assert(tmpl->iot_num_keys + 1 <= 16); + for (i = 0; i < tmpl->iot_num_keys; ++i) + tags[i] = tmpl->iot_keys[i]; + if (tmpl->iot_index) + tags[i++] = tmpl->iot_index; + isns_attr_list_prune(&obj->ie_attrs, tags, i); +} + +/* + * Convenience functions + */ + +/* + * Create a portal object. + * For now, always assume TCP. + */ +isns_object_t * +isns_create_portal(const isns_portal_info_t *info, + isns_object_t *parent) +{ + isns_object_t *obj; + + obj = isns_create_object(&isns_portal_template, NULL, parent); + isns_portal_to_object(info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + obj); + return obj; +} + +/* + * Extract all key attrs and place them + * in the attribute list. + */ +int +isns_object_extract_keys(const isns_object_t *obj, + isns_attr_list_t *list) +{ + isns_object_template_t *tmpl = obj->ie_template; + const isns_attr_list_t *src = &obj->ie_attrs; + unsigned int i; + + for (i = 0; i < tmpl->iot_num_keys; ++i) { + isns_attr_t *attr; + + if (!isns_attr_list_get_attr(src, tmpl->iot_keys[i], &attr)) + return 0; + isns_attr_list_append_attr(list, attr); + } + + return 1; +} + +/* + * Extract all attributes we are permitted to overwrite and place them + * in the attribute list. + */ +int +isns_object_extract_writable(const isns_object_t *obj, + isns_attr_list_t *list) +{ + const isns_attr_list_t *src = &obj->ie_attrs; + unsigned int i; + + for (i = 0; i < src->ial_count; ++i) { + isns_attr_t *attr = src->ial_data[i]; + + if (attr->ia_tag->it_readonly) + continue; + isns_attr_list_append_attr(list, attr); + } + + return 1; +} + +/* + * Extract all attrs and place them + * in the attribute list. We copy the attributes + * as they appear inside the object; which allows + * duplicate attributes (eg inside a discovery domain). + */ +int +isns_object_extract_all(const isns_object_t *obj, isns_attr_list_t *list) +{ + isns_attr_list_append_list(list, &obj->ie_attrs); + return 1; +} + +/* + * Check if the given object is valid + */ +int +isns_object_attr_valid(isns_object_template_t *tmpl, uint32_t tag) +{ + const uint32_t *attr_tags = tmpl->iot_attrs; + unsigned int i; + + for (i = 0; i < tmpl->iot_num_attrs; ++i) { + if (*attr_tags == tag) + return 1; + ++attr_tags; + } + return 0; +} + +/* + * Set an object attribute + */ +static int +__isns_object_set_attr(isns_object_t *obj, uint32_t tag, + const isns_attr_type_t *type, + const isns_value_t *value) +{ + const isns_tag_type_t *tag_type; + + if (!isns_object_attr_valid(obj->ie_template, tag)) + return 0; + + tag_type = isns_tag_type_by_id(tag); + if (type != &isns_attr_type_nil + && type != tag_type->it_type) { + isns_warning("application bug: cannot set attr %s(id=%u, " + "type=%s) to a value of type %s\n", + tag_type->it_name, tag, + tag_type->it_type->it_name, + type->it_name); + return 0; + } + + isns_attr_list_update_value(&obj->ie_attrs, + tag, tag_type, value); + + /* Timestamp updates should just be written out, but we + * do not want to trigger SCN messages and such. */ + if (tag != ISNS_TAG_TIMESTAMP) + isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED); + else + __isns_mark_object(obj); + return 1; +} + +/* + * Copy an attribute to the object + */ +int +isns_object_set_attr(isns_object_t *obj, isns_attr_t *attr) +{ + isns_attr_list_t *list = &obj->ie_attrs; + uint32_t tag = attr->ia_tag_id; + + /* If this attribute exists within the object, + * and it cannot occur multiple times, replace it. */ + if (!attr->ia_tag->it_multiple + && isns_attr_list_replace_attr(list, attr)) + goto done; + + /* It doesn't exist; make sure it's a valid + * attribute. */ + if (!isns_object_attr_valid(obj->ie_template, tag)) + return 0; + + isns_attr_list_append_attr(list, attr); + +done: + isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED); + return 1; +} + +int +isns_object_set_attrlist(isns_object_t *obj, const isns_attr_list_t *attrs) +{ + unsigned int i; + + for (i = 0; i < attrs->ial_count; ++i) { + isns_attr_t *attr = attrs->ial_data[i]; + if (!isns_object_set_attr(obj, attr)) + return 0; + } + isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED); + return 1; +} + +/* + * Untyped version of isns_object_set. + * Any type checking must be done by the caller; + * failure to do so will result in the end of the world. + */ +int +isns_object_set_value(isns_object_t *obj, uint32_t tag, const void *data) +{ + return isns_attr_list_update(&obj->ie_attrs, tag, data); +} + +/* + * Typed versions of isns_object_set + */ +int +isns_object_set_nil(isns_object_t *obj, uint32_t tag) +{ + return __isns_object_set_attr(obj, tag, + &isns_attr_type_nil, NULL); +} + +int +isns_object_set_string(isns_object_t *obj, uint32_t tag, + const char *value) +{ + isns_value_t var = ISNS_VALUE_INIT(string, (char *) value); + int rc; + + rc = __isns_object_set_attr(obj, tag, + &isns_attr_type_string, &var); + return rc; +} + +int +isns_object_set_uint32(isns_object_t *obj, uint32_t tag, + uint32_t value) +{ + isns_value_t var = ISNS_VALUE_INIT(uint32, value); + + return __isns_object_set_attr(obj, tag, + &isns_attr_type_uint32, &var); +} + +int +isns_object_set_uint64(isns_object_t *obj, + uint32_t tag, + uint64_t value) +{ + isns_value_t var = ISNS_VALUE_INIT(uint64, value); + + return __isns_object_set_attr(obj, tag, + &isns_attr_type_uint64, &var); +} + +int +isns_object_set_ipaddr(isns_object_t *obj, uint32_t tag, + const struct in6_addr *value) +{ + isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value); + + return __isns_object_set_attr(obj, tag, + &isns_attr_type_ipaddr, &var); +} + +/* + * Query object attributes + */ +int +isns_object_get_attr(const isns_object_t *obj, uint32_t tag, + isns_attr_t **result) +{ + return isns_attr_list_get_attr(&obj->ie_attrs, tag, result); +} + +int +isns_object_get_attrlist(isns_object_t *obj, + isns_attr_list_t *result, + const isns_attr_list_t *req_attrs) +{ + isns_attr_list_t *attrs = &obj->ie_attrs; + isns_attr_t *attr; + unsigned int i; + + if (req_attrs == NULL) { + /* Retrieve all attributes */ + isns_attr_list_append_list(result, attrs); + } else { + for (i = 0; i < req_attrs->ial_count; ++i) { + uint32_t tag = req_attrs->ial_data[i]->ia_tag_id; + + if (tag == obj->ie_template->iot_next_index) { + /* FIXME: for now, we fake this value. + * We need the DB object at this point + * to find out what the next unused + * index is. + */ + isns_attr_list_append_uint32(result, + tag, 0); + } else + if (isns_attr_list_get_attr(attrs, tag, &attr)) + isns_attr_list_append_attr(result, attr); + } + } + return 1; +} + +int +isns_object_get_key_attrs(isns_object_t *obj, + isns_attr_list_t *result) +{ + isns_object_template_t *tmpl = obj->ie_template; + isns_attr_list_t *attrs = &obj->ie_attrs; + isns_attr_t *attr; + unsigned int i; + + for (i = 0; i < tmpl->iot_num_keys; ++i) { + uint32_t tag = tmpl->iot_keys[i]; + + if (!isns_attr_list_get_attr(attrs, tag, &attr)) { + isns_error("%s: %s object is missing key attr %u\n", + __FUNCTION__, + tmpl->iot_name, + tag); + return 0; + } + isns_attr_list_append_attr(result, attr); + } + return 1; +} + +int +isns_object_get_string(const isns_object_t *obj, uint32_t tag, + const char **result) +{ + isns_attr_t *attr; + + if (!isns_object_get_attr(obj, tag, &attr) + || !ISNS_ATTR_IS_STRING(attr)) + return 0; + + *result = attr->ia_value.iv_string; + return 1; +} + +int +isns_object_get_ipaddr(const isns_object_t *obj, uint32_t tag, + struct in6_addr *result) +{ + isns_attr_t *attr; + + if (!isns_object_get_attr(obj, tag, &attr) + || !ISNS_ATTR_IS_IPADDR(attr)) + return 0; + + *result = attr->ia_value.iv_ipaddr; + return 1; +} + +int +isns_object_get_uint32(const isns_object_t *obj, uint32_t tag, + uint32_t *result) +{ + isns_attr_t *attr; + + if (!isns_object_get_attr(obj, tag, &attr) + || !ISNS_ATTR_IS_UINT32(attr)) + return 0; + + *result = attr->ia_value.iv_uint32; + return 1; +} + +int +isns_object_get_uint64(const isns_object_t *obj, uint32_t tag, + uint64_t *result) +{ + isns_attr_t *attr; + + if (!isns_object_get_attr(obj, tag, &attr) + || !ISNS_ATTR_IS_UINT64(attr)) + return 0; + + *result = attr->ia_value.iv_uint64; + return 1; +} + +int +isns_object_get_opaque(const isns_object_t *obj, uint32_t tag, + const void **ptr, size_t *len) +{ + isns_attr_t *attr; + + if (!isns_object_get_attr(obj, tag, &attr) + || !ISNS_ATTR_IS_OPAQUE(attr)) + return 0; + + *ptr = attr->ia_value.iv_opaque.ptr; + *len = attr->ia_value.iv_opaque.len; + return 1; +} + +int +isns_object_delete_attr(isns_object_t *obj, uint32_t tag) +{ + return isns_attr_list_remove_tag(&obj->ie_attrs, tag); +} + +int +isns_object_remove_member(isns_object_t *obj, + const isns_attr_t *attr, + const uint32_t *subordinate_tags) +{ + return isns_attr_list_remove_member(&obj->ie_attrs, + attr, subordinate_tags); +} + +/* + * Object list functions + */ +void +isns_object_list_init(isns_object_list_t *list) +{ + memset(list, 0, sizeof(*list)); +} + +static inline void +__isns_object_list_resize(isns_object_list_t *list, unsigned int count) +{ + unsigned int max; + + max = (list->iol_count + 15) & ~15; + if (count < max) + return; + + count = (count + 15) & ~15; + list->iol_data = isns_realloc(list->iol_data, count * sizeof(isns_object_t *)); + if (!list->iol_data) + isns_fatal("Out of memory!\n"); +} + +void +isns_object_list_append(isns_object_list_t *list, isns_object_t *obj) +{ + __isns_object_list_resize(list, list->iol_count + 1); + list->iol_data[list->iol_count++] = obj; + obj->ie_users++; +} + +void +isns_object_list_append_list(isns_object_list_t *dst, + const isns_object_list_t *src) +{ + unsigned int i, j; + + __isns_object_list_resize(dst, dst->iol_count + src->iol_count); + j = dst->iol_count; + for (i = 0; i < src->iol_count; ++i, ++j) { + isns_object_t *obj = src->iol_data[i]; + + dst->iol_data[j] = obj; + obj->ie_users++; + } + dst->iol_count = j; +} + +int +isns_object_list_contains(const isns_object_list_t *list, + isns_object_t *obj) +{ + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) { + if (obj == list->iol_data[i]) + return 1; + } + return 0; +} + +isns_object_t * +isns_object_list_lookup(const isns_object_list_t *list, + isns_object_template_t *tmpl, + const isns_attr_list_t *keys) +{ + unsigned int i; + + if (!tmpl && !keys) + return NULL; + + if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys))) + return NULL; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj->ie_template != tmpl) + continue; + if (keys && !isns_object_match(obj, keys)) + continue; + + obj->ie_users++; + return obj; + } + + return NULL; +} + + +int +isns_object_list_gang_lookup(const isns_object_list_t *list, + isns_object_template_t *tmpl, + const isns_attr_list_t *keys, + isns_object_list_t *result) +{ + unsigned int i; + + if (!tmpl && !keys) + return ISNS_INVALID_QUERY; + + if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys))) + return ISNS_INVALID_QUERY; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj->ie_template != tmpl) + continue; + if (keys && !isns_object_match(obj, keys)) + continue; + + isns_object_list_append(result, obj); + } + + return ISNS_SUCCESS; +} + + +void +isns_object_list_destroy(isns_object_list_t *list) +{ + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + isns_object_release(obj); + } + + isns_free(list->iol_data); + memset(list, 0, sizeof(*list)); +} + +int +isns_object_list_remove(isns_object_list_t *list, isns_object_t *tbr) +{ + unsigned int i, last; + + last = list->iol_count - 1; + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (obj == tbr) { + list->iol_data[i] = list->iol_data[last]; + list->iol_count--; + isns_object_release(tbr); + return 1; + } + } + return 0; +} + +static int +isns_object_compare_id(const void *pa, const void *pb) +{ + const isns_object_t *a = *(const isns_object_t **) pa; + const isns_object_t *b = *(const isns_object_t **) pb; + + return (int) a->ie_index - (int) b->ie_index; +} + +void +isns_object_list_sort(isns_object_list_t *list) +{ + if (list->iol_count == 0) + return; + + qsort(list->iol_data, list->iol_count, + sizeof(void *), isns_object_compare_id); +} + +void +isns_object_list_uniq(isns_object_list_t *list) +{ + isns_object_t *prev = NULL, *this; + unsigned int i, j; + + isns_object_list_sort(list); + for (i = j = 0; i < list->iol_count; i++) { + this = list->iol_data[i]; + if (this != prev) + list->iol_data[j++] = this; + prev = this; + } + list->iol_count = j; +} + +void +isns_object_list_print(const isns_object_list_t *list, isns_print_fn_t *fn) +{ + unsigned int i; + + if (list->iol_count == 0) { + fn("(Object list empty)\n"); + return; + } + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj; + + obj = list->iol_data[i]; + fn("object[%u] = <%s>\n", i, + obj->ie_template->iot_name); + isns_object_print(obj, fn); + } +} + +/* + * Handle object references + */ +void +isns_object_reference_set(isns_object_ref_t *ref, isns_object_t *obj) +{ + isns_object_t *old; + + if (obj) { + isns_assert(obj->ie_users); + obj->ie_references++; + obj->ie_users++; + } + if ((old = ref->obj) != NULL) { + isns_assert(old->ie_references); + old->ie_references--; + isns_object_release(old); + } + ref->obj = obj; +} + +void +isns_object_reference_drop(isns_object_ref_t *ref) +{ + isns_object_reference_set(ref, NULL); +} + +/* + * Helper function for portal/object conversion + */ +int +isns_portal_from_object(isns_portal_info_t *portal, + uint32_t addr_tag, uint32_t port_tag, + const isns_object_t *obj) +{ + return isns_portal_from_attr_list(portal, + addr_tag, port_tag, &obj->ie_attrs); +} + +int +isns_portal_to_object(const isns_portal_info_t *portal, + uint32_t addr_tag, uint32_t port_tag, + isns_object_t *obj) +{ + return isns_portal_to_attr_list(portal, + addr_tag, port_tag, + &obj->ie_attrs); +} + +/* + * Portal + */ +static uint32_t portal_attrs[] = { + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + ISNS_TAG_PORTAL_SYMBOLIC_NAME, + ISNS_TAG_ESI_INTERVAL, + ISNS_TAG_ESI_PORT, + ISNS_TAG_PORTAL_INDEX, + ISNS_TAG_SCN_PORT, + ISNS_TAG_PORTAL_NEXT_INDEX, + ISNS_TAG_PORTAL_SECURITY_BITMAP, + ISNS_TAG_PORTAL_ISAKMP_PHASE_1, + ISNS_TAG_PORTAL_ISAKMP_PHASE_2, + ISNS_TAG_PORTAL_CERTIFICATE, +}; + +static uint32_t portal_key_attrs[] = { + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, +}; + +isns_object_template_t isns_portal_template = { + .iot_name = "Portal", + .iot_handle = ISNS_OBJECT_TYPE_PORTAL, + .iot_attrs = portal_attrs, + .iot_num_attrs = array_num_elements(portal_attrs), + .iot_keys = portal_key_attrs, + .iot_num_keys = array_num_elements(portal_key_attrs), + .iot_index = ISNS_TAG_PORTAL_INDEX, + .iot_next_index = ISNS_TAG_PORTAL_NEXT_INDEX, + .iot_container = &isns_entity_template, +}; diff --git a/utils/open-isns/objects.h b/utils/open-isns/objects.h new file mode 100644 index 0000000..93ce208 --- /dev/null +++ b/utils/open-isns/objects.h @@ -0,0 +1,167 @@ +/* + * iSNS object model + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_OBJECTS_H +#define ISNS_OBJECTS_H + +#include "isns.h" +#include "attrs.h" + +enum isns_object_id { + ISNS_OBJECT_TYPE_ENTITY = 1, + ISNS_OBJECT_TYPE_NODE, + ISNS_OBJECT_TYPE_PORTAL, + ISNS_OBJECT_TYPE_PG, + ISNS_OBJECT_TYPE_DD, + ISNS_OBJECT_TYPE_DDSET, + ISNS_OBJECT_TYPE_POLICY, + ISNS_OBJECT_TYPE_FC_PORT, + ISNS_OBJECT_TYPE_FC_NODE, + + __ISNS_OBJECT_TYPE_MAX +}; + + +struct isns_object_template { + const char * iot_name; + unsigned int iot_handle; /* internal handle */ + unsigned int iot_num_attrs; + unsigned int iot_num_keys; + uint32_t * iot_attrs; + uint32_t * iot_keys; + uint32_t iot_index; + uint32_t iot_next_index; + + isns_object_template_t *iot_container; + + unsigned int iot_relation_type; + isns_relation_t * (*iot_build_relation)(isns_db_t *, + isns_object_t *, + const isns_object_list_t *); + + unsigned int iot_vendor_specific : 1; +}; + +struct isns_object { + /* There are two kinds of users of an object + * - Temporary references that result from the + * object being examined; being on a list, + * etc. The main purpose of these references + * is to make sure the object doesn't go away + * while being used. + * + * These are accounted for by ie_users. + * + * - Permanent references that result from the + * object being references by other objects + * (usually relations) such as a Portal Group, + * or a Discovery Domain. + * + * These are accounted for by ie_references. + * + * The main purpose of these references is to + * model some of the weirder life cycle states + * described in RFC 4711. + * + * Every reference via ie_references implies a + * reference via ie_users. + */ + unsigned int ie_users; + unsigned int ie_references; + + uint32_t ie_index; + + unsigned int ie_state; + unsigned int ie_flags; + time_t ie_mtime; + + uint32_t ie_scn_mask; /* Events this node listens for */ + uint32_t ie_scn_bits; /* Current event bits */ + + isns_attr_list_t ie_attrs; + isns_object_t * ie_container; + isns_object_template_t *ie_template; + + isns_relation_t * ie_relation; + isns_object_list_t ie_children; + + /* Bit vector describing DD membership */ + isns_bitvector_t * ie_membership; + + /* Support for virtual objects */ + int (*ie_rebuild)(isns_object_t *, isns_db_t *); +}; + +typedef struct isns_object_ref { + isns_object_t * obj; +} isns_object_ref_t; + +enum { + ISNS_RELATION_NONE = 0, + ISNS_RELATION_PORTAL_GROUP, +}; + +struct isns_relation { + unsigned int ir_type; + unsigned int ir_users; + isns_object_t * ir_object; + isns_object_ref_t ir_subordinate[2]; +}; + +typedef struct isns_relation_soup isns_relation_soup_t; + +typedef struct isns_relation_list isns_relation_list_t; +struct isns_relation_list { + unsigned int irl_count; + isns_relation_t ** irl_data; +}; +#define ISNS_RELATION_LIST_INIT { .irl_count = 0, .irl_data = NULL } + +#define ISNS_OBJECT_DIRTY 0x0001 +#define ISNS_OBJECT_PRIVATE 0x0002 +#define ISNS_OBJECT_DEAD 0x0004 + +enum { + ISNS_OBJECT_STATE_LARVAL, + ISNS_OBJECT_STATE_MATURE, + ISNS_OBJECT_STATE_LIMBO, + ISNS_OBJECT_STATE_DEAD, +}; + +extern int isns_object_remove_member(isns_object_t *obj, + const isns_attr_t *attr, + const uint32_t *subordinate_tags); + +extern void isns_object_reference_set(isns_object_ref_t *ref, + isns_object_t *obj); +extern void isns_object_reference_drop(isns_object_ref_t *ref); + +extern const char *isns_object_state_string(unsigned int); + +extern isns_object_template_t *isns_object_template_by_name(const char *); +extern int isns_object_is_valid_container(const isns_object_t *, + isns_object_template_t *); + +extern void isns_object_set_scn_mask(isns_object_t *, uint32_t); + +extern isns_object_t *isns_create_default_domain(void); + +/* + * Helper macros for object type check + */ +#define __ISNS_OBJECT_TYPE_CHECK(obj, type) \ + ((obj)->ie_template == &isns_##type##_template) +#define ISNS_IS_ENTITY(obj) __ISNS_OBJECT_TYPE_CHECK(obj, entity) +#define ISNS_IS_ISCSI_NODE(obj) __ISNS_OBJECT_TYPE_CHECK(obj, iscsi_node) +#define ISNS_IS_FC_PORT(obj) __ISNS_OBJECT_TYPE_CHECK(obj, fc_port) +#define ISNS_IS_FC_NODE(obj) __ISNS_OBJECT_TYPE_CHECK(obj, fc_node) +#define ISNS_IS_PORTAL(obj) __ISNS_OBJECT_TYPE_CHECK(obj, portal) +#define ISNS_IS_PG(obj) __ISNS_OBJECT_TYPE_CHECK(obj, iscsi_pg) +#define ISNS_IS_POLICY(obj) __ISNS_OBJECT_TYPE_CHECK(obj, policy) +#define ISNS_IS_DD(obj) __ISNS_OBJECT_TYPE_CHECK(obj, dd) +#define ISNS_IS_DDSET(obj) __ISNS_OBJECT_TYPE_CHECK(obj, ddset) + +#endif /* ISNS_OBJECTS_H */ diff --git a/utils/open-isns/parser.c b/utils/open-isns/parser.c new file mode 100644 index 0000000..378f2c8 --- /dev/null +++ b/utils/open-isns/parser.c @@ -0,0 +1,134 @@ +/* + * parser.c - simple line based parser + * + * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <getopt.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <err.h> +#include "util.h" + +/* + * By default, the parser will recognize any white space + * as "word" separators. + * If you need additional separators, you can put them + * here. + */ +const char * parser_separators = NULL; +const char * parser_punctuation = "="; + +char * +parser_get_next_line(FILE *fp) +{ + static char buffer[8192]; + unsigned int n = 0, count = 0; + int c, continuation = 0; + + while (n < sizeof(buffer) - 1) { + c = fgetc(fp); + if (c == EOF) + break; + + count++; + if (c == '\r') + continue; + /* Discard all blanks + * following a backslash-newline + */ + if (continuation) { + if (c == ' ' || c == '\t') + continue; + continuation = 0; + } + + if (c == '\n') { + if (n && buffer[n-1] == '\\') { + buffer[--n] = '\0'; + continuation = 1; + } + while (n && isspace(buffer[n-1])) + buffer[--n] = '\0'; + if (!continuation) + break; + buffer[n++] = ' '; + continue; + } + + buffer[n++] = c; + } + + if (count == 0) + return NULL; + + buffer[n] = '\0'; + return buffer; +} + +static inline int +is_separator(char c) +{ + if (isspace(c)) + return 1; + return parser_separators && c && strchr(parser_separators, c); +} + +static inline int +is_punctuation(char c) +{ + return parser_punctuation && c && strchr(parser_punctuation, c); +} + +char * +parser_get_next_word(char **sp) +{ + static char buffer[512]; + char *s = *sp, *p = buffer; + + while (is_separator(*s)) + ++s; + + if (*s == '\0') + goto done; + + if (is_punctuation(*s)) { + *p++ = *s++; + goto done; + } + + while (*s && !is_separator(*s) && !is_punctuation(*s)) + *p++ = *s++; + +done: + *p++ = '\0'; + *sp = s; + return buffer[0]? buffer : NULL; +} + +int +parser_split_line(char *line, unsigned int argsmax, char **argv) +{ + unsigned int argc = 0; + char *s; + + while (argc < argsmax && (s = parser_get_next_word(&line))) + argv[argc++] = strdup(s); + return argc; +} + +char * +parser_get_rest_of_line(char **sp) +{ + char *s = *sp, *res = NULL; + + while (is_separator(*s)) + ++s; + + *sp = ""; + if (*s != '\0') + res = s; + return res; +} diff --git a/utils/open-isns/paths.h b/utils/open-isns/paths.h new file mode 100644 index 0000000..b54612c --- /dev/null +++ b/utils/open-isns/paths.h @@ -0,0 +1,22 @@ +/* + * Compile time configuration. + * For now, let's keep it simple and ignore autoconf... + * + * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_CONFIG_H +#define ISNS_CONFIG_H + +#define __OPENISNS_MKVERSION(maj, min) (((maj) << 8) + (min)) +#define OPENISNS_VERSION __OPENISNS_MKVERSION(0, 90); +#define OPENISNS_VERSION_STRING "0.90" + +#define ISNS_ETCDIR "/etc/isns" +#define ISNS_RUNDIR "/var/run" +#define ISNS_DEFAULT_ISNSD_CONFIG ISNS_ETCDIR "/isnsd.conf" +#define ISNS_DEFAULT_ISNSDD_CONFIG ISNS_ETCDIR "/isnsdd.conf" +#define ISNS_DEFAULT_ISNSADM_CONFIG ISNS_ETCDIR "/isnsadm.conf" +#define ISNS_DEFAULT_LOCAL_REGISTRY ISNS_RUNDIR "/isns.registry" + +#endif /* ISNS_CONFIG_H */ diff --git a/utils/open-isns/pidfile.c b/utils/open-isns/pidfile.c new file mode 100644 index 0000000..3384373 --- /dev/null +++ b/utils/open-isns/pidfile.c @@ -0,0 +1,98 @@ +/* + * write pidfile + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> + +#include "util.h" + +static void +__update_pidfile(int fd) +{ + char pidbuf[32]; + + snprintf(pidbuf, sizeof(pidbuf), "%u\n", getpid()); + if (write(fd, pidbuf, strlen(pidbuf)) < 0) + isns_fatal("Error writing pid file: %m\n"); + close(fd); +} + +static pid_t +__read_pidfile(const char *filename) +{ + char pidbuf[32]; + FILE *fp; + pid_t pid = -1; + + fp = fopen(filename, "r"); + if (fp != NULL) { + if (fgets(pidbuf, sizeof(pidbuf), fp)) + pid = strtoul(pidbuf, NULL, 0); + fclose(fp); + } + return pid; +} + +void +isns_write_pidfile(const char *filename) +{ + int fd; + pid_t pid; + + fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0644); + if (fd >= 0) { + __update_pidfile(fd); + return; + } + + if (errno != EEXIST) + isns_fatal("Error creating pid file %s: %m\n", + filename); + + /* If the pid file is stale, remove it. + * Not really needed in real life, but + * highly convenient for debugging :) */ + if ((pid = __read_pidfile(filename)) > 0 + && kill(pid, 0) < 0 + && errno == ESRCH) { + isns_debug_general( + "Removing stale PID file %s\n", + filename); + unlink(filename); + } + + /* Try again */ + fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0644); + if (fd < 0) + isns_fatal("PID file exists; another daemon " + "seems to be running\n"); + + __update_pidfile(fd); +} + +void +isns_update_pidfile(const char *filename) +{ + int fd; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + isns_fatal("Error opening pid file %s: %m\n", + filename); + } + + __update_pidfile(fd); +} + +void +isns_remove_pidfile(const char *filename) +{ + unlink(filename); +} diff --git a/utils/open-isns/pki.c b/utils/open-isns/pki.c new file mode 100644 index 0000000..f3af922 --- /dev/null +++ b/utils/open-isns/pki.c @@ -0,0 +1,536 @@ +/* + * PKI related functions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <fcntl.h> +#include "isns.h" +#include "security.h" +#include "util.h" +#include "config.h" + +#ifdef WITH_SECURITY + +/* versions prior to 9.6.8 didn't seem to have these */ +#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906080L +# define EVP_MD_CTX_init(c) do { } while (0) +# define EVP_MD_CTX_cleanup(c) do { } while (0) +#endif +#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L +# define i2d_DSA_PUBKEY i2d_DSA_PUBKEY_backwards + +static int i2d_DSA_PUBKEY_backwards(DSA *, unsigned char **); +#endif + +static int isns_openssl_init = 0; + +static int isns_dsasig_verify(isns_security_t *ctx, + isns_principal_t *peer, + buf_t *pdu, + const struct isns_authblk *); +static int isns_dsasig_sign(isns_security_t *ctx, + isns_principal_t *peer, + buf_t *pdu, + struct isns_authblk *); +static EVP_PKEY *isns_dsasig_load_private_pem(isns_security_t *ctx, + const char *filename); +static EVP_PKEY *isns_dsasig_load_public_pem(isns_security_t *ctx, + const char *filename); +static DSA * isns_dsa_load_params(const char *); + + +/* + * Create a DSA security context + */ +isns_security_t * +isns_create_dsa_context(void) +{ + isns_security_t *ctx; + + if (!isns_openssl_init) { + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + isns_openssl_init = 1; + } + + ctx = isns_calloc(1, sizeof(*ctx)); + + ctx->is_name = "DSA"; + ctx->is_type = ISNS_AUTH_TYPE_SHA1_DSA; + ctx->is_replay_window = isns_config.ic_auth.replay_window; + ctx->is_timestamp_jitter = isns_config.ic_auth.timestamp_jitter; + + ctx->is_verify = isns_dsasig_verify; + ctx->is_sign = isns_dsasig_sign; + ctx->is_load_private = isns_dsasig_load_private_pem; + ctx->is_load_public = isns_dsasig_load_public_pem; + + isns_debug_auth("Created DSA authentication context\n"); + return ctx; +} + +/* + * DSA signature generation and verification + */ +static void +isns_message_digest(EVP_MD_CTX *md, const buf_t *pdu, + const struct isns_authblk *blk) +{ + uint64_t stamp; + + EVP_DigestUpdate(md, buf_head(pdu), buf_avail(pdu)); + + /* The RFC doesn't say which pieces of the + * message should be hashed. + * We make an educated guess. + */ + stamp = htonll(blk->iab_timestamp); + EVP_DigestUpdate(md, &stamp, sizeof(stamp)); +} + +static void +isns_dsasig_report_errors(const char *msg, isns_print_fn_t *fn) +{ + unsigned long code; + + fn("%s - OpenSSL errors follow:\n", msg); + while ((code = ERR_get_error()) != 0) + fn("> %s: %s\n", + ERR_func_error_string(code), + ERR_reason_error_string(code)); +} + +int +isns_dsasig_sign(isns_security_t *ctx, + isns_principal_t *peer, + buf_t *pdu, + struct isns_authblk *blk) +{ + static unsigned char signature[1024]; + unsigned int sig_len = sizeof(signature); + EVP_MD_CTX md_ctx; + EVP_PKEY *pkey; + int err; + + if ((pkey = peer->is_key) == NULL) + return 0; + + if (pkey->type != EVP_PKEY_DSA) { + isns_debug_message( + "Incompatible public key (spi=%s)\n", + peer->is_name); + return 0; + } + if (EVP_PKEY_size(pkey) > sizeof(signature)) { + isns_error("isns_dsasig_sign: signature buffer too small\n"); + return 0; + } + if (pkey->pkey.dsa->priv_key == NULL) { + isns_error("isns_dsasig_sign: oops, seems to be a public key\n"); + return 0; + } + + isns_debug_auth("Signing messages with spi=%s, DSA/%u\n", + peer->is_name, EVP_PKEY_bits(pkey)); + + EVP_MD_CTX_init(&md_ctx); + EVP_SignInit(&md_ctx, EVP_dss1()); + isns_message_digest(&md_ctx, pdu, blk); + err = EVP_SignFinal(&md_ctx, + signature, &sig_len, + pkey); + EVP_MD_CTX_cleanup(&md_ctx); + + if (err == 0) { + isns_dsasig_report_errors("EVP_SignFinal failed", isns_error); + return 0; + } + + blk->iab_sig = signature; + blk->iab_sig_len = sig_len; + return 1; +} + +int +isns_dsasig_verify(isns_security_t *ctx, + isns_principal_t *peer, + buf_t *pdu, + const struct isns_authblk *blk) +{ + EVP_MD_CTX md_ctx; + EVP_PKEY *pkey; + int err; + + if ((pkey = peer->is_key) == NULL) + return 0; + + if (pkey->type != EVP_PKEY_DSA) { + isns_debug_message( + "Incompatible public key (spi=%s)\n", + peer->is_name); + return 0; + } + + EVP_MD_CTX_init(&md_ctx); + EVP_VerifyInit(&md_ctx, EVP_dss1()); + isns_message_digest(&md_ctx, pdu, blk); + err = EVP_VerifyFinal(&md_ctx, + blk->iab_sig, blk->iab_sig_len, + pkey); + EVP_MD_CTX_cleanup(&md_ctx); + + if (err == 0) { + isns_debug_auth("*** Incorrect signature ***\n"); + return 0; + } + if (err < 0) { + isns_dsasig_report_errors("EVP_VerifyFinal failed", isns_error); + return 0; + } + + isns_debug_message("Good signature from %s\n", + peer->is_name?: "<server>"); + return 1; +} + +EVP_PKEY * +isns_dsasig_load_private_pem(isns_security_t *ctx, const char *filename) +{ + EVP_PKEY *pkey; + FILE *fp; + + if (!(fp = fopen(filename, "r"))) { + isns_error("Unable to open DSA keyfile %s: %m\n", + filename); + return 0; + } + + pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + fclose(fp); + return pkey; +} + +EVP_PKEY * +isns_dsasig_load_public_pem(isns_security_t *ctx, const char *filename) +{ + EVP_PKEY *pkey; + FILE *fp; + + if (!(fp = fopen(filename, "r"))) { + isns_error("Unable to open DSA keyfile %s: %m\n", + filename); + return 0; + } + + pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); + if (pkey == NULL) { + isns_dsasig_report_errors("Error loading DSA public key", + isns_error); + } + + fclose(fp); + return pkey; +} + +EVP_PKEY * +isns_dsa_decode_public(const void *ptr, size_t len) +{ + const unsigned char *der = ptr; + EVP_PKEY *evp; + DSA *dsa; + + /* Assigning ptr to a temporary variable avoids a silly + * compiled warning about type-punning. */ + dsa = d2i_DSA_PUBKEY(NULL, &der, len); + if (dsa == NULL) + return NULL; + + evp = EVP_PKEY_new(); + EVP_PKEY_assign_DSA(evp, dsa); + return evp; +} + +int +isns_dsa_encode_public(EVP_PKEY *pkey, void **ptr, size_t *len) +{ + int bytes; + + *ptr = NULL; + bytes = i2d_DSA_PUBKEY(pkey->pkey.dsa, (unsigned char **) ptr); + if (bytes < 0) + return 0; + + *len = bytes; + return 1; +} + +EVP_PKEY * +isns_dsa_load_public(const char *name) +{ + return isns_dsasig_load_public_pem(NULL, name); +} + +int +isns_dsa_store_private(const char *name, EVP_PKEY *key) +{ + FILE *fp; + int rv, fd; + + if ((fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { + isns_error("Cannot save DSA key to %s: %m\n", name); + return 0; + } + + if (!(fp = fdopen(fd, "w"))) { + isns_error("fdopen(%s): %m\n", name); + close(fd); + return 0; + } + + rv = PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL); + fclose(fp); + + if (rv == 0) + isns_dsasig_report_errors("Failed to store private key", + isns_error); + + return rv; +} + +int +isns_dsa_store_public(const char *name, EVP_PKEY *key) +{ + FILE *fp; + int rv; + + if (!(fp = fopen(name, "w"))) { + isns_error("Unable to open %s: %m\n", name); + return 0; + } + + rv = PEM_write_PUBKEY(fp, key); + fclose(fp); + + if (rv == 0) + isns_dsasig_report_errors("Failed to store public key", + isns_error); + + return rv; +} + + +/* + * DSA key generation + */ +EVP_PKEY * +isns_dsa_generate_key(void) +{ + EVP_PKEY *pkey; + DSA *dsa = NULL; + + if (!(dsa = isns_dsa_load_params(isns_config.ic_dsa.param_file))) + goto failed; + + if (!DSA_generate_key(dsa)) { + isns_dsasig_report_errors("Failed to generate DSA key", + isns_error); + goto failed; + } + + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_DSA(pkey, dsa); + return pkey; + +failed: + if (dsa) + DSA_free(dsa); + return NULL; +} + +DSA * +isns_dsa_load_params(const char *filename) +{ + FILE *fp; + DSA *dsa; + + if (!filename) { + isns_error("Cannot generate key - no DSA parameter file\n"); + return NULL; + } + if (!(fp = fopen(filename, "r"))) { + isns_error("Unable to open %s: %m\n", filename); + return NULL; + } + + dsa = PEM_read_DSAparams(fp, NULL, NULL, NULL); + fclose(fp); + + if (dsa == NULL) { + isns_dsasig_report_errors("Error loading DSA parameters", + isns_error); + } + + return dsa; +} + +static void +isns_dsa_param_gen_callback(int stage, int index, void *dummy) +{ + if (stage == 0) + write(1, "+", 1); + else if (stage == 1) + write(1, ".", 1); + else if (stage == 2) + write(1, "/", 1); +} + +int +isns_dsa_init_params(const char *filename) +{ + FILE *fp; + DSA *dsa; + + if (access(filename, R_OK) == 0) + return 1; + + isns_mkdir_recursive(isns_dirname(filename)); + if (!(fp = fopen(filename, "w"))) { + isns_error("Unable to open %s: %m\n", filename); + return 0; + } + + isns_notice("Generating DSA parameters; this may take a while\n"); + dsa = DSA_generate_parameters(1024, NULL, 0, + NULL, NULL, isns_dsa_param_gen_callback, NULL); + write(1, "\n", 1); + + if (dsa == NULL) { + isns_dsasig_report_errors("Error generating DSA parameters", + isns_error); + fclose(fp); + return 0; + } + + if (!PEM_write_DSAparams(fp, dsa)) { + isns_dsasig_report_errors("Error writing DSA parameters", + isns_error); + DSA_free(dsa); + fclose(fp); + return 0; + } + DSA_free(dsa); + fclose(fp); + return 1; +} + +/* + * Make sure the authentication key is present. + */ +int +isns_dsa_init_key(const char *filename) +{ + char pubkey_path[1024]; + EVP_PKEY *pkey; + + isns_mkdir_recursive(isns_dirname(filename)); + snprintf(pubkey_path, sizeof(pubkey_path), + "%s.pub", filename); + if (access(filename, R_OK) == 0 + && access(pubkey_path, R_OK) == 0) + return 1; + + if (!(pkey = isns_dsa_generate_key())) { + isns_error("Failed to generate AuthKey\n"); + return 0; + } + + if (!isns_dsa_store_private(filename, pkey)) { + isns_error("Unable to write private key to %s\n", filename); + return 0; + } + isns_notice("Stored private key in %s\n", filename); + + if (!isns_dsa_store_public(pubkey_path, pkey)) { + isns_error("Unable to write public key to %s\n", pubkey_path); + return 0; + } + isns_notice("Stored private key in %s\n", pubkey_path); + + return 1; +} + +/* + * Simple keystore - this is a flat directory, with + * public key files using the SPI as their name. + */ +typedef struct isns_simple_keystore isns_simple_keystore_t; +struct isns_simple_keystore { + isns_keystore_t sc_base; + char * sc_dirpath; +}; + +/* + * Load a DSA key from the cert store + * In fact, this will load RSA keys as well. + */ +static EVP_PKEY * +__isns_simple_keystore_find(isns_keystore_t *store_base, + const char *name, size_t namelen) +{ + isns_simple_keystore_t *store = (isns_simple_keystore_t *) store_base; + char pathname[PATH_MAX]; + + /* Refuse to open key files with names + * that refer to parent directories */ + if (memchr(name, '/', namelen) || name[0] == '.') + return NULL; + + snprintf(pathname, sizeof(pathname), + "%s/%.*s", store->sc_dirpath, + (int) namelen, name); + if (access(pathname, R_OK) < 0) + return NULL; + return isns_dsasig_load_public_pem(NULL, pathname); +} + +isns_keystore_t * +isns_create_simple_keystore(const char *dirname) +{ + isns_simple_keystore_t *store; + + store = isns_calloc(1, sizeof(*store)); + store->sc_base.ic_name = "simple key store"; + store->sc_base.ic_find = __isns_simple_keystore_find; + store->sc_dirpath = isns_strdup(dirname); + + return (isns_keystore_t *) store; +} + +#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L +#undef i2d_DSA_PUBKEY + +int +i2d_DSA_PUBKEY_backwards(DSA *dsa, unsigned char **ptr) +{ + unsigned char *buf; + int len; + + len = i2d_DSA_PUBKEY(dsa, NULL); + if (len < 0) + return 0; + + *ptr = buf = OPENSSL_malloc(len); + return i2d_DSA_PUBKEY(dsa, &buf); +} +#endif + +#endif /* WITH_SECURITY */ diff --git a/utils/open-isns/policy.c b/utils/open-isns/policy.c new file mode 100644 index 0000000..46de15b --- /dev/null +++ b/utils/open-isns/policy.c @@ -0,0 +1,577 @@ +/* + * Open-iSNS policy engine + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + * + * For now, policy is static. We can make it configurable + * later. + */ + +#include <string.h> +#include "isns.h" +#include "security.h" +#include "objects.h" +#include "message.h" +#include "util.h" + +/* + A brief discussion of policy + + For now, a principal's name (ie its SPI string) *must* match + the iSNS source name it uses. + + Special care needs to be taken to restrict which principals + are permitted to act as a control node. For now, we don't + implement control node semantics. + + */ + +static unsigned int isns_policy_gen = 0; + +/* + * Administrative policy (everything allowed, + * talks to entity "CONTROL" + */ +static isns_policy_t isns_superhero_powers = { + .ip_name = "administrator", + .ip_users = 1, + .ip_gen = 0, + + .ip_entity = ISNS_ENTITY_CONTROL, + .ip_functions = ~0, + .ip_object_types = ~0, + .ip_node_types = ~0, +}; + +/* + * Policy for anon user + */ +static isns_policy_t isns_dweeb_powers = { + .ip_name = "anonymous", + .ip_users = 1, + .ip_gen = 0, + + .ip_functions = 1 << ISNS_DEVICE_ATTRIBUTE_QUERY, + .ip_object_types = 0, + .ip_node_types = 0, +}; + +#define IS_ANON_POLICY(p) ((p) == &isns_dweeb_powers) + +/* + * These are used when security is turned off. + * Essentially the same as superhero, except + * no eid specified. + */ +static isns_policy_t isns_flyingpigs_powers = { + .ip_name = "insecure", + .ip_users = 1, + .ip_gen = 0, + + .ip_functions = ~0, + .ip_object_types = ~0, + .ip_node_types = ~0, +}; + + +isns_policy_t * +isns_policy_bind(const isns_message_t *msg) +{ + isns_policy_t *policy = NULL; + isns_principal_t *princ = NULL; + + /* When the admin turns off gravity, + * pigs can fly, too. */ + if (isns_config.ic_security == 0) { + policy = &isns_flyingpigs_powers; + goto found; + } + + /* If the caller is the local root user, s/he can + * do anything. */ + if (msg->im_creds && msg->im_creds->uid == 0) { + policy = &isns_superhero_powers; + goto found; + } + + /* Tie the SPI given in the auth block to a + * source name. + * For now, the names have to match. Down the road, + * there may be more flexible schemes. + */ + if ((princ = msg->im_security) != NULL) { + if ((policy = princ->is_policy) != NULL) + goto found; + + isns_error("Internal error - no policy for " + "principal %s!\n", + princ->is_name); + } + + policy = &isns_dweeb_powers; + +found: + policy->ip_users++; + return policy; +} + +/* + * Check whether the call is permitted. + * This is particularly useful to prevent rogue + * clients from messing with Discovery Domains. + */ +int +isns_policy_validate_function(const isns_policy_t *policy, + const isns_message_t *msg) +{ + uint32_t function = msg->im_header.i_function; + int rv = 0; + + if (function >= 32) { + isns_debug_auth("Bad function code %08x\n", function); + return 0; + } + + if (!(policy->ip_functions & (1 << function))) + goto reject; + + rv = 1; + +reject: + isns_debug_auth(":: policy %s function %s (%04x) %s\n", + policy->ip_name, + isns_function_name(function), function, + rv? "permitted" : "DENIED"); + return rv; +} + +/* + * Helper function to validate node names and source names + */ +static int +__validate_node_name(const isns_policy_t *policy, const char *name) +{ + const struct string_array *ap; + unsigned int i; + + /* Control nodes get to do everything */ + if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) + return 1; + + ap = &policy->ip_node_names; + for (i = 0; i < ap->count; ++i) { + const char *s; + + s = ap->list[i]; + if (s == NULL) + continue; + if (isns_source_pattern_match(s, name)) + return 1; + } + return 0; +} + +/* + * Validate the source of a message + */ +int +isns_policy_validate_source(const isns_policy_t *policy, + const isns_source_t *source) +{ + const char *src_name = isns_source_name(source); + int rv = 0; + + if (!__validate_node_name(policy, src_name)) + goto reject; + + rv = 1; + +reject: + isns_debug_auth(":: policy %s source %s %s\n", + policy->ip_name, src_name, + rv? "permitted" : "DENIED"); + return rv; +} + +/* + * Check whether the entity name specified by the client + * is actually his to use. + */ +int +isns_policy_validate_entity(const isns_policy_t *policy, + const char *eid) +{ + int rv = 0, eidlen; + + /* Control nodes get to do everything */ + if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) + goto accept; + + /* For anonymous clients, refuse any attempt to + * create an entity */ + if (IS_ANON_POLICY(policy)) + goto reject; + + /* If no entity is assigned, this means the client + * is not permitted to specify its own entity name, + * and accept what we assign it. + */ + if (policy->ip_entity == NULL) + goto reject; + + eidlen = strlen(policy->ip_entity); + if (strncasecmp(policy->ip_entity, eid, eidlen) + && (eid[eidlen] == ':' || eid[eidlen] == '\0')) + goto reject; + +accept: rv = 1; + +reject: + isns_debug_auth(":: policy %s entity ID %s %s\n", + policy->ip_name, eid, + rv? "permitted" : "DENIED"); + return rv; +} + +const char * +isns_policy_default_entity(const isns_policy_t *policy) +{ + return policy->ip_entity; +} + +int +isns_policy_validate_node_name(const isns_policy_t *policy, + const char *node_name) +{ + int rv = 0; + + /* Control nodes get to do everything */ + if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) + goto accept; + + if (!__validate_node_name(policy, node_name)) + goto reject; + +accept: rv = 1; +reject: + isns_debug_auth(":: policy %s storage node name %s %s\n", + policy->ip_name, node_name, + rv? "permitted" : "DENIED"); + return rv; +} + +/* + * Check whether the client is allowed to access + * the given object in a particular way. + */ +static int +__isns_policy_validate_object_access(const isns_policy_t *policy, + const isns_source_t *source, + const isns_object_t *obj, + isns_object_template_t *tmpl, + unsigned int function) +{ + uint32_t mask, perm = ISNS_PERMISSION_WRITE; + int rv = 0; + + /* Control nodes get to do everything */ + if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) + goto accept; + + if (function == ISNS_DEVICE_ATTRIBUTE_QUERY + || function == ISNS_DEVICE_GET_NEXT) + perm = ISNS_PERMISSION_READ; + + /* + * 5.6.1. Source Attribute + * + * For messages that change the contents of the iSNS + * database, the iSNS server MUST verify that the Source + * Attribute identifies either a Control Node or a Storage + * Node that is a part of the Network Entity containing + * the added, deleted, or modified objects. + * + * Note: this statement makes sense for nodes, portals + * etc, but not for discovery domains, which are not + * part of any network entity (but the Control Node clause + * above still applies). + */ + if (perm == ISNS_PERMISSION_WRITE && obj != NULL) { + const isns_object_t *entity; + + entity = obj->ie_container; + if (entity && entity != source->is_entity) + goto refuse; + + /* You're not allowed to modify virtual objects */ + if (obj->ie_rebuild) + goto refuse; + } + + /* Check whether the client is permitted + to access such an object */ + mask = ISNS_ACCESS(tmpl->iot_handle, perm); + if (!(policy->ip_object_types & mask)) + goto refuse; + + if (source->is_untrusted && (obj->ie_flags & ISNS_OBJECT_PRIVATE)) + goto refuse; + +accept: + rv = 1; + +refuse: + if (obj) { + isns_debug_auth(":: policy %s operation %s on object %08x (%s) %s\n", + policy->ip_name, + isns_function_name(function), + obj->ie_index, + tmpl->iot_name, + rv? "permitted" : "DENIED"); + } else { + isns_debug_auth(":: policy %s operation %s on %s object %s\n", + policy->ip_name, + isns_function_name(function), + tmpl->iot_name, + rv? "permitted" : "DENIED"); + } + return rv; +} + +/* + * Check whether the client is allowed to access + * the given object. This is called for read functions. + */ +int +isns_policy_validate_object_access(const isns_policy_t *policy, + const isns_source_t *source, + const isns_object_t *obj, + unsigned int function) +{ + return __isns_policy_validate_object_access(policy, source, + obj, obj->ie_template, + function); +} + +/* + * Check whether the client is allowed to update + * the given object. + */ +int +isns_policy_validate_object_update(const isns_policy_t *policy, + const isns_source_t *source, + const isns_object_t *obj, + const isns_attr_list_t *attrs, + unsigned int function) +{ + return __isns_policy_validate_object_access(policy, source, + obj, obj->ie_template, + function); +} + +/* + * Check whether the client is allowed to create an object + * with the given attrs. + */ +int +isns_policy_validate_object_creation(const isns_policy_t *policy, + const isns_source_t *source, + isns_object_template_t *tmpl, + const isns_attr_list_t *keys, + const isns_attr_list_t *attrs, + unsigned int function) +{ + const char *name = NULL; + + if (tmpl == &isns_entity_template) { + /* DevReg messages may contain an empty EID + * string, which means the server should select + * one. */ + if (isns_attr_list_get_string(keys, + ISNS_TAG_ENTITY_IDENTIFIER, &name) + && !isns_policy_validate_entity(policy, name)) + return 0; + } + + if (tmpl == &isns_iscsi_node_template) { + if (isns_attr_list_get_string(keys, + ISNS_TAG_ISCSI_NAME, &name) + && !isns_policy_validate_node_name(policy, name)) + return 0; + } + + /* Should we also include the permitted portals + * in the policy? */ + + return __isns_policy_validate_object_access(policy, source, + NULL, tmpl, function); +} + +/* + * Check whether the client is permitted to access + * or create an object of this type. + * FIXME: Pass R/W permission bit + */ +int +isns_policy_validate_object_type(const isns_policy_t *policy, + isns_object_template_t *tmpl, + unsigned int function) +{ + uint32_t mask; + int rv = 0; + + /* Control nodes get to do everything */ + if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) + goto accept; + + mask = ISNS_ACCESS_R(tmpl->iot_handle); + if (!(policy->ip_object_types & mask)) + goto reject; + +accept: rv = 1; + +reject: + isns_debug_auth(":: policy %s operation %s on object type %s %s\n", + policy->ip_name, + isns_function_name(function), + tmpl->iot_name, + rv? "permitted" : "DENIED"); + return rv; +} + +int +isns_policy_validate_node_type(const isns_policy_t *policy, uint32_t type) +{ + int rv = 0; + + if ((~policy->ip_node_types & type) == 0) + rv = 1; + + isns_debug_auth(":: policy %s registration of node type 0x%x %s\n", + policy->ip_name, type, + rv? "permitted" : "DENIED"); + return rv; +} + +/* + * 6.4.4. + * Management SCNs provide information about all changes to the network, + * regardless of discovery domain membership. Registration for management + * SCNs is indicated by setting bit 26 to 1. Only Control Nodes may + * register for management SCNs. Bits 30 and 31 may only be enabled if + * bit 26 is set to 1. + */ +int +isns_policy_validate_scn_bitmap(const isns_policy_t *policy, + uint32_t bitmap) +{ + int rv = 1; + + if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK) + goto accept; + + if (!(bitmap & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)) { + if (bitmap & (ISNS_SCN_DD_MEMBER_ADDED_MASK | + ISNS_SCN_DD_MEMBER_REMOVED_MASK)) + goto reject; + goto accept; + } + +reject: + rv = 0; + +accept: + isns_debug_auth(":: policy %s scn bitmap 0x%x %s\n", + policy->ip_name, bitmap, + rv? "permitted" : "DENIED"); + return rv; +} + +/* + * Create the default policy for a given SPI + */ +isns_policy_t * +isns_policy_default(const char *spi, size_t len) +{ + return __isns_policy_alloc(spi, len); +} + +/* + * Create the policy object for the server we're + * talking to. The server is allowed to send us + * ESI and SCN messages, and that's about it. + */ +isns_policy_t * +isns_policy_server(void) +{ + isns_policy_t *policy; + + policy = __isns_policy_alloc("<server>", 8); + + policy->ip_functions = + (1 << ISNS_ENTITY_STATUS_INQUIRY) | + (1 << ISNS_STATE_CHANGE_NOTIFICATION); + policy->ip_node_types = 0; + policy->ip_object_types = 0; + isns_string_array_append(&policy->ip_node_names, "*"); + return policy; +} + +/* + * Allocate an empty policy object + */ +isns_policy_t * +__isns_policy_alloc(const char *spi, size_t len) +{ + isns_policy_t *policy; + + policy = isns_calloc(1, sizeof(*policy)); + policy->ip_name = isns_malloc(len + 1); + policy->ip_users = 1; + policy->ip_gen = isns_policy_gen; + + memcpy(policy->ip_name, spi, len); + policy->ip_name[len] = '\0'; + + /* Only register/query allowed */ + policy->ip_functions = + (1 << ISNS_DEVICE_ATTRIBUTE_REGISTER) | + (1 << ISNS_DEVICE_ATTRIBUTE_QUERY) | + (1 << ISNS_DEVICE_GET_NEXT) | + (1 << ISNS_DEVICE_DEREGISTER) | + (1 << ISNS_SCN_REGISTER); + + /* Can only register initiator node(s) */ + policy->ip_node_types = + ISNS_ISCSI_INITIATOR_MASK; + + /* Can only view/modify standard objects */ + policy->ip_object_types = ISNS_DEFAULT_OBJECT_ACCESS; + + return policy; +} + +/* + * Release a policy object + */ +void +isns_policy_release(isns_policy_t *policy) +{ + if (!policy) + return; + + isns_assert(policy->ip_users); + if (--(policy->ip_users)) + return; + + isns_assert(policy != &isns_superhero_powers); + isns_assert(policy != &isns_flyingpigs_powers); + isns_assert(policy != &isns_dweeb_powers); + + isns_free(policy->ip_name); + isns_free(policy->ip_entity); + isns_free(policy->ip_dd_default); + isns_string_array_destroy(&policy->ip_node_names); + + isns_free(policy); +} diff --git a/utils/open-isns/portal-group.c b/utils/open-isns/portal-group.c new file mode 100644 index 0000000..647bbde --- /dev/null +++ b/utils/open-isns/portal-group.c @@ -0,0 +1,307 @@ +/* + * iSNS object model - portal group specific code + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "objects.h" +#include "vendor.h" +#include "attrs.h" +#include "util.h" + +/* For relationship stuff - should go */ +#include "db.h" + + +/* + * Retrieve attribute @old_tag from object @obj, create a copy with + * tag @new_tag, and append it to list @dst. + * (Helper function for the portal group stuff) + */ +static int +__isns_object_translate_attr(isns_object_t *obj, + uint32_t old_tag, uint32_t new_tag, + isns_attr_list_t *dst) +{ + isns_value_t value; + + if (!isns_attr_list_get_value(&obj->ie_attrs, old_tag, &value)) + return 0; + isns_attr_list_append_value(dst, new_tag, NULL, &value); + return 1; +} + + +/* + * Portal Group + */ +static isns_object_t * +__isns_pg_create(const isns_attr_list_t *attrs, uint32_t pg_tag, + isns_object_t *portal, isns_object_t *node) +{ + isns_object_t *obj; + + obj = isns_create_object(&isns_iscsi_pg_template, attrs, + isns_object_get_entity(portal)); + + /* + * 3.4 + * + * Each Portal and iSCSI Storage Node registered in an Entity can + * be associated using a Portal Group (PG) object. The PG Tag + * (PGT), if non-NULL, indicates that the associated Portal + * provides access to the associated iSCSI Storage Node in + * the Entity. All Portals that have the same PGT value for + * a specific iSCSI Storage Node allow coordinated access to + * that node. + * + * 5.6.5.2 + * + * If the PGT of the Portal Group is not NULL, then a relationship + * exists between the indicated Storage Node and Portal; if the + * PGT is NULL, then no relationship exists. + */ + if (pg_tag != 0) { + isns_object_set_uint32(obj, + ISNS_TAG_PG_TAG, pg_tag); + } else { + /* A NULL PGT indicates that the + * storage node cannot be accessed through + * this portal. */ + isns_object_set_nil(obj, ISNS_TAG_PG_TAG); + } + + /* This object represents a relationship between portal + and storage node. Create a relation. */ + obj->ie_relation = isns_create_relation(obj, + ISNS_RELATION_PORTAL_GROUP, + portal, node); + + return obj; +} + +/* + * Find the portal for a given portal group + */ +static isns_object_t * +__isns_pg_find_portal(isns_db_t *db, isns_object_t *pg, + const isns_object_list_t *extra_objs) +{ + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + isns_object_t *obj = NULL; + + /* FIXME: ISNS_TAG_PG_PORTAL_IP_ADDR -> ...ADDRESS */ + if (!__isns_object_translate_attr(pg, + ISNS_TAG_PG_PORTAL_IP_ADDR, + ISNS_TAG_PORTAL_IP_ADDRESS, + &key_attrs)) + goto out; + if (!__isns_object_translate_attr(pg, + ISNS_TAG_PG_PORTAL_TCP_UDP_PORT, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + &key_attrs)) + goto out; + + obj = isns_db_lookup(db, &isns_portal_template, &key_attrs); + if (!obj && extra_objs) + obj = isns_object_list_lookup(extra_objs, + &isns_portal_template, &key_attrs); + +out: + isns_attr_list_destroy(&key_attrs); + return obj; +} + +/* + * Find the node for a given portal group + */ +static isns_object_t * +__isns_pg_find_node(isns_db_t *db, isns_object_t *pg, + const isns_object_list_t *extra_objs) +{ + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + isns_object_t *obj = NULL; + + if (!__isns_object_translate_attr(pg, + ISNS_TAG_PG_ISCSI_NAME, + ISNS_TAG_ISCSI_NAME, + &key_attrs)) + goto out; + + obj = isns_db_lookup(db, &isns_iscsi_node_template, &key_attrs); + if (!obj && extra_objs) + obj = isns_object_list_lookup(extra_objs, + &isns_iscsi_node_template, &key_attrs); + +out: + isns_attr_list_destroy(&key_attrs); + return obj; +} + +/* + * When creating a portal group, it must not connect nodes and + * portals from other entities. However, it is perfectly fine to + * link objects in limbo. + */ +static inline int +__isns_pg_may_relate(isns_object_t *entity, isns_object_t *subordinate) +{ + isns_object_t *other; + + other = isns_object_get_entity(subordinate); + return other == NULL || other == entity; +} + +/* + * Given a portal group object, create the relationship + */ +isns_relation_t * +isns_db_build_pg_relation(isns_db_t *db, isns_object_t *pg, + const isns_object_list_t *extra_objs) +{ + isns_object_t *entity, *node = NULL, *portal = NULL; + + entity = isns_object_get_entity(pg); + + node = __isns_pg_find_node(db, pg, extra_objs); + if (node == NULL) { + isns_error("Trying to register PG for non-existant node\n"); + goto failed; + } + if (!__isns_pg_may_relate(entity, node)) { + isns_error("Trying to register PG for node in other entity\n"); + goto failed; + } + + portal = __isns_pg_find_portal(db, pg, extra_objs); + if (portal == NULL) { + isns_error("Trying to register PG for non-existant portal\n"); + goto failed; + } + if (!__isns_pg_may_relate(entity, portal)) { + isns_error("Trying to register PG for portal in other entity\n"); + goto failed; + } + + pg->ie_relation = isns_create_relation(pg, + ISNS_RELATION_PORTAL_GROUP, + node, portal); + isns_object_release(portal); + isns_object_release(node); + + return pg->ie_relation; + +failed: + if (portal) + isns_object_release(portal); + if (node) + isns_object_release(node); + return NULL; +} + +/* + * Create a portal group given node, portal and PGT + */ +isns_object_t * +isns_create_portal_group(isns_object_t *portal, + isns_object_t *node, uint32_t pg_tag) +{ + isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT; + isns_object_t *obj = NULL; + + if (portal == NULL || node == NULL) + return NULL; + + if (node->ie_container != portal->ie_container) { + isns_error("Refusing to create portal group " + "linking objects from different entities\n"); + return NULL; + } + + if (__isns_object_translate_attr(node, + ISNS_TAG_ISCSI_NAME, + ISNS_TAG_PG_ISCSI_NAME, + &key_attrs) + && __isns_object_translate_attr(portal, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PG_PORTAL_IP_ADDR, + &key_attrs) + && __isns_object_translate_attr(portal, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + ISNS_TAG_PG_PORTAL_TCP_UDP_PORT, + &key_attrs)) { + obj = __isns_pg_create(&key_attrs, pg_tag, portal, node); + } + + isns_attr_list_destroy(&key_attrs); + return obj; +} + +/* + * 5.6.5.1 + * New PG objects are registered when an associated Portal or + * iSCSI Node object is registered. An explicit PG object + * registration MAY follow a Portal or iSCSI Node object + * registration in a DevAttrReg message. + * [...] + * If the PGT value is not included in the Storage Node or + * Portal object registration, and if a PGT value was not + * previously registered for the relationship, then the PGT for + * the corresponding PG object SHALL be registered with a value + * of 0x00000001. + * + * We return non-NULL if the object was created. + */ +isns_object_t * +isns_create_default_portal_group(isns_db_t *db, + isns_object_t *portal, isns_object_t *node) +{ + isns_object_t *obj; + + if (portal == NULL || node == NULL) + return 0; + + /* See if there is a PG already */ + obj = isns_db_get_relationship_object(db, node, portal, + ISNS_RELATION_PORTAL_GROUP); + if (obj != NULL) { + isns_object_release(obj); + return NULL; + } + + return isns_create_portal_group(portal, node, 1); +} + +static uint32_t iscsi_pg_attrs[] = { + ISNS_TAG_PG_ISCSI_NAME, + ISNS_TAG_PG_PORTAL_IP_ADDR, + ISNS_TAG_PG_PORTAL_TCP_UDP_PORT, + ISNS_TAG_PG_TAG, + ISNS_TAG_PG_INDEX, +}; + +static uint32_t iscsi_pg_key_attrs[] = { + ISNS_TAG_PG_ISCSI_NAME, + ISNS_TAG_PG_PORTAL_IP_ADDR, + ISNS_TAG_PG_PORTAL_TCP_UDP_PORT, +}; + +isns_object_template_t isns_iscsi_pg_template = { + .iot_name = "iSCSI Portal Group", + .iot_handle = ISNS_OBJECT_TYPE_PG, + .iot_attrs = iscsi_pg_attrs, + .iot_num_attrs = array_num_elements(iscsi_pg_attrs), + .iot_keys = iscsi_pg_key_attrs, + .iot_num_keys = array_num_elements(iscsi_pg_key_attrs), + .iot_attrs = iscsi_pg_attrs, + .iot_keys = iscsi_pg_key_attrs, + .iot_index = ISNS_TAG_PG_INDEX, + .iot_next_index = ISNS_TAG_PG_NEXT_INDEX, + .iot_container = &isns_entity_template, + .iot_relation_type = ISNS_RELATION_PORTAL_GROUP, + .iot_build_relation = isns_db_build_pg_relation, +}; + diff --git a/utils/open-isns/query.c b/utils/open-isns/query.c new file mode 100644 index 0000000..b2cfbc9 --- /dev/null +++ b/utils/open-isns/query.c @@ -0,0 +1,238 @@ +/* + * Handle iSNS Device Attribute Query + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "message.h" +#include "security.h" +#include "objects.h" +#include "db.h" +#include "util.h" + +/* + * Create a query, and set the source name + */ +static isns_simple_t * +__isns_create_query(isns_source_t *source, const isns_attr_list_t *key) +{ + return isns_simple_create(ISNS_DEVICE_ATTRIBUTE_QUERY, source, key); +} + +isns_simple_t * +isns_create_query(isns_client_t *clnt, const isns_attr_list_t *key) +{ + return __isns_create_query(clnt->ic_source, key); +} + +isns_simple_t * +isns_create_query2(isns_client_t *clnt, const isns_attr_list_t *key, isns_source_t *source) +{ + return __isns_create_query(source?: clnt->ic_source, key); +} + +int +isns_query_request_attr_tag(isns_simple_t *qry, uint32_t tag) +{ + isns_attr_list_append_nil(&qry->is_operating_attrs, tag); + return ISNS_SUCCESS; +} + +int +isns_query_request_attr(isns_simple_t *qry, isns_attr_t *attr) +{ + if (!ISNS_ATTR_IS_NIL(attr)) { + isns_error("Query operating attribute must be NIL\n"); + return ISNS_INVALID_QUERY; + } + isns_attr_list_append_attr(&qry->is_operating_attrs, attr); + return ISNS_SUCCESS; +} + +static unsigned int +isns_query_get_requested_types(const isns_attr_list_t *attrs) +{ + unsigned int i, mask = 0; + + for (i = 0; i < attrs->ial_count; ++i) { + uint32_t tag = attrs->ial_data[i]->ia_tag_id; + isns_object_template_t *tmpl; + + tmpl = isns_object_template_find(tag); + /* Ignore unknown tags */ + if (tmpl == NULL) + continue; + + mask |= 1 << tmpl->iot_handle; + } + return mask; +} + +/* + * Get the list of objects matching this query + */ +static int +isns_query_get_objects(isns_simple_t *qry, isns_db_t *db, isns_object_list_t *result) +{ + isns_scope_t *scope = NULL; + isns_object_list_t matching = ISNS_OBJECT_LIST_INIT; + isns_attr_list_t *keys = &qry->is_message_attrs; + isns_object_template_t *query_type = NULL; + unsigned int i, qry_mask = 0; + int status; + + /* 5.6.5.2 + * If multiple attributes are used as the Message Key, then they + * MUST all be from the same object type (e.g., IP address and + * TCP/UDP Port are attributes of the Portal object type). + */ + for (i = 0; i < keys->ial_count; ++i) { + isns_object_template_t *tmpl; + uint32_t tag = keys->ial_data[i]->ia_tag_id; + + tmpl = isns_object_template_for_tag(tag); + if (tmpl == NULL) + return ISNS_ATTRIBUTE_NOT_IMPLEMENTED; + if (query_type == NULL) + query_type = tmpl; + else if (tmpl != query_type) + return ISNS_INVALID_QUERY; + } + + /* + * 5.6.5.2 + * An empty Message Key field indicates the query is scoped to + * the entire database accessible by the source Node. + */ + if (keys->ial_count == 0) { + query_type = &isns_entity_template; + keys = NULL; + } + + /* Policy: check whether the client is allowed to + * query this type of object. */ + if (!isns_policy_validate_object_type(qry->is_policy, + query_type, qry->is_function)) + return ISNS_SOURCE_UNAUTHORIZED; + + /* No scope means that the source is not part of + * any discovery domain, and there's no default DD. + * Just return an empty reply. */ + scope = isns_scope_for_call(db, qry); + if (scope == NULL) + return ISNS_SUCCESS; + + status = isns_scope_gang_lookup(scope, query_type, keys, &matching); + if (status != ISNS_SUCCESS) + goto out; + + /* Extract the mask of requested objects */ + qry_mask = isns_query_get_requested_types(&qry->is_operating_attrs); + + /* + * 5.6.5.2 + * The DevAttrQry response message returns attributes of objects + * listed in the Operating Attributes that are related to the + * Message Key of the original DevAttrQry message. + */ + for (i = 0; i < matching.iol_count; ++i) { + isns_object_t *obj = matching.iol_data[i]; + + if (!isns_policy_validate_object_access(qry->is_policy, + qry->is_source, obj, + qry->is_function)) + continue; + + if (obj->ie_container) + isns_object_list_append(result, obj->ie_container); + isns_object_list_append(result, obj); + isns_scope_get_related(scope, obj, qry_mask, result); + } + +out: + isns_object_list_destroy(&matching); + isns_scope_release(scope); + return status; +} + +/* + * Create a Query Response + */ +static isns_simple_t * +isns_create_query_response(isns_server_t *srv, + const isns_simple_t *qry, const isns_object_list_t *objects) +{ + const isns_attr_list_t *req_attrs = NULL; + isns_simple_t *resp; + unsigned int i; + + resp = __isns_create_query(srv->is_source, &qry->is_message_attrs); + + /* + * 5.7.5.2. + * If no Operating Attributes are included in the original + * query, then all Operating Attributes SHALL be returned + * in the response. + */ + if (qry->is_operating_attrs.ial_count != 0) + req_attrs = &qry->is_operating_attrs; + + for (i = 0; i < objects->iol_count; ++i) { + isns_object_t *obj = objects->iol_data[i]; + + if (obj->ie_rebuild) + obj->ie_rebuild(obj, srv->is_db); + isns_object_get_attrlist(obj, + &resp->is_operating_attrs, + req_attrs); + } + return resp; +} + +int +isns_process_query(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_simple_t *reply = NULL; + isns_db_t *db = srv->is_db; + int status; + + /* Get the objects matching the query */ + status = isns_query_get_objects(call, db, &objects); + if (status != ISNS_SUCCESS) + goto done; + + /* Success: build the response */ + reply = isns_create_query_response(srv, call, &objects); + if (reply == NULL) { + status = ISNS_INTERNAL_ERROR; + goto done; + } + + /* There's nothing in the spec that tells us what to + * return if the query matches no object. + */ + if (objects.iol_count == 0) { + status = ISNS_NO_SUCH_ENTRY; + goto done; + } + +done: + isns_object_list_destroy(&objects); + *result = reply; + return status; +} + +/* + * Parse the list of objects in a query response + */ +int +isns_query_response_get_objects(isns_simple_t *qry, + isns_object_list_t *result) +{ + return isns_simple_response_get_objects(qry, result); +} diff --git a/utils/open-isns/register.c b/utils/open-isns/register.c new file mode 100644 index 0000000..120deae --- /dev/null +++ b/utils/open-isns/register.c @@ -0,0 +1,934 @@ +/* + * Handle iSNS Device Attribute Registration + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "attrs.h" +#include "objects.h" +#include "message.h" +#include "security.h" +#include "util.h" +#include "db.h" + + +static int isns_create_default_pgs_for_object(isns_db_t *, isns_object_t *); + +/* + * Create a registration, and set the source name + */ +static isns_simple_t * +__isns_create_registration(isns_source_t *source, isns_object_t *key_obj) +{ + isns_simple_t *reg; + + reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, source, NULL); + if (reg == NULL) + return NULL; + + /* + * When sending a registration, you can either specify + * the object to be modified in the key attrs, or leave + * the key empty. + */ + if (key_obj == NULL) + return reg; + + /* User gave us a key object. We need to put the key + * attributes into the message attrs, and *all* attrs + * into the operating attrs. */ + if (!isns_object_extract_keys(key_obj, ®->is_message_attrs)) { + /* bummer - seems the object is missing some + * vital organs. */ + isns_warning("%s: object not fully specified, key attrs missing\n", + __FUNCTION__); + goto failed; + } + + /* + * The Message Key identifies the object the DevAttrReg message + * acts upon. [...] The key attribute(s) identifying this object + * MUST also be included among the Operating Attributes. + * + * We do not enforce this here, we rely on the caller to get this + * right. + */ +#if 0 + if (!isns_object_extract_all(key_obj, ®->is_operating_attrs)) { + isns_warning("%s: unable to extract attrs from key objects\n", + __FUNCTION__); + goto failed; + } +#endif + + return reg; + +failed: + isns_simple_free(reg); + return NULL; +} + +isns_simple_t * +isns_create_registration(isns_client_t *clnt, isns_object_t *key_obj) +{ + return __isns_create_registration(clnt->ic_source, key_obj); +} + +isns_simple_t * +isns_create_registration2(isns_client_t *clnt, isns_object_t *key_obj, + isns_source_t *source) +{ + return __isns_create_registration(source?: clnt->ic_source, key_obj); +} + +/* + * Set the replace flag + */ +void +isns_registration_set_replace(isns_simple_t *reg, int replace) +{ + reg->is_replace = !!replace; +} + +/* + * Add an object to the registration + */ +void +isns_registration_add_object(isns_simple_t *reg, isns_object_t *obj) +{ + isns_object_extract_writable(obj, ®->is_operating_attrs); +} + +void +isns_registration_add_object_list(isns_simple_t *reg, isns_object_list_t *list) +{ + unsigned int i; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_extract_writable(list->iol_data[i], + ®->is_operating_attrs); + } +} + +/* + * Get the key object given in this message + * + * It doesn't say anywhere explicitly in the RFC, but + * the message key can contain both key and non-key + * attributes. For instance, you can search by + * Portal Group Index (section 3.4). + */ +static int +isns_registration_get_key(isns_simple_t *reg, isns_db_t *db, isns_object_t **key_obj) +{ + isns_attr_list_t *keys = ®->is_message_attrs; + isns_attr_list_t dummy_keys = ISNS_ATTR_LIST_INIT; + isns_attr_t *attr; + isns_object_t *obj = NULL; + const char *eid = NULL; + char eidbuf[128]; + int status = ISNS_SUCCESS; + int obj_must_exist = 0; + + /* + * 5.6.5.1 + * If the Message Key is not present, then the DevAttrReg message + * implicitly registers a new Network Entity. In this case, + * the replace bit SHALL be ignored; a new Network Entity SHALL + * be created. + * + * Note that some clients seem to leave the message key + * empty, but hide the entity identifier in the operating + * attrs. + */ + if (keys->ial_count != 0) { + attr = keys->ial_data[0]; + + /* + * 5.6.5.1 + * If the Message Key does not contain an EID, and no + * pre-existing objects match the Message Key, then the + * DevAttrReg message SHALL be rejected with a status + * code of 3 (Invalid Registration). + */ + if (keys->ial_count != 1 + || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER) + obj_must_exist = 1; + } else { + /* Empty message key. But the client may have hidden + * the EID in the operating attrs :-/ + */ + if (reg->is_operating_attrs.ial_count == 0) + goto create_entity; + + attr = reg->is_operating_attrs.ial_data[0]; + if (attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER) + goto create_entity; + + isns_attr_list_append_attr(&dummy_keys, attr); + keys = &dummy_keys; + } + + /* If the caller specifies an EID, extract it while + * we know what we're doing :-) */ + if (attr->ia_tag_id == ISNS_TAG_ENTITY_IDENTIFIER + && ISNS_ATTR_IS_STRING(attr)) + eid = attr->ia_value.iv_string; + + /* Look up the object identified by the keys. + * We do not scope the lookup, as the client + * may want to add nodes to an entity that's + * currently empty - and hence not visible to + * any DD. */ + if (!ISNS_ATTR_IS_NIL(attr)) + obj = isns_db_lookup(db, NULL, keys); + + if (obj == NULL && obj_must_exist) + goto err_invalid; + + if (obj != NULL) { + /* + * Policy: verify that the client is permitted + * to access this object. + * + * This includes + * - the client node must be the object owner, + * or a control node. + * - the policy must allow modification of + * this object type. + */ + if (!isns_policy_validate_object_access(reg->is_policy, + reg->is_source, + obj, reg->is_function)) + goto err_unauthorized; + +found_object: + if (reg->is_replace) { + isns_object_t *container = NULL; + + if (!ISNS_IS_ENTITY(obj)) { + container = isns_object_get_entity(obj); + if (container == NULL) { + isns_error("Trying to replace %s (id %u) " + "which has no container\n", + obj->ie_template->iot_name, + obj->ie_index); + goto err_invalid; + } + } + + isns_debug_state("Replacing %s (id %u)\n", + obj->ie_template->iot_name, + obj->ie_index); + isns_db_remove(db, obj); + isns_object_release(obj); + + /* Purge the deleted objects from the database now */ + isns_db_purge(db); + + /* We need to flush pending SCNs because the + * objects may be resurrected from limbo, + * and we might be looking at stale data. */ + isns_scn_transmit_all(); + + /* It's an entity. Nuke it and create + * a new one. */ + if (container == NULL) { + isns_source_set_entity(reg->is_source, NULL); + goto create_entity; + } + + obj = isns_object_get(container); + } + + goto out; + } + + /* + * If the Message Key contains an EID and no pre-existing objects + * match the Message Key, then the DevAttrReg message SHALL create a + * new Entity with the specified EID and any new object(s) specified + * by the Operating Attributes. The replace bit SHALL be ignored. + * + * Implementer's note: the EID attribute may be empty, in which case + * we also create a new entity. + */ + +create_entity: + if (!isns_policy_validate_object_creation(reg->is_policy, + reg->is_source, + &isns_entity_template, keys, NULL, + reg->is_function)) + goto err_unauthorized; + + /* + * 5.6.5.1 + * A registration message that creates a new Network Entity object + * MUST contain at least one Portal or one Storage Node. If the + * message does not, then it SHALL be considered invalid and result + * in a response with Status Code of 3 (Invalid Registration). + */ + /* FIXME: Implement this check */ + + /* We try to play nice with lazy clients and attempt to + * look up the network entity given the source name. + * But we don't do this if a non-NULL EID was given, + * because the client may explicitly want to specify more + * than one Network Entity. + */ + if (eid == NULL) { + obj = reg->is_source->is_entity; + if (obj != NULL) { + isns_object_get(obj); + goto found_object; + } + + /* The policy may define a default entity name. + * If that is the case, use it. + */ + eid = isns_policy_default_entity(reg->is_policy); + if (eid) { + obj = isns_db_vlookup(db, &isns_entity_template, + ISNS_TAG_ENTITY_IDENTIFIER, eid, + 0); + if (obj) { + reg->is_source->is_entity = isns_object_get(obj); + goto found_object; + } + } + } + + /* + * 5.6.5.1 + * If the Message Key and Operating Attributes do not contain + * an EID attribute, or if the EID attribute has a length of 0, + * then a new Network Entity object SHALL be created and the iSNS + * server SHALL supply a unique EID value for it. + */ + if (eid == NULL) + eid = isns_db_generate_eid(db, eidbuf, sizeof(eidbuf)); + + /* + * 6.2.2. Entity Protocol + * + * This attribute is required during initial registration of + * the Network Entity. + * + * Implementer's note: we don't rely on this. Instead, the + * Entity Protocol is selected based on the source type. + * If the client specifies the protocol, the auto-selected + * value is overwritten. + */ + obj = isns_create_entity_for_source(reg->is_source, eid); + if (obj == NULL) + goto err_invalid; + + isns_source_set_entity(reg->is_source, obj); + + /* + * 6.2.6 + * If a Registration Period is not requested by the iSNS + * client and Entity Status Inquiry (ESI) messages are not + * enabled for that client, then the Registration Period + * SHALL be set to a non-zero value by the iSNS server. + * This implementation-specific value for the Registration + * Period SHALL be returned in the registration response to the + * iSNS client. The Registration Period may be set to zero, + * indicating its non-use, only if ESI messages are enabled for + * that Network Entity. + * + * Implementer's note: we diverge from this in two ways: + * - the admin may choose to disable registration timeout, + * by setting RegistrationPeriod=0 in the config file + * + * - When a new entity is created, we always set the + * registration interval because we cannot know yet + * whether the client will subsequently enable ESI or + * not. + * + * - The control entity (holding policy objects) will + * not expire. + */ + if (isns_config.ic_registration_period + && strcasecmp(eid, ISNS_ENTITY_CONTROL)) { + isns_object_set_uint32(obj, + ISNS_TAG_REGISTRATION_PERIOD, + isns_config.ic_registration_period); + isns_object_set_uint64(obj, + ISNS_TAG_TIMESTAMP, + time(NULL)); + } + + /* Insert into database, and set the object's owner */ + isns_db_insert(db, obj); + + reg->is_replace = 0; + +out: + *key_obj = obj; + isns_attr_list_destroy(&dummy_keys); + return ISNS_SUCCESS; + +error: + if (obj) + isns_object_release(obj); + isns_attr_list_destroy(&dummy_keys); + return status; + +err_unauthorized: + status = ISNS_SOURCE_UNAUTHORIZED; + goto error; + +err_invalid: + status = ISNS_INVALID_REGISTRATION; + goto error; +} + +static int +isns_registration_get_next_object(isns_db_t *db, + struct isns_attr_list_scanner *st, + isns_object_list_t *result) +{ + isns_object_t *current; + int status, esi = 0; + + status = isns_attr_list_scanner_next(st); + /* We get here if the registration has a trailing PGT */ + if (status == ISNS_NO_SUCH_ENTRY) + return ISNS_SUCCESS; + if (status) + return status; + + /* + * Validate the attrlist. + * This makes sure the client does not include + * duplicate attributes, readonly attributes + * such as Registration Timestamp, Index and Next Index, + * or privileged data (such as marking a storage node as + * control node). + */ + status = isns_attr_list_validate(&st->attrs, + st->policy, + ISNS_DEVICE_ATTRIBUTE_REGISTER); + if (status) { + isns_debug_protocol("invalid attr in message\n"); + return status; + } + + /* + * 6.3.4. Entity Status Inquiry Interval + * + * If the iSNS server is unable to support ESI messages + * or the ESI Interval requested, it SHALL [...] reject + * the ESI request by returning an "ESI Not Available" + * Status Code [...] + * + * Implementer's note: In section 5.7.5.1, the RFC talks + * about modifying the requested ESI interval; so it seems + * it's okay to be liberal about the ESI intervals we accept, + * and update them quietly. + */ + if (isns_attr_list_contains(&st->attrs, ISNS_TAG_ESI_PORT)) { + if (!isns_esi_enabled) { + isns_debug_esi("Refusing to accept portal " + "registration with ESI port\n"); + return ISNS_ESI_NOT_AVAILABLE; + } + esi = 1; + } + + /* + * Override any registration period specified by the client. + */ + if (isns_attr_list_contains(&st->attrs, ISNS_TAG_REGISTRATION_PERIOD)) { + isns_value_t value = ISNS_VALUE_INIT(uint32, + isns_config.ic_registration_period); + + isns_attr_list_update_value(&st->attrs, + ISNS_TAG_REGISTRATION_PERIOD, NULL, + &value); + } + + if (st->tmpl == &isns_entity_template) { + /* + * 5.6.5.1. + * A maximum of one Network Entity object can be + * created or updated with a single DevAttrReg + * message. Consequently, the Operating Attributes + * MUST NOT contain more than one Network Entity + * object. + */ + if (st->entities++) { + isns_debug_protocol("More than one entity in DevAttrReg msg\n"); + return ISNS_INVALID_REGISTRATION; + } + + /* This should be the key object. + * The EID specified by by the client may be + * empty, so don't overwrite the value we + * assigned with something else. + */ + if (!isns_object_match(st->key_obj, &st->keys)) { + isns_debug_protocol("Entity mismatch in message vs. operating attrs\n"); + return ISNS_INVALID_REGISTRATION; + } + current = isns_object_get(st->key_obj); + } else + if (st->tmpl == &isns_dd_template || st->tmpl == &isns_ddset_template) { + isns_debug_protocol("DevAttrReg of type %s not allowed\n", + st->tmpl->iot_name); + return ISNS_INVALID_REGISTRATION; + } else { + /* This will also catch objects in limbo. */ + current = isns_db_lookup(db, st->tmpl, &st->keys); + } + + if (current != NULL) { + /* + * If the replace bit is not set, then the message updates + * the attributes of the object identified by the Message Key + * and its subordinate objects. Existing object containment + * relationships MUST NOT be changed. For existing objects, + * key attributes MUST NOT be modified, but new subordinate + * objects MAY be added. + */ + + /* + * [...] + * If the Node identified by the Source Attribute is + * not a Control Node, then the objects in the operating + * attributes MUST be members of the same Network Entity + * as the Source Node. + */ + if (!isns_policy_validate_object_update(st->policy, + st->source, current, &st->attrs, + ISNS_DEVICE_ATTRIBUTE_REGISTER)) { + isns_object_release(current); + return ISNS_SOURCE_UNAUTHORIZED; + } + + /* We shouldn't allow messages affecting one Entity + * to modify objects owned by a different Entity. + * + * However, there may be orphan objects (created + * while populating discovery domains). These will + * not be associated with any Network Entity, so + * they're up for grabs. + */ + if (st->key_obj == current + || st->key_obj == current->ie_container) { + /* All is well. The current object is the + * key object itself, or a direct descendant of the + * key object. */ + /* FIXME: with FC we can get deeper nesting; + * this needs work. */ + } else + if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) { + isns_error("Client attempts to add %s object to a %s - tsk tsk.\n", + st->tmpl->iot_name, + st->key_obj->ie_template->iot_name); + goto invalid_registration; + } else if (current->ie_container) { + /* We shouldn't get here in authenticated mode, + * but in insecure mode we still may. */ + isns_error("Client attempts to move %s %u to a different %s\n", + current->ie_template->iot_name, + current->ie_index, + st->key_obj->ie_template->iot_name); + goto invalid_registration; + } + } else { + if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) { + isns_error("Client attempts to add %s object to a %s - tsk tsk.\n", + st->tmpl->iot_name, + st->key_obj->ie_template->iot_name); + goto invalid_registration; + } + + if (!isns_policy_validate_object_creation(st->policy, + st->source, st->tmpl, + &st->keys, &st->attrs, + ISNS_DEVICE_ATTRIBUTE_REGISTER)) { + return ISNS_SOURCE_UNAUTHORIZED; + } + current = isns_create_object(st->tmpl, &st->keys, + isns_object_get_entity(st->key_obj)); + + /* We do not insert the new object into the database yet. + * That happens after we're done with parsing *all* + * objects. */ + } + + if (!isns_object_set_attrlist(current, &st->attrs)) { + isns_debug_state("Error updating object's attrlist\n"); + isns_object_release(current); + return ISNS_INTERNAL_ERROR; + } + + /* If the client specifies an ESI port, make sure the + * ESI interval is set and within bounds. */ + if (esi) { + uint32_t esi_interval; + + if (!isns_object_get_uint32(current, + ISNS_TAG_ESI_INTERVAL, &esi_interval)) { + esi_interval = isns_config.ic_esi_min_interval; + } else + if (esi_interval < isns_config.ic_esi_min_interval) { + esi_interval = isns_config.ic_esi_min_interval; + } else + if (esi_interval > isns_config.ic_esi_max_interval) { + esi_interval = isns_config.ic_esi_max_interval; + } else { + esi_interval = 0; + } + + if (esi_interval) + isns_object_set_uint32(current, + ISNS_TAG_ESI_INTERVAL, esi_interval); + } + + /* Append it to the result list. + * We do not return the key object, otherwise + * we end up putting it into the response twice. + */ + if (current != st->key_obj) + isns_object_list_append(result, current); + + /* + * When a Portal is registered, the Portal attributes MAY immediately be + * followed by a PGT attribute. + * [...] + * When an iSCSI Storage Node is registered, the Storage Node attributes + * MAY immediately be followed by a PGT attribute. + */ + if (st->tmpl == &isns_portal_template + || st->tmpl == &isns_iscsi_node_template) { + st->pgt_next_attr = ISNS_TAG_PG_TAG; + st->pgt_base_object = current; + } else if (st->tmpl != &isns_iscsi_pg_template) { + st->pgt_next_attr = 0; + st->pgt_base_object = NULL; + } + + isns_object_release(current); + return ISNS_SUCCESS; + +invalid_registration: + if (current) + isns_object_release(current); + return ISNS_INVALID_REGISTRATION; +} + +/* + * Extract the list of objects to be registered from + * the list of operating attributes. + */ +static int +isns_registration_get_objects(isns_simple_t *reg, isns_db_t *db, + isns_object_t *key_obj, + isns_object_list_t *result) +{ + struct isns_attr_list_scanner state; + int status = ISNS_SUCCESS; + + isns_attr_list_scanner_init(&state, key_obj, ®->is_operating_attrs); + state.source = reg->is_source; + state.policy = reg->is_policy; + + /* + * 5.6.4. + * The ordering of Operating Attributes in the message is + * important for determining the relationships among objects + * and their ownership of non-key attributes. iSNS protocol + * messages that violate these ordering rules SHALL be rejected + * with the Status Code of 2 (Message Format Error). + */ + /* FIXME: Implement this check */ + + while (state.pos < state.orig_attrs.ial_count) { + status = isns_registration_get_next_object(db, + &state, result); + + if (status) + break; + } + + isns_attr_list_scanner_destroy(&state); + return status; +} + +/* + * 5.6.5.1 + * New PG objects are registered when an associated Portal or + * iSCSI Node object is registered. An explicit PG object + * registration MAY follow a Portal or iSCSI Node object + * registration in a DevAttrReg message. + * [...] + * If the PGT value is not included in the Storage Node or + * Portal object registration, and if a PGT value was not + * previously registered for the relationship, then the PGT for + * the corresponding PG object SHALL be registered with a value + * of 0x00000001. + */ +static int +isns_create_registration_pgs(isns_db_t *db, + const isns_object_list_t *list) +{ + unsigned int i, num_created = 0; + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + + if (ISNS_IS_ISCSI_NODE(obj) || ISNS_IS_PORTAL(obj)) + num_created += isns_create_default_pgs_for_object(db, obj); + } + return num_created; +} + +static int +isns_create_default_pgs_for_object(isns_db_t *db, isns_object_t *this) +{ + isns_object_template_t *match_tmpl; + isns_object_t *entity; + unsigned int i, num_created = 0; + + if (ISNS_IS_ISCSI_NODE(this)) + match_tmpl = &isns_portal_template; + else + match_tmpl = &isns_iscsi_node_template; + + entity = isns_object_get_entity(this); + for (i = 0; i < entity->ie_children.iol_count; ++i) { + isns_object_t *that = entity->ie_children.iol_data[i], *pg; + + if (that->ie_template != match_tmpl) + continue; + + /* Create the portal group if it does not + * exist. + * Note: we do not return these implicitly + * created portal groups - that's a matter + * of sheer laziness. We would have to + * splice these into the list in the + * appropriate location, and I guess it's + * not really worth the hassle. + */ + if (ISNS_IS_ISCSI_NODE(this)) + pg = isns_create_default_portal_group(db, that, this); + else + pg = isns_create_default_portal_group(db, this, that); + + /* There already is a PG linking these two + * objects. */ + if (pg == NULL) + continue; + + isns_db_insert(db, pg); + + isns_debug_message("--Created default PG:--\n"); + isns_object_print(pg, isns_debug_message); + + isns_object_release(pg); + num_created++; + } + + return num_created; +} + +/* + * Commit all changes to the DB + */ +static int +isns_commit_registration(isns_db_t *db, isns_object_t *key_obj, isns_object_list_t *list) +{ + unsigned int i; + + /* + * If there are any Portal Groups in this registration, build + * the relationship handle: + * + * 3.4 + * A new PG object can only be registered by referencing + * its associated iSCSI Storage Node or Portal object. + * A pre-existing PG object can be modified or queried + * by using its Portal Group Index as message key, or + * by referencing its associated iSCSI Storage Node or + * Portal object. + * + * Implementation note: isns_db_create_pg_relation + * checks whether the referenced node and portal exist, + * and belong to the same entity as the PG. If this is + * not the case, NULL is returned, and no relation is + * defined. + */ + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + isns_object_template_t *tmpl; + + tmpl = obj->ie_template; + if (tmpl->iot_build_relation && !obj->ie_relation + && !tmpl->iot_build_relation(db, obj, list)) { + isns_debug_protocol("Unable to build relation for new %s\n", + tmpl->iot_name); + return ISNS_INVALID_REGISTRATION; + } + } + + for (i = 0; i < list->iol_count; ++i) { + isns_object_t *obj = list->iol_data[i]; + isns_object_template_t *tmpl; + + tmpl = obj->ie_template; + if (key_obj != obj && !obj->ie_container) { + if (!isns_object_attach(obj, key_obj)) { + /* This should not fail any longer */ + isns_debug_protocol("Unable to attach %s %u to %s\n", + tmpl->iot_name, obj->ie_index, + key_obj->ie_template->iot_name); + return ISNS_INVALID_REGISTRATION; + } + } + + if (obj->ie_state != ISNS_OBJECT_STATE_MATURE) + isns_db_insert(db, obj); + } + + return ISNS_SUCCESS; +} + +/* + * Process a registration + */ +int +isns_process_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; + isns_simple_t *reply = NULL; + isns_object_t *key_obj = NULL; + isns_db_t *db = srv->is_db; + int status; + unsigned int i; + + /* + * 5.6.1 + * For messages that change the contents of the iSNS database, + * the iSNS server MUST verify that the Source Attribute + * identifies either a Control Node or a Storage Node that is + * a part of the Network Entity containing the added, deleted, + * or modified objects. + * + * This check happens in isns_registration_get_key by calling + * isns_policy_validate_object_access. + */ + + /* Get the key object (usually a Network Entity) */ + status = isns_registration_get_key(call, db, &key_obj); + if (status) + goto done; + + /* Get the objects to register */ + status = isns_registration_get_objects(call, db, key_obj, &objects); + if (status != ISNS_SUCCESS) + goto done; + + /* We parsed the request alright; all semantic checks passed. + * Now insert the modified/new objects. + * We do this in two passes, by first committing all nodes and + * portals, and then committing the portal groups. + */ + status = isns_commit_registration(db, key_obj, &objects); + if (status != ISNS_SUCCESS) + goto done; + + /* The client may have registered a bunch of storage nodes, + * and created an entity in the process. However, there's the + * odd chance that the source node name it used was not + * registered. However, we need to be able to later find + * the entity it registered based on its source name. + * So we implicitly create a dummy storage node with the given + * source name and attach it. + */ +#if 1 + if (ISNS_IS_ENTITY(key_obj) + && !isns_source_set_node(call->is_source, db)) { + isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT; + isns_source_t *source = call->is_source; + isns_object_t *obj; + + isns_attr_list_append_attr(&attrs, isns_source_attr(source)); + isns_attr_list_append_uint32(&attrs, + ISNS_TAG_ISCSI_NODE_TYPE, + 0); + obj = isns_create_object(&isns_iscsi_node_template, + &attrs, key_obj); + if (obj) { + isns_db_insert(db, obj); + } else { + isns_warning("Unable to create dummy storage node " + "for source %s\n", + isns_source_name(source)); + } + isns_attr_list_destroy(&attrs); + } +#endif + + /* + * 5.6.5.1 + * New PG objects are registered when an associated Portal or + * iSCSI Node object is registered. An explicit PG object + * registration MAY follow a Portal or iSCSI Node object + * registration in a DevAttrReg message. + * [...] + * If the PGT value is not included in the Storage Node or + * Portal object registration, and if a PGT value was not + * previously registered for the relationship, then the PGT for + * the corresponding PG object SHALL be registered with a value + * of 0x00000001. + */ + isns_create_registration_pgs(db, &objects); + + /* Success: create a new registration message, and + * send it in our reply. */ + reply = __isns_create_registration(srv->is_source, key_obj); + if (reply == NULL) { + status = ISNS_INTERNAL_ERROR; + goto done; + } + + /* If the key object was modified (or created) + * include it in the response. + * We really ought to restrict ourselves to the + * key attrs plus those that were modified by this + * registration. But right now have no way of + * finding out. + */ + if (key_obj->ie_flags & ISNS_OBJECT_DIRTY) + isns_registration_add_object(reply, key_obj); + + for (i = 0; i < objects.iol_count; ++i) { + isns_registration_add_object(reply, + objects.iol_data[i]); + } + + +done: + isns_object_list_destroy(&objects); + isns_object_release(key_obj); + *result = reply; + return status; +} + +/* + * Extract the list of objects from the DevAttrReg response + */ +int +isns_registration_response_get_objects(isns_simple_t *reg, + isns_object_list_t *result) +{ + return isns_simple_response_get_objects(reg, result); +} diff --git a/utils/open-isns/relation.c b/utils/open-isns/relation.c new file mode 100644 index 0000000..caac38b --- /dev/null +++ b/utils/open-isns/relation.c @@ -0,0 +1,281 @@ +/* + * iSNS object relationships + * + * Relations are used to express a connection between two + * objects. Currently, two relationship types are implemented: + * + * - portal group: this relates a storage node and a portal + * - visibility: this relates a nodes nodes that share a + * common discovery domain. + * + * Relation objects are nice for portals groups, but kind of + * awkward for DDs. A better way of expressing DD membership + * (which also allows for a fast visibility check) could be + * to store a [bit] vector of DD IDs in each storage node. + * A visibility check would amount to just doing the bitwise + * AND of two vectors, and checking for NULL. The only thing + * to take care of would be to make sure a DD object takes a + * reference on its members (this is necessary so that objects + * maintain their ID/name associations even when removed from + * the database). + * + * Aug 22 2007 - changed DD code to use bit vectors. A lot + * of code in this file is now obsolete. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <stdarg.h> + +#include "isns.h" +#include "objects.h" +#include "util.h" +#include "db.h" + +struct isns_relation_soup { + /* For now, use one plain list. For better + * scalability, we'll need a hash table or + * something similar later. */ + isns_relation_list_t irs_data; +}; + +static void isns_relation_list_append(isns_relation_list_t *, + isns_relation_t *); +static int isns_relation_list_remove(isns_relation_list_t *, + isns_relation_t *); + +isns_relation_soup_t * +isns_relation_soup_alloc(void) +{ + return isns_calloc(1, sizeof(isns_relation_soup_t)); +} + +void +isns_relation_add(isns_relation_soup_t *soup, + isns_relation_t *rp) +{ + isns_relation_list_append(&soup->irs_data, rp); +} + +isns_relation_t * +isns_relation_find_edge(isns_relation_soup_t *soup, + const isns_object_t *left, + const isns_object_t *right, + unsigned int relation_type) +{ + isns_relation_list_t *list = &soup->irs_data; + unsigned int i; + + for (i = 0; i < list->irl_count; ++i) { + isns_relation_t *rp = list->irl_data[i]; + + if (rp->ir_type != relation_type) + continue; + if (rp->ir_subordinate[0].obj == left + && rp->ir_subordinate[1].obj == right) + return rp; + if (rp->ir_subordinate[0].obj == right + && rp->ir_subordinate[1].obj == left) + return rp; + } + return NULL; +} + +void +isns_relation_get_edge_objects(isns_relation_soup_t *soup, + const isns_object_t *left, + unsigned int relation_type, + isns_object_list_t *result) +{ + isns_relation_list_t *list = &soup->irs_data; + unsigned int i; + + for (i = 0; i < list->irl_count; ++i) { + isns_relation_t *rp = list->irl_data[i]; + + if (rp->ir_type != relation_type) + continue; + if (rp->ir_object == NULL) + continue; + if (rp->ir_subordinate[0].obj == left + || rp->ir_subordinate[1].obj == left) { + isns_object_list_append(result, + rp->ir_object); + } + } +} + + + +void +isns_relation_halfspace(isns_relation_soup_t *soup, + const isns_object_t *left, + unsigned int relation_type, + isns_object_list_t *result) +{ + isns_relation_list_t *list = &soup->irs_data; + unsigned int i; + + for (i = 0; i < list->irl_count; ++i) { + isns_relation_t *rp = list->irl_data[i]; + + if (rp->ir_type != relation_type) + continue; + if (rp->ir_subordinate[0].obj == left) { + isns_object_list_append(result, + rp->ir_subordinate[1].obj); + } else + if (rp->ir_subordinate[1].obj == left) { + isns_object_list_append(result, + rp->ir_subordinate[0].obj); + } + } +} + +int +isns_relation_exists(isns_relation_soup_t *soup, + const isns_object_t *relating_object, + const isns_object_t *left, + const isns_object_t *right, + unsigned int relation_type) +{ + isns_relation_list_t *list = &soup->irs_data; + unsigned int i; + + for (i = 0; i < list->irl_count; ++i) { + isns_relation_t *rp = list->irl_data[i]; + + if (rp->ir_type != relation_type) + continue; + if (rp->ir_object != relating_object) + continue; + if (rp->ir_subordinate[0].obj == left + && rp->ir_subordinate[1].obj == right) + return 1; + if (rp->ir_subordinate[0].obj == right + && rp->ir_subordinate[1].obj == left) + return 1; + } + return 0; +} + +isns_object_t * +isns_relation_get_other(const isns_relation_t *rp, + const isns_object_t *this) +{ + if (rp->ir_subordinate[0].obj == this) + return rp->ir_subordinate[1].obj; + if (rp->ir_subordinate[1].obj == this) + return rp->ir_subordinate[0].obj; + return NULL; +} + +void +isns_relation_remove(isns_relation_soup_t *soup, + isns_relation_t *rp) +{ + isns_object_release(rp->ir_object); + rp->ir_object = NULL; + + isns_relation_list_remove(&soup->irs_data, rp); +} + +isns_relation_t * +isns_create_relation(isns_object_t *relating_object, + unsigned int relation_type, + isns_object_t *subordinate_object1, + isns_object_t *subordinate_object2) +{ + isns_relation_t *rp; + + rp = isns_calloc(1, sizeof(*rp)); + rp->ir_type = relation_type; + rp->ir_users = 1; + rp->ir_object = isns_object_get(relating_object); + isns_object_reference_set(&rp->ir_subordinate[0], subordinate_object1); + isns_object_reference_set(&rp->ir_subordinate[1], subordinate_object2); + +#if 0 + if (relating_object) { + relating_object->ie_relation = rp; + rp->ir_users++; + } +#endif + + return rp; +} + +void +isns_relation_sever(isns_relation_t *rp) +{ + isns_object_release(rp->ir_object); + rp->ir_object = NULL; + + isns_object_reference_drop(&rp->ir_subordinate[0]); + isns_object_reference_drop(&rp->ir_subordinate[1]); +} + +void +isns_relation_release(isns_relation_t *rp) +{ + if (--(rp->ir_users)) + return; + + isns_relation_sever(rp); + isns_free(rp); +} + +/* + * Check whether the relation references two dead/limbo objects. + * This is used for dead PG removal. + */ +int +isns_relation_is_dead(const isns_relation_t *rel) +{ + isns_object_t *left, *right; + + left = rel->ir_subordinate[0].obj; + right = rel->ir_subordinate[1].obj; + if ((left->ie_flags & ISNS_OBJECT_DEAD) + && (right->ie_flags & ISNS_OBJECT_DEAD)) + return 1; + + return 0; +} + +void +isns_relation_list_append(isns_relation_list_t *list, + isns_relation_t *rp) +{ + if ((list->irl_count % 128) == 0) { + list->irl_data = isns_realloc(list->irl_data, + (list->irl_count + 128) * sizeof(void *)); + if (list->irl_data == NULL) + isns_fatal("out of memory!\n"); + } + + list->irl_data[list->irl_count++] = rp; + rp->ir_users++; +} + +int +isns_relation_list_remove(isns_relation_list_t *list, + isns_relation_t *rp) +{ + unsigned int i, count = list->irl_count; + + for (i = 0; i < count; ++i) { + if (list->irl_data[i] != rp) + continue; + if (i < count - 1) + list->irl_data[i] = list->irl_data[count-1]; + isns_relation_release(rp); + list->irl_count -= 1; + return 1; + } + + return 0; +} diff --git a/utils/open-isns/scn.c b/utils/open-isns/scn.c new file mode 100644 index 0000000..d8fda1d --- /dev/null +++ b/utils/open-isns/scn.c @@ -0,0 +1,926 @@ +/* + * Handle SCN registration/deregistration/events + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "attrs.h" +#include "objects.h" +#include "message.h" +#include "security.h" +#include "util.h" +#include "db.h" + +typedef struct isns_scn isns_scn_t; +typedef struct isns_scn_funnel isns_scn_funnel_t; + +struct isns_scn { + isns_scn_t * scn_next; + char * scn_name; + isns_object_t * scn_entity; + isns_object_t * scn_owner; + isns_attr_t * scn_attr; + + isns_simple_t * scn_message; + isns_simple_t * scn_pending; + unsigned int scn_retries; + time_t scn_timeout; + uint16_t scn_xid; + + time_t scn_last_update; + isns_scn_funnel_t * scn_current_funnel; + isns_scn_funnel_t * scn_funnels; +}; + +struct isns_scn_funnel { + isns_scn_funnel_t * scn_next; + isns_portal_info_t scn_portal; + isns_socket_t * scn_socket; + unsigned int scn_bad; +}; + +static isns_server_t * isns_scn_server = NULL; +static isns_scn_t * isns_scn_list; + +static isns_scn_t * isns_scn_create_scn(isns_object_t *, uint32_t, isns_db_t *); +static void isns_scn_delete_scn(isns_object_t *); +static isns_scn_t * isns_scn_setup(isns_scn_t *, isns_object_t *); +static void isns_scn_callback(const isns_db_event_t *, void *); +static void isns_scn_free(isns_scn_t *); + +/* + * Initialize SCN machinery + */ +void +isns_scn_init(isns_server_t *srv) +{ + isns_db_t *db = srv->is_db; + isns_object_list_t nodes = ISNS_OBJECT_LIST_INIT; + isns_scn_t **tail; + unsigned int i; + + isns_scn_server = srv; + isns_register_callback(isns_scn_callback, db); + + /* Recover SCN state. */ + isns_db_gang_lookup(db, &isns_iscsi_node_template, NULL, &nodes); +#ifdef notyet + isns_db_gang_lookup(db, &isns_fc_node_template, NULL, &nodes); +#endif + + tail = &isns_scn_list; + for (i = 0; i < nodes.iol_count; ++i) { + isns_object_t *node = nodes.iol_data[i]; + isns_scn_t *scn; + + if (!node->ie_scn_mask) + continue; + + isns_debug_state("Recovering SCN state for %s %u\n", + node->ie_template->iot_name, + node->ie_index); + scn = isns_scn_setup(NULL, node); + if (scn) { + *tail = scn; + tail = &scn->scn_next; + } + } +} + +/* + * Support for SCNRegister calls + */ +isns_simple_t * +isns_create_scn_registration2(isns_client_t *clnt, unsigned int bitmap, isns_source_t *source) +{ + isns_simple_t *call; + + if (!source) + source = clnt->ic_source; + call = isns_simple_create(ISNS_SCN_REGISTER, source, NULL); + if (call) { + isns_attr_list_append_attr(&call->is_message_attrs, + isns_source_attr(source)); + isns_attr_list_append_uint32(&call->is_operating_attrs, + ISNS_TAG_ISCSI_SCN_BITMAP, + bitmap); + } + return call; +} + +isns_simple_t * +isns_create_scn_registration(isns_client_t *clnt, unsigned int bitmap) +{ + return isns_create_scn_registration2(clnt, bitmap, clnt->ic_source); +} + +/* + * Create an SCN + */ +isns_simple_t * +isns_create_scn(isns_source_t *source, isns_attr_t *nodeattr, isns_attr_t *tsattr) +{ + isns_simple_t *call; + + call = isns_simple_create(ISNS_STATE_CHANGE_NOTIFICATION, source, NULL); + if (call && nodeattr) + isns_attr_list_append_attr(&call->is_message_attrs, nodeattr); + if (call && tsattr) + isns_attr_list_append_attr(&call->is_message_attrs, tsattr); + return call; +} + +static void +isns_scn_add_event(isns_simple_t *call, uint32_t scn_bits, + const isns_object_t *obj, + const isns_object_t *dd) +{ + isns_attr_list_t *attrs = &call->is_message_attrs; + + isns_attr_list_append_uint32(attrs, + ISNS_TAG_ISCSI_SCN_BITMAP, + scn_bits); + isns_object_extract_keys(obj, attrs); + if (dd) + isns_object_extract_keys(dd, attrs); +} + +/* + * Process a SCN registration + */ +int +isns_process_scn_register(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_attr_list_t *keys = &call->is_message_attrs; + isns_attr_list_t *attrs = &call->is_operating_attrs; + isns_db_t *db = srv->is_db; + isns_attr_t *attr; + isns_object_t *node = NULL; + uint32_t scn_bitmap; + isns_scn_t *scn; + int status = ISNS_SUCCESS; + + /* + * 5.6.5.5 + * The SCNReg request PDU Payload contains a Source Attribute, a Message + * Key Attribute, and an Operating Attribute. Valid Message Key + * Attributes for a SCNReg are shown below: + * + * Valid Message Key Attributes for SCNReg + * --------------------------------------- + * iSCSI Name + * FC Port Name WWPN + */ + if (keys->ial_count != 1 || attrs->ial_count != 1) + return ISNS_SCN_REGISTRATION_REJECTED; + + attr = keys->ial_data[0]; + if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME && + attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) + return ISNS_SCN_REGISTRATION_REJECTED; + + /* Look up the storage node for this source. If it does + * not exist, reject the message. */ + node = isns_db_lookup(db, NULL, keys); + if (node == NULL) + return ISNS_SOURCE_UNKNOWN; + + /* + * Policy: verify that the client is permitted + * to access this entity. + * + * This includes + * - the client node must be the object owner, + * or a control node. + * - the policy must allow monitoring of + * this object type. + */ + if (!isns_policy_validate_object_access(call->is_policy, + call->is_source, + node, call->is_function)) + goto unauthorized; + + /* + * 5.6.5.5 + * The SCN Bitmap is the only operating attribute of this message + * [...] + * Control Nodes MAY conduct registrations for management SCNs; + * iSNS clients that are not supporting Control Nodes MUST NOT + * conduct registrations for management SCNs. + * + * Implementer's note: for iFCP sources, we should check for + * ISNS_TAG_IFCP_SCN_BITMAP. + */ + attr = attrs->ial_data[0]; + if (attr->ia_tag_id != ISNS_TAG_ISCSI_SCN_BITMAP + || !ISNS_ATTR_IS_UINT32(attr)) + goto rejected; + + scn_bitmap = attr->ia_value.iv_uint32; + if (!isns_policy_validate_scn_bitmap(call->is_policy, scn_bitmap)) + goto unauthorized; + + /* + * 5.6.5.5 + * If no SCN Port fields of any Portals of the Storage Node are + * registered to receive SCN messages, then the SCNReg message SHALL + * be rejected with Status Code 17 (SCN Registration Rejected). + */ + if (!(scn = isns_scn_create_scn(node, scn_bitmap, db))) + goto rejected; + + *result = isns_simple_create(ISNS_SCN_REGISTER, srv->is_source, NULL); + status = ISNS_SUCCESS; + +out: + if (node) + isns_object_release(node); + + return status; + +rejected: + status = ISNS_SCN_REGISTRATION_REJECTED; + goto out; + +unauthorized: + status = ISNS_SOURCE_UNAUTHORIZED; + goto out; +} + +/* + * Process a SCNDereg message + */ +int +isns_process_scn_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) +{ + isns_attr_list_t *keys = &call->is_message_attrs; + isns_db_t *db = srv->is_db; + isns_attr_t *attr; + isns_object_t *node = NULL; + int status = ISNS_SUCCESS; + + /* + * 5.6.5.6 + * The SCNDereg request message PDU Payload contains a Source Attribute + * and Message Key Attribute(s). Valid Message Key Attributes for a + * SCNDereg are shown below: + * + * Valid Message Key Attributes for SCNDereg + * ----------------------------------------- + * iSCSI Name + * FC Port Name WWPN + * + * There are no Operating Attributes in the SCNDereg message. + */ + + if (keys->ial_count != 1) + return ISNS_SCN_REGISTRATION_REJECTED; + + attr = keys->ial_data[0]; + if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME && + attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) + return ISNS_SCN_REGISTRATION_REJECTED; + + /* Look up the storage node for this source. If it does + * not exist, reject the message. */ + node = isns_db_lookup(db, NULL, keys); + if (node == NULL) + return ISNS_SUCCESS; + + /* + * Policy: verify that the client is permitted + * to access this entity. + * + * This includes + * - the client node must be the object owner, + * or a control node. + * - the policy must allow monitoring of + * this object type. + */ + if (!isns_policy_validate_object_access(call->is_policy, + call->is_source, + node, call->is_function)) + goto unauthorized; + + isns_object_set_scn_mask(node, 0); + isns_scn_delete_scn(node); + + *result = isns_simple_create(ISNS_SCN_DEREGISTER, srv->is_source, NULL); + status = ISNS_SUCCESS; + +out: + if (node) + isns_object_release(node); + + return status; + +unauthorized: + status = ISNS_SOURCE_UNAUTHORIZED; + goto out; +} + +/* + * Set up the SCN object. + */ +static isns_scn_t * +isns_scn_setup(isns_scn_t *scn, isns_object_t *node) +{ + isns_object_list_t portals = ISNS_OBJECT_LIST_INIT; + isns_object_t *entity; + unsigned int i; + + entity = isns_object_get_entity(node); + if (entity == NULL + || !isns_object_find_descendants(entity, + &isns_portal_template, NULL, &portals)) + return NULL; + + for (i = 0; i < portals.iol_count; ++i) { + isns_object_t *portal = portals.iol_data[i]; + isns_portal_info_t info; + isns_scn_funnel_t *funnel; + + /* Extract address and SCN port from portal */ + if (!isns_portal_from_object(&info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_SCN_PORT, + portal)) + continue; + + /* We know where to send our notifications! */ + if (scn == NULL) { + isns_attr_t *attr; + + if (!isns_object_get_attr(node, ISNS_TAG_ISCSI_NAME, &attr) + && !isns_object_get_attr(node, ISNS_TAG_FC_PORT_NAME_WWPN, &attr)) { + isns_error("Attempt to set up SCN for strange node type\n"); + return NULL; + } + + scn = isns_calloc(1, sizeof(*scn)); + scn->scn_entity = isns_object_get(entity); + scn->scn_owner = isns_object_get(node); + scn->scn_attr = isns_attr_get(attr); + scn->scn_name = isns_strdup(attr->ia_value.iv_string); + } + + funnel = isns_calloc(1, sizeof(*funnel)); + funnel->scn_portal = info; + funnel->scn_next = scn->scn_funnels; + scn->scn_funnels = funnel; + } + + isns_object_list_destroy(&portals); + return scn; +} + +/* + * See if an SCN object exists for the given target; + * if it doesn't, then create one. + */ +static isns_scn_t * +isns_scn_create_scn(isns_object_t *node, uint32_t bitmap, isns_db_t *db) +{ + isns_scn_t *scn; + + for (scn = isns_scn_list; scn; scn = scn->scn_next) { + if (scn->scn_owner == node) + goto done; + } + + /* Not found - create it */ + scn = isns_scn_setup(NULL, node); + if (scn == NULL) + return NULL; + + scn->scn_next = isns_scn_list; + isns_scn_list = scn; + +done: + /* We're all set - update the bitmap */ + isns_object_set_scn_mask(node, bitmap); + return scn; +} + +static void +isns_scn_delete_scn(isns_object_t *node) +{ + isns_scn_t *scn, **pos; + + pos = &isns_scn_list; + while ((scn = *pos) != NULL) { + if (scn->scn_owner == node) { + isns_debug_scn("Deregistering SCN for node %u\n", + node->ie_index); + *pos = scn->scn_next; + isns_scn_free(scn); + return; + } + pos = &scn->scn_next; + } +} + +static void +isns_scn_release_funnels(isns_scn_t *scn) +{ + isns_scn_funnel_t *funnel; + + while ((funnel = scn->scn_funnels) != NULL) { + scn->scn_funnels = funnel->scn_next; + if (funnel->scn_socket) + isns_socket_free(funnel->scn_socket); + isns_free(funnel); + } +} + +static void +isns_scn_free(isns_scn_t *scn) +{ + isns_scn_release_funnels(scn); + isns_object_release(scn->scn_owner); + isns_object_release(scn->scn_entity); + isns_attr_release(scn->scn_attr); + isns_free(scn->scn_name); + isns_free(scn); +} + +/* + * Check whether we should send an event to the target + */ +static inline int +isns_scn_match(isns_scn_t *scn, uint32_t event, + const isns_object_t *node, + uint32_t node_type) +{ + if (event == 0) + return 0; + + if (node->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK) + return event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK; + +#if 0 + /* This is a normal (non-control) node. Check whether the object + * is in the scope of this client. */ + if (!isns_object_in_scope(scn->scn_owner, node)) + return 0; +#endif + + if (node->ie_scn_mask & ISNS_SCN_TARGET_AND_SELF_ONLY_MASK) { + if (node != scn->scn_owner && !(node_type & ISNS_ISCSI_TARGET_MASK)) + return 0; + } + if (node->ie_scn_mask & ISNS_SCN_INITIATOR_AND_SELF_ONLY_MASK) { + if (node != scn->scn_owner && !(node_type & ISNS_ISCSI_INITIATOR_MASK)) + return 0; + } + + return event; +} + +/* + * Helper to create time stamp attr + */ +static isns_attr_t * +isns_create_timestamp_attr(void) +{ + isns_value_t value = ISNS_VALUE_INIT(uint64, time(NULL)); + + return isns_attr_alloc(ISNS_TAG_TIMESTAMP, NULL, &value); +} + +/* + * This function is invoked whenever someone changes the + * database. + * + * SCNs are another area where the RFC is fabulously wishy washy. + * It is not entirely clear when DD/DDS information should be + * included in a management SCN - one *reasonable* interpretation + * would be that this happens for DDReg/DDDereg/DDSReg/DDSDereg + * events only. But some sections make it sound as if DD + * information is included for all management SCNs. + */ +void +isns_scn_callback(const isns_db_event_t *ev, void *ptr) +{ + isns_object_t *obj = ev->ie_object; + isns_scn_t *scn, **pos; + isns_attr_t *timestamp; + uint32_t node_type; + + /* Never send out notifications for policy objects and the like. */ + if (obj->ie_flags & ISNS_OBJECT_PRIVATE) + return; + + /* When an entity is nuked, remove all SCNs to nodes + * that registered from there */ + if (ISNS_IS_ENTITY(obj) && (ev->ie_bits & ISNS_SCN_OBJECT_REMOVED_MASK)) { + pos = &isns_scn_list; + while ((scn = *pos) != NULL) { + if (scn->scn_entity != obj) { + pos = &scn->scn_next; + continue; + } + isns_debug_scn("Deleting SCN registration for %s\n", + scn->scn_name); + *pos = scn->scn_next; + isns_scn_free(scn); + } + return; + } + + /* For now we handle iSCSI nodes only. Maybe later we'll + * do iFC nodes as well. */ + if (!ISNS_IS_ISCSI_NODE(obj)) + return; + if (!isns_object_get_uint32(obj, ISNS_TAG_ISCSI_NODE_TYPE, &node_type)) + return; + + if (ev->ie_recipient) { + isns_object_t *dst = ev->ie_recipient; + + isns_debug_scn("SCN unicast <%s %u, %s> -> %s %u\n", + obj->ie_template->iot_name, obj->ie_index, + isns_event_string(ev->ie_bits), + dst->ie_template->iot_name, dst->ie_index); + } else { + isns_debug_scn("SCN multicast <%s %u, %s>\n", + obj->ie_template->iot_name, obj->ie_index, + isns_event_string(ev->ie_bits)); + } + timestamp = isns_create_timestamp_attr(); + + pos = &isns_scn_list; + while ((scn = *pos) != NULL) { + unsigned int scn_bits, management; + isns_object_t *recipient, *dd = NULL; + isns_simple_t *call; + + recipient = scn->scn_owner; + + /* Check if the node has gone away completely. */ + if (recipient->ie_scn_mask == 0) { + *pos = scn->scn_next; + isns_scn_free(scn); + continue; + } + + if (recipient->ie_container == NULL) { + isns_warning("Internal bug - SCN recipient without container\n"); + /* Clear the bitmask and loop over - this will remove it */ + recipient->ie_scn_mask = 0; + continue; + } + + /* See if portals were added/removed. + * This does not catch updates that modified *just* + * the SCN port */ + if (recipient->ie_container->ie_mtime != scn->scn_last_update) { + /* Rebuild the list of SCN portals */ + isns_scn_release_funnels(scn); + scn->scn_last_update = 0; + } + pos = &scn->scn_next; + + /* Check for unicast events (triggered for DD addition/removal). + * For unicast events, we do not mask the SCN bits, so that + * clients who have registered for non-management events + * will see the membership events for their DDs nevertheless. */ + if (ev->ie_recipient == NULL) { + scn_bits = ev->ie_bits & recipient->ie_scn_mask; + if (scn_bits == 0) + continue; + /* Management SCNs should not be delivered to nodes + * that have not registered for them. */ + if ((ev->ie_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK) + && !(recipient->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)) + continue; + } else if (recipient == ev->ie_recipient) { + scn_bits = ev->ie_bits; + } else { + /* No match, skip this recipient */ + continue; + } + + if (scn->scn_last_update == 0) { + scn->scn_last_update = recipient->ie_container->ie_mtime; + isns_scn_setup(scn, recipient); + } + + /* We check for SCN capable portals when processing + * the SCN registration. But the portals may go away + * in the meantime. */ + if (scn->scn_funnels == NULL) + continue; + + /* Check SCN bitmask. This will modify the event bits. */ + scn_bits = isns_scn_match(scn, scn_bits, obj, node_type); + if (scn_bits == 0) + continue; + management = !!(scn_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK); + + /* + * 2.2.3 + * A regular SCN registration indicates that the + * Discovery Domain Service SHALL be used to control the + * distribution of SCN messages. Receipt of regular + * SCNs is limited to the discovery domains in which + * the SCN-triggering event takes place. Regular SCNs + * do not contain information about discovery domains. + * + * Implementer's note: We override check for unicast events. + * The reason is that DDDereg will sever the + * relationship, and we would never send an SCN for that + * event. + */ + if (!management && !ev->ie_recipient) { + if (!isns_object_test_visibility(obj, recipient)) + continue; + } + + isns_debug_scn("preparing to send SCN to %s\n", + scn->scn_name); + + if ((call = scn->scn_message) == NULL) { + call = isns_create_scn(isns_scn_server->is_source, + scn->scn_attr, + timestamp); + if (call == NULL) + continue; + scn->scn_message = call; + } + + /* + * If the SCN is a Management SCN, then the SCN message + * SHALL also list the DD_ID and/or DDS_ID of the + * Discovery Domains and Discovery Domain Sets (if any) + * that caused the change in state for that Storage Node. + * These additional attributes (i.e., DD_ID and/or DDS_ID) + * shall immediately follow the iSCSI Name or FC Port + * Name and precede the next SCN bitmap for the next + * notification message (if any). + */ + if (management && ev->ie_trigger) + dd = ev->ie_trigger; + + isns_scn_add_event(call, scn_bits, obj, dd); + + } + + isns_attr_release(timestamp); +} + +/* + * Obtain a socket to talk to this guy. + * Not entirely trivial - this can be both an established + * (incoming) connection, or one that we should establish. + * + * Note, we do not support transmission on the incoming + * connection yet. + */ +static isns_socket_t * +isns_scn_get_socket(isns_scn_t *scn) +{ + isns_scn_funnel_t *f, *best = NULL; + isns_socket_t *sock; + unsigned int worst = 0, loops = 0, nfunnels; + + /* Keep it simple for now */ + if ((f = scn->scn_current_funnel) != NULL && f->scn_socket) { + if (!f->scn_bad) + return f->scn_socket; + /* Oops, we've seen timeouts on this socket. */ + isns_socket_free(f->scn_socket); + f->scn_socket = NULL; + } + +again: + nfunnels = 0; + for (f = scn->scn_funnels; f; f = f->scn_next) { + unsigned int badness = f->scn_bad; + + if (!best || badness < best->scn_bad) + best = f; + if (badness > worst) + worst = badness; + nfunnels++; + } + + if (!best) + return NULL; + + sock = isns_connect_to_portal(&best->scn_portal); + if (sock == NULL) { + /* Make sure we try each funnel exactly once */ + best->scn_bad = worst + 1; + if (++loops < nfunnels) + goto again; + return NULL; + } + + /* Set the security context */ + isns_socket_set_security_ctx(sock, + isns_default_security_context(1)); + + isns_debug_scn("SCN: %s using portal %s\n", + scn->scn_name, + isns_portal_string(&best->scn_portal)); + scn->scn_current_funnel = best; + best->scn_socket = sock; + return sock; +} + +/* + * This is the callback function invoked when the SCN message reply + * comes in, or when the message timed out. + */ +static void +isns_process_scn_response(uint32_t xid, isns_simple_t *msg) +{ + isns_scn_t *scn; + + if (msg == NULL) { + isns_debug_scn("SCN timed out\n"); + return; + } + + isns_debug_scn("Received an SCN response\n"); + for (scn = isns_scn_list; scn; scn = scn->scn_next) { + if (scn->scn_pending && scn->scn_xid == xid) { + isns_debug_scn("SCN: %s acknowledged notification\n", + scn->scn_name); + isns_simple_free(scn->scn_pending); + scn->scn_pending = NULL; + + if (scn->scn_current_funnel) + scn->scn_current_funnel->scn_bad = 0; + } + } +} +/* + * Transmit all pending SCN messages + * + * 2.9.2 + * If a Network Entity has multiple Portals with registered SCN UDP Ports, + * then SCN messages SHALL be delivered to each Portal registered to + * receive such messages. + * + * FIXME: we should make this timer based just as the ESI code. + */ +time_t +isns_scn_transmit_all(void) +{ + time_t now = time(NULL), next_timeout; + isns_scn_t *scn; + + for (scn = isns_scn_list; scn; scn = scn->scn_next) { + isns_simple_t *call; + isns_socket_t *sock; + + /* We do not allow more than one outstanding + * notification for now. */ + if ((call = scn->scn_pending) != NULL) { + if (scn->scn_timeout > now) + continue; + scn->scn_current_funnel->scn_bad++; + if (--(scn->scn_retries)) + goto retry; + isns_warning("SCN for %s timed out\n", + scn->scn_name); + isns_simple_free(call); + scn->scn_pending = NULL; + } + + if ((call = scn->scn_message) == NULL) + continue; + + isns_debug_scn("SCN: transmit pending message for %s\n", + scn->scn_name); + scn->scn_retries = isns_config.ic_scn_retries; + scn->scn_pending = call; + scn->scn_message = NULL; + +retry: + if ((sock = isns_scn_get_socket(scn)) == NULL) { + /* Sorry, no can do. */ + isns_warning("SCN for %s dropped - no portal\n", + scn->scn_name); + scn->scn_pending = NULL; + isns_simple_free(call); + continue; + } + + isns_simple_transmit(sock, call, NULL, + isns_config.ic_scn_timeout, + isns_process_scn_response); + scn->scn_xid = call->is_xid; + scn->scn_timeout = now + isns_config.ic_scn_timeout; + } + + next_timeout = now + 3600; + for (scn = isns_scn_list; scn; scn = scn->scn_next) { + if (scn->scn_pending && scn->scn_timeout < next_timeout) + next_timeout = scn->scn_timeout; + } + + return next_timeout; +} + +/* + * Process an incoming State Change Notification + */ +int +isns_process_scn(isns_server_t *srv, isns_simple_t *call, isns_simple_t **reply) +{ + isns_attr_list_t *list = &call->is_message_attrs; + isns_attr_t *dstattr, *tsattr; + const char *dst_name; + unsigned int i; + + /* The first attribute is the destination, and should match + * our source name. Don't bother checking. The second is the + * time stamp. + */ + if (list->ial_count < 2) + goto rejected; + + dstattr = list->ial_data[0]; + if (dstattr->ia_tag_id != ISNS_TAG_ISCSI_NAME + && dstattr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) + goto rejected; + if (!ISNS_ATTR_IS_STRING(dstattr)) + goto rejected; + dst_name = dstattr->ia_value.iv_string; + + tsattr = list->ial_data[1]; + if (tsattr->ia_tag_id != ISNS_TAG_TIMESTAMP) + return ISNS_SCN_EVENT_REJECTED; + + for (i = 2; i < list->ial_count; ) { + isns_object_template_t *tmpl; + isns_attr_t *bmattr, *srcattr; + const char *node_name; + uint32_t bitmap; + + if (i + 1 >= list->ial_count) + goto rejected; + + bmattr = list->ial_data[i++]; + srcattr = list->ial_data[i++]; + + /* Validate that bitmap and node type match */ + switch (bmattr->ia_tag_id) { + case ISNS_TAG_ISCSI_SCN_BITMAP: + if (srcattr->ia_tag_id != ISNS_TAG_ISCSI_NAME) + goto rejected; + tmpl = &isns_iscsi_node_template; + break; + + case ISNS_TAG_IFCP_SCN_BITMAP: + if (srcattr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) + goto rejected; + tmpl = &isns_fc_port_template; + break; + + default: + goto rejected; + } + + /* Skip over and DD_ID or DDS_ID attrs */ + while (i < list->ial_count) { + isns_attr_t *ddattr = list->ial_data[i]; + + if (ddattr->ia_tag_id == ISNS_TAG_ISCSI_SCN_BITMAP + || ddattr->ia_tag_id == ISNS_TAG_IFCP_SCN_BITMAP) + break; + ++i; + } + + if (!ISNS_ATTR_IS_UINT32(bmattr)) + goto rejected; + bitmap = bmattr->ia_value.iv_uint32; + + if (!ISNS_ATTR_IS_STRING(srcattr)) + goto rejected; + node_name = srcattr->ia_value.iv_string; + + if (srv->is_scn_callback) + srv->is_scn_callback(srv->is_db, bitmap, tmpl, node_name, dst_name); + } + + /* + * 5.7.5.8. SCN Response (SCNRsp) + * The SCNRsp response contains the SCN Destination Attribute + * representing the Node identifier that received the SCN. + */ + *reply = isns_create_scn(srv->is_source, + list->ial_data[0], + NULL); + return ISNS_SUCCESS; + +rejected: + return ISNS_SCN_EVENT_REJECTED; +} diff --git a/utils/open-isns/scope.c b/utils/open-isns/scope.c new file mode 100644 index 0000000..9ee7f9a --- /dev/null +++ b/utils/open-isns/scope.c @@ -0,0 +1,513 @@ +/* + * Handle object visibility and scope. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "objects.h" +#include "message.h" +#include "security.h" +#include "util.h" +#include "db.h" + +struct isns_scope { + isns_db_t * ic_db; + unsigned int ic_users; + isns_object_t * ic_source_node; + + isns_object_template_t * ic_query_class; + + isns_object_list_t ic_dd_nodes; + isns_object_list_t ic_dd_portals; + isns_object_list_t ic_objects; +}; + +static int __isns_scope_collect_dd(uint32_t, void *); + +/* + * Allocate an empty scope + */ +isns_scope_t * +isns_scope_alloc(isns_db_t *db) +{ + isns_scope_t *scope; + + scope = isns_calloc(1, sizeof(*scope)); + + scope->ic_db = db; + scope->ic_users = 1; + return scope; +} + +isns_scope_t * +isns_scope_get(isns_scope_t *scope) +{ + if (scope) { + isns_assert(scope->ic_users); + scope->ic_users++; + } + return scope; +} + +void +isns_scope_release(isns_scope_t *scope) +{ + if (!scope) + return; + + isns_assert(scope->ic_users); + if (--(scope->ic_users)) + return; + + isns_object_release(scope->ic_source_node); + isns_object_list_destroy(&scope->ic_dd_nodes); + isns_object_list_destroy(&scope->ic_dd_portals); + isns_object_list_destroy(&scope->ic_objects); + isns_free(scope); +} + +/* + * Get the scope for this operation + */ +isns_scope_t * +isns_scope_for_call(isns_db_t *db, const isns_simple_t *call) +{ + isns_source_t *source = call->is_source; + isns_object_t *node; + isns_scope_t *scope; + uint32_t node_type; + + /* FIXME use source->is_node and source->is_node_type */ + + /* When we get here, we already know that the client + * represents the specified source node. */ + node = isns_db_lookup_source_node(db, source); + + /* Allow unknown nodes to query the DB */ + if (node == NULL) { + node = isns_create_storage_node2(source, 0, NULL); + if (node == NULL) + return NULL; + source->is_untrusted = 1; + } + + if (isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type) + && (node_type & ISNS_ISCSI_CONTROL_MASK)) { + isns_object_release(node); + return isns_scope_get(db->id_global_scope); + } + + scope = isns_scope_alloc(db); + scope->ic_source_node = node; + + { + isns_object_list_t members = ISNS_OBJECT_LIST_INIT; + unsigned int i; + + isns_object_get_visible(node, db, &members); + isns_object_list_uniq(&members); + + /* If the node is not a member of any DD, allow it + * to at least talk to itself. */ + if (members.iol_count == 0) + isns_object_list_append(&members, node); + + /* Sort DD members into nodes and portals */ + for (i = 0; i < members.iol_count; ++i) { + isns_object_t *obj = members.iol_data[i]; + + if (obj->ie_state != ISNS_OBJECT_STATE_MATURE) + continue; + if (!isns_policy_validate_object_access(call->is_policy, + source, obj, + call->is_function)) + continue; + if (ISNS_IS_ISCSI_NODE(obj)) + isns_object_list_append(&scope->ic_dd_nodes, obj); + else + if (ISNS_IS_PORTAL(obj)) + isns_object_list_append(&scope->ic_dd_portals, obj); + } + isns_object_list_destroy(&members); + } + + return scope; +} + +/* + * Add an object to a scope + */ +void +isns_scope_add(isns_scope_t *scope, isns_object_t *obj) +{ + isns_object_list_append(&scope->ic_objects, obj); +} + +int +isns_scope_remove(isns_scope_t *scope, isns_object_t *obj) +{ + return isns_object_list_remove(&scope->ic_objects, obj); +} + +/* + * Get all objects related through a portal group, optionally + * including the portal group objects themselves + */ +static void +__isns_scope_get_pg_related(isns_scope_t *scope, + const isns_object_t *obj, + isns_object_list_t *result) +{ + isns_object_list_t temp = ISNS_OBJECT_LIST_INIT; + unsigned int i; + + /* Get all portal groups related to this object */ + isns_db_get_relationship_objects(scope->ic_db, + obj, ISNS_RELATION_PORTAL_GROUP, &temp); + + /* Include all portals/nodes that we can reach. */ + for (i = 0; i < temp.iol_count; ++i) { + isns_object_t *pg, *other; + uint32_t pgt; + + pg = temp.iol_data[i]; + + /* Skip any portal group objects with a PG tag of 0; + * these actually deny access. */ + if (!isns_object_get_uint32(pg, ISNS_TAG_PG_TAG, &pgt) + || pgt == 0) + continue; + + /* Get the other object. + * Note that isns_relation_get_other doesn't + * bump the reference count, so there's no need + * to call isns_object_release(other). */ + other = isns_relation_get_other(pg->ie_relation, obj); + if (other->ie_state != ISNS_OBJECT_STATE_MATURE) + continue; + + isns_object_list_append(result, other); + isns_object_list_append(result, pg); + } + + isns_object_list_destroy(&temp); +} + +/* + * Get all portals related to the given node. + * + * 2.2.2 + * Placing Portals of a Network Entity into Discovery Domains allows + * administrators to indicate the preferred IP Portal interface through + * which storage traffic should access specific Storage Nodes of that + * Network Entity. If no Portals of a Network Entity have been placed + * into a DD, then queries scoped to that DD SHALL report all Portals of + * that Network Entity. If one or more Portals of a Network Entity have + * been placed into a DD, then queries scoped to that DD SHALL report + * only those Portals that have been explicitly placed in the DD. + */ +static void +__isns_scope_get_portals(isns_scope_t *scope, + const isns_object_t *node, + isns_object_list_t *portals, + isns_object_list_t *pgs, + int unique) +{ + isns_object_list_t related = ISNS_OBJECT_LIST_INIT; + unsigned int i, specific = 0; + + /* Get all portals and portal groups related to the + * given node. This will put pairs of (portal, portal-group) + * on the list. + */ + __isns_scope_get_pg_related(scope, node, &related); + + /* If we're querying for our own portals, don't limit + * visibility. */ + if (node == scope->ic_source_node) + goto report_all_portals; + + /* Check if any of the portals is mentioned in the DD + * FIXME: There is some ambiguity over what the right + * answer is when you have two nodes (initiator, target), + * and two discovery domains linking the two. One + * DD mentions a specific portal through which target + * should be accessed; the other DD does not (allowing + * use of any portal in that entity). Which portals + * to return here? + * We go for the strict interpretation, ie if *any* DD + * restricts access to certain portals, we report only + * those. + */ + for (i = 0; i < related.iol_count; i += 2) { + isns_object_t *portal = related.iol_data[i]; + + if (isns_object_list_contains(&scope->ic_dd_portals, portal)) { + if (portals + && !(unique || isns_object_list_contains(portals, portal))) + isns_object_list_append(portals, portal); + if (pgs) + isns_object_list_append(pgs, + related.iol_data[i + 1]); + specific++; + } + } + + if (specific) + goto out; + +report_all_portals: + /* No specific portal given for this node. Add them all. */ + for (i = 0; i < related.iol_count; i += 2) { + isns_object_t *portal = related.iol_data[i]; + + if (portals + && !(unique && isns_object_list_contains(portals, portal))) + isns_object_list_append(portals, portal); + if (pgs) + isns_object_list_append(pgs, + related.iol_data[i + 1]); + } + +out: + isns_object_list_destroy(&related); +} + +/* + * Get all nodes reachable through a given portal + * This is really the same as __isns_scope_get_portals + * minus the special casing for preferred portals. + * Still, let's put this into it's own function - the whole + * thing is already complex enough already. + */ +static void +__isns_scope_get_nodes(isns_scope_t *scope, + const isns_object_t *portal, + isns_object_list_t *nodes, + isns_object_list_t *pgs, + int unique) +{ + isns_object_list_t related = ISNS_OBJECT_LIST_INIT; + unsigned int i; + + /* Get all nodes and portal groups related to the + * given node. This will put pairs of (nodes, portal-group) + * on the list. + */ + __isns_scope_get_pg_related(scope, portal, &related); + + for (i = 0; i < related.iol_count; i += 2) { + isns_object_t *node = related.iol_data[i]; + + if (nodes + && !(unique && isns_object_list_contains(nodes, node))) + isns_object_list_append(nodes, node); + if (pgs) + isns_object_list_append(pgs, + related.iol_data[i + 1]); + } + + isns_object_list_destroy(&related); +} + +static void +__isns_scope_get_default_dd(isns_scope_t *scope) +{ + isns_object_t *obj; + + if (isns_config.ic_use_default_domain) { + obj = isns_create_default_domain(); + isns_object_list_append(&scope->ic_objects, obj); + isns_object_release(obj); + } +} + + +/* + * Scope the query + */ +static void +__isns_scope_prepare_query(isns_scope_t *scope, + isns_object_template_t *tmpl) +{ + isns_object_list_t *nodes; + unsigned int i; + + /* Global and default scope have no source node; they're just + * a list of objects. + */ + if (scope->ic_source_node == NULL) + return; + + if (scope->ic_query_class) { + if (scope->ic_query_class == tmpl) + return; + isns_object_list_destroy(&scope->ic_objects); + } + scope->ic_query_class = tmpl; + + nodes = &scope->ic_dd_nodes; + if (tmpl == &isns_entity_template) { + for (i = 0; i < nodes->iol_count; ++i) { + isns_object_t *obj = nodes->iol_data[i]; + + if (obj->ie_container) + isns_object_list_append(&scope->ic_objects, + obj->ie_container); + } + } else + if (tmpl == &isns_iscsi_node_template) { + for (i = 0; i < nodes->iol_count; ++i) { + isns_object_t *obj = nodes->iol_data[i]; + + isns_object_list_append(&scope->ic_objects, obj); + } + } else + if (tmpl == &isns_portal_template) { + for (i = 0; i < nodes->iol_count; ++i) { + isns_object_t *obj = nodes->iol_data[i]; + + __isns_scope_get_portals(scope, obj, + &scope->ic_objects, NULL, 0); + } + } else + if (tmpl == &isns_iscsi_pg_template) { + for (i = 0; i < nodes->iol_count; ++i) { + isns_object_t *obj = nodes->iol_data[i]; + + __isns_scope_get_portals(scope, obj, + NULL, &scope->ic_objects, 0); + } + } else + if (tmpl == &isns_dd_template) { + isns_object_t *node = scope->ic_source_node; + + if (node && !isns_bitvector_is_empty(node->ie_membership)) + isns_bitvector_foreach(node->ie_membership, + __isns_scope_collect_dd, + scope); + else + __isns_scope_get_default_dd(scope); + } + + isns_object_list_uniq(&scope->ic_objects); +} + +static int +__isns_scope_collect_dd(uint32_t dd_id, void *ptr) +{ + isns_scope_t *scope = ptr; + isns_object_t *dd; + + dd = isns_db_vlookup(scope->ic_db, &isns_dd_template, + ISNS_TAG_DD_ID, dd_id, + 0); + if (dd) { + isns_object_list_append(&scope->ic_objects, dd); + isns_object_release(dd); + } + + return 0; +} + +/* + * Lookup functions for scope + */ +int +isns_scope_gang_lookup(isns_scope_t *scope, + isns_object_template_t *tmpl, + const isns_attr_list_t *match, + isns_object_list_t *result) +{ + isns_assert(tmpl); + + if (!scope) + return 0; + + __isns_scope_prepare_query(scope, tmpl); + return isns_object_list_gang_lookup(&scope->ic_objects, + tmpl, match, result); +} + +/* + * Get related objects. + * This is used by the query code. + */ +void +isns_scope_get_related(isns_scope_t *scope, + const isns_object_t *origin, + unsigned int type_mask, + isns_object_list_t *result) +{ + isns_object_template_t *tmpl = origin->ie_template; + isns_object_list_t nodes_result = ISNS_OBJECT_LIST_INIT; + isns_object_list_t portals_result = ISNS_OBJECT_LIST_INIT; + isns_object_list_t *members = &scope->ic_dd_nodes; + unsigned int i; + + if (tmpl == &isns_entity_template) { + /* Entity: include all storage nodes contained, + * the portals through which to reach them, and + * the portal groups for those. */ + for (i = 0; i < members->iol_count; ++i) { + isns_object_t *obj = members->iol_data[i]; + + if (obj->ie_container != origin) + continue; + + isns_object_list_append(&nodes_result, obj); + __isns_scope_get_portals(scope, obj, + &portals_result, + &portals_result, 1); + } + } else + if (tmpl == &isns_iscsi_node_template) { + /* Storage node: include all portals through + * which it can be reached, and the portal + * groups for those. */ + __isns_scope_get_portals(scope, origin, + &portals_result, + &portals_result, 1); + /* FIXME: Include all discovery domains the + * node is a member of. */ + } else + if (tmpl == &isns_portal_template) { + /* Portal: include all storage nodes which can + * be reached through it, and the portal groups + * for those. */ + __isns_scope_get_nodes(scope, origin, + &portals_result, + &portals_result, 1); + } else + if (tmpl == &isns_iscsi_pg_template) { + /* Portal group: PGs *are* a relationship, but + * unclear how this should be handled. + * Return nothing for now. */ + } else + if (tmpl == &isns_dd_template) { + /* Discovery domain: no related objects. */ + } + + isns_object_list_append_list(result, &nodes_result); + isns_object_list_append_list(result, &portals_result); + + isns_object_list_destroy(&nodes_result); + isns_object_list_destroy(&portals_result); +} + +isns_object_t * +isns_scope_get_next(isns_scope_t *scope, + isns_object_template_t *tmpl, + const isns_attr_list_t *current, + const isns_attr_list_t *match) +{ + if (!tmpl || !scope) + return NULL; + + __isns_scope_prepare_query(scope, tmpl); + return __isns_db_get_next(&scope->ic_objects, tmpl, current, match); +} diff --git a/utils/open-isns/security.c b/utils/open-isns/security.c new file mode 100644 index 0000000..548ce18 --- /dev/null +++ b/utils/open-isns/security.c @@ -0,0 +1,437 @@ +/* + * Security functions for iSNS + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "security.h" +#include "source.h" +#include "util.h" +#include "config.h" + +#ifdef WITH_SECURITY + +/* + * Allocate a security peer + */ +static isns_principal_t * +isns_create_principal(const char *spi, size_t spi_len, EVP_PKEY *pk) +{ + char keydesc[32]; + isns_principal_t *peer; + + peer = isns_calloc(1, sizeof(*peer)); + peer->is_users = 1; + if (spi) { + peer->is_name = isns_malloc(spi_len + 1); + memcpy(peer->is_name, spi, spi_len); + peer->is_name[spi_len] = '\0'; + peer->is_namelen = spi_len; + } + + peer->is_key = pk; + if (pk) { + const char *algo; + + switch (pk->type) { + case EVP_PKEY_DSA: algo = "DSA"; break; + case EVP_PKEY_RSA: algo = "RSA"; break; + default: algo = "unknown"; break; + } + + snprintf(keydesc, sizeof(keydesc), " (%s/%u)", + algo, EVP_PKEY_bits(pk)); + } + + isns_debug_auth("Created security principal \"%s\"%s\n", + peer->is_name, keydesc); + return peer; +} + +static void +isns_principal_set_key(isns_principal_t *princ, EVP_PKEY *key) +{ + if (princ->is_key == key) + return; + if (princ->is_key) + EVP_PKEY_free(princ->is_key); + princ->is_key = key; +} + +void +isns_principal_free(isns_principal_t *peer) +{ + if (!peer) + return; + + isns_assert(peer->is_users); + if (--(peer->is_users)) + return; + + if (peer->is_name) + isns_free(peer->is_name); + if (peer->is_key) + EVP_PKEY_free(peer->is_key); + isns_policy_release(peer->is_policy); + isns_free(peer); +} + +/* + * Set the principal's name + */ +void +isns_principal_set_name(isns_principal_t *princ, const char *spi) +{ + isns_assign_string(&princ->is_name, spi); + isns_debug_auth("Setting principal name to \"%s\"\n", spi); +} + +const char * +isns_principal_name(const isns_principal_t *princ) +{ + return princ->is_name; +} + +/* + * Cache policy in the principal object. + */ +void +isns_principal_set_policy(isns_principal_t *princ, + isns_policy_t *policy) +{ + if (policy) + policy->ip_users++; + isns_policy_release(princ->is_policy); + princ->is_policy = policy; +} + +/* + * Key management functions for a security context. + */ +isns_principal_t * +isns_security_load_privkey(isns_security_t *ctx, const char *filename) +{ + EVP_PKEY *pkey; + + isns_debug_auth("Loading private %s key from %s\n", + ctx->is_name, filename); + if (!ctx->is_load_private) + return NULL; + if (!(pkey = ctx->is_load_private(ctx, filename))) { + isns_error("Unable to load private %s key from %s\n", + ctx->is_name, filename); + return NULL; + } + + return isns_create_principal(NULL, 0, pkey); +} + +isns_principal_t * +isns_security_load_pubkey(isns_security_t *ctx, const char *filename) +{ + EVP_PKEY *pkey; + + isns_debug_auth("Loading public %s key from %s\n", + ctx->is_name, filename); + if (!ctx->is_load_public) + return NULL; + if (!(pkey = ctx->is_load_public(ctx, filename))) { + isns_error("Unable to load public %s key from %s\n", + ctx->is_name, filename); + return NULL; + } + + return isns_create_principal(NULL, 0, pkey); +} + +void +isns_security_set_identity(isns_security_t *ctx, isns_principal_t *princ) +{ + if (princ) + princ->is_users++; + if (ctx->is_self) + isns_principal_free(ctx->is_self); + ctx->is_self = princ; +} + +void +isns_add_principal(isns_security_t *ctx, isns_principal_t *princ) +{ + if (princ) + princ->is_users++; + princ->is_next = ctx->is_peers; + ctx->is_peers = princ; +} + +isns_principal_t * +isns_get_principal(isns_security_t *ctx, const char *spi, size_t spi_len) +{ + isns_principal_t *princ; + isns_policy_t *policy; + isns_keystore_t *ks; + EVP_PKEY *pk; + + ks = ctx->is_peer_keys; + + for (princ = ctx->is_peers; princ; princ = princ->is_next) { + /* In a client socket, we set the (expected) + * public key of the peer through + * isns_security_set_peer_key, which will + * just put it on the peers list. + * This key usually has no name. + */ + if (princ->is_name == NULL) { + princ->is_users++; + return princ; + } + if (spi_len == princ->is_namelen + && !memcmp(princ->is_name, spi, spi_len)) { + /* Check whether the cached key and policy + * might be stale. */ + if (ks && ks->ic_generation != princ->is_generation) { + pk = ks->ic_find(ks, spi, spi_len); + if (pk == NULL) { + isns_debug_auth("Unable to refresh key " + "for principal %.*s - probably deleted\n", + spi_len, spi); + return NULL; + } + isns_debug_auth("Refresh key for principal %.*s\n", + spi_len, spi); + isns_principal_set_key(princ, pk); + princ->is_users++; + goto refresh_policy; + } + princ->is_users++; + return princ; + } + } + + if ((ks = ctx->is_peer_keys) == NULL) + return NULL; + + if (!(pk = ks->ic_find(ks, spi, spi_len))) + return NULL; + princ = isns_create_principal(spi, spi_len, pk); + + /* Add it to the list */ + princ->is_next = ctx->is_peers; + ctx->is_peers = princ; + princ->is_users++; + + /* Bind the policy for this peer */ +refresh_policy: + if (!ks->ic_get_policy + || !(policy = ks->ic_get_policy(ks, spi, spi_len))) + policy = isns_policy_default(spi, spi_len); + + /* If no entity is set, use the SPI */ + if (policy->ip_entity == NULL) + isns_assign_string(&policy->ip_entity, policy->ip_name); + + /* If the list of permitted node names is empty, + * default to the standard pattern derived from + * the reversed entity name */ + if (policy->ip_node_names.count == 0) { + char *pattern; + + pattern = isns_build_source_pattern(policy->ip_entity); + if (pattern != NULL) + isns_string_array_append(&policy->ip_node_names, + pattern); + isns_free(pattern); + } + + isns_principal_set_policy(princ, policy); + isns_policy_release(policy); + + /* Remember the keystore generation number */ + princ->is_generation = ks->ic_generation; + + return princ; +} + +/* + * Create a keystore for a security context. + * Key stores let the server side retrieve the + * keys associated with a given SPI. + * + * For now, we support just simple key stores, + * but this could be extended to support + * URLs such as ldaps://ldap.example.com + */ +isns_keystore_t * +isns_create_keystore(const char *spec) +{ + if (*spec != '/') + return NULL; + + return isns_create_simple_keystore(spec); +} + +/* + * Attach the keystore to the security context + */ +void +isns_security_set_keystore(isns_security_t *ctx, + isns_keystore_t *ks) +{ + ctx->is_peer_keys = ks; +} + +/* + * Check that the client supplied time stamp is within a + * certain window. + */ +static int +isns_security_check_timestamp(isns_security_t *ctx, + isns_principal_t *peer, + uint64_t timestamp) +{ + int64_t delta; + + /* The time stamp must not be earlier than timestamp_jitter + * before the last message received. */ + if (peer->is_timestamp) { + delta = timestamp - peer->is_timestamp; + if (delta < -(int64_t) ctx->is_timestamp_jitter) + return 0; + } + + /* We allow the client's clock to diverge from ours, within + * certain limits. */ + if (ctx->is_replay_window != 0) { + time_t now = time(NULL); + + delta = timestamp - now; + if (delta < 0) + delta = -delta; + if (delta > ctx->is_replay_window) + return 0; + } + + peer->is_timestamp = timestamp; + return 1; +} + +int +isns_security_sign(isns_security_t *ctx, isns_principal_t *peer, + buf_t *bp, struct isns_authblk *auth) +{ + if (!ctx->is_sign) { + isns_debug_auth("isns_security_sign: auth context without " + "sign handler.\n"); + return 0; + } + if (!ctx->is_sign(ctx, peer, bp, auth)) { + isns_debug_auth("Failed to sign message, spi=%s\n", + peer->is_name); + return 0; + } + + return 1; +} + +int +isns_security_verify(isns_security_t *ctx, isns_principal_t *peer, + buf_t *bp, struct isns_authblk *auth) +{ + if (!isns_security_check_timestamp(ctx, peer, auth->iab_timestamp)) { + isns_debug_auth("Possible replay attack (bad timestamp) " + "from spi=%s\n", peer->is_name); + return 0; + } + + if (!ctx->is_verify) { + isns_debug_auth("isns_security_verify: auth context without " + "verify handler.\n"); + return 0; + } + if (!ctx->is_verify(ctx, peer, bp, auth)) { + isns_debug_auth("Failed to authenticate message, spi=%s\n", + peer->is_name); + return 0; + } + + return 1; +} + +/* + * Initialize security services. + */ +int +isns_security_init(void) +{ + if (!isns_config.ic_dsa.param_file) { + isns_error("No DSA parameter file - please edit configuration\n"); + return 0; + } + + if (!isns_dsa_init_params(isns_config.ic_dsa.param_file)) + return 0; + + if (!isns_config.ic_auth_key_file) { + isns_error("No AuthKey specified; please edit configuration\n"); + return 0; + } + + if (!isns_dsa_init_key(isns_config.ic_auth_key_file)) + return 0; + + return 1; +} + +#else /* WITH_SECURITY */ + +static void +isns_no_security(void) +{ + static int complain = 0; + + if (complain++ < 5) + isns_error("iSNS authentication disabled in this build\n"); +} + +int +isns_security_init(void) +{ + isns_no_security(); + return 0; +} + +isns_keystore_t * +isns_create_keystore(const char *spec) +{ + isns_no_security(); + return NULL; +} + +void +isns_security_set_keystore(isns_security_t *ctx, + isns_keystore_t *ks) +{ + isns_no_security(); +} + +void +isns_principal_free(isns_principal_t *peer) +{ +} + +isns_principal_t * +isns_get_principal(isns_security_t *ctx, const char *spi, size_t spi_len) +{ + return NULL; +} + +const char * +isns_principal_name(const isns_principal_t *princ) +{ + return NULL; +} + +#endif /* WITH_SECURITY */ diff --git a/utils/open-isns/security.h b/utils/open-isns/security.h new file mode 100644 index 0000000..9ba0f0d --- /dev/null +++ b/utils/open-isns/security.h @@ -0,0 +1,180 @@ +/* + * Security functions for iSNS + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_SECURITY_H +#define ISNS_SECURITY_H + +#include <openssl/evp.h> +#include "buffer.h" +#include "util.h" + +/* + * Security context + */ +struct isns_security { + const char * is_name; + unsigned int is_type; + unsigned int is_replay_window; + unsigned int is_timestamp_jitter; + + /* Our own key and identity */ + isns_principal_t * is_self; + + /* Key store for peer keys */ + isns_principal_t * is_peers; + isns_keystore_t * is_peer_keys; + + EVP_PKEY * (*is_load_private)(isns_security_t *ctx, + const char *filename); + EVP_PKEY * (*is_load_public)(isns_security_t *ctx, + const char *filename); + int (*is_verify)(isns_security_t *ctx, + isns_principal_t *peer, + buf_t *pdu, + const struct isns_authblk *); + int (*is_sign)(isns_security_t *ctx, + isns_principal_t *peer, + buf_t *pdu, + struct isns_authblk *); +}; + +struct isns_principal { + unsigned int is_users; + isns_principal_t * is_next; + char * is_name; + unsigned int is_namelen; + EVP_PKEY * is_key; + unsigned int is_generation; + uint64_t is_timestamp; + + isns_policy_t * is_policy; +}; + +struct isns_policy { + unsigned int ip_users; + unsigned int ip_gen; + + /* SPI */ + char * ip_name; + + /* The client's entity name. This is usually + * the FQDN. */ + char * ip_entity; + + /* Bitmap of functions the client is + * permitted to call. */ + unsigned int ip_functions; + + /* Bitmap of object types the client is + * permitted to register (uses iot_handle) */ + unsigned int ip_object_types; + + /* Names of storage nodes the client is permitted + * to register. */ + struct string_array ip_node_names; + + /* Storage node types the client is permitted + * to read or modify. */ + unsigned int ip_node_types; + + /* The client's default Discovery Domain */ + char * ip_dd_default; +}; + +#define ISNS_PERMISSION_READ 0x01 +#define ISNS_PERMISSION_WRITE 0x02 +#define ISNS_ACCESS(t, p) ((p) << (2 * (t))) +#define ISNS_ACCESS_W(t) ISNS_ACCESS(t, ISNS_PERMISSION_WRITE) +#define ISNS_ACCESS_R(t) ISNS_ACCESS(t, ISNS_PERMISSION_READ) +#define ISNS_ACCESS_RW(t) ISNS_ACCESS(t, ISNS_PERMISSION_READ|ISNS_PERMISSION_WRITE) + +#define ISNS_DEFAULT_OBJECT_ACCESS \ + ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_ENTITY) | \ + ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_NODE) | \ + ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_FC_PORT) | \ + ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_FC_NODE) | \ + ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_PORTAL) | \ + ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_PG) | \ + ISNS_ACCESS_R(ISNS_OBJECT_TYPE_DD) + +struct isns_keystore { + char * ic_name; + unsigned int ic_generation; + EVP_PKEY * (*ic_find)(isns_keystore_t *, + const char *, size_t); + isns_policy_t * (*ic_get_policy)(isns_keystore_t *, + const char *, size_t); +}; + +extern isns_principal_t * isns_get_principal(isns_security_t *, + const char *, size_t); +extern int isns_security_sign(isns_security_t *, + isns_principal_t *, buf_t *, + struct isns_authblk *); +extern int isns_security_verify(isns_security_t *, + isns_principal_t *, buf_t *, + struct isns_authblk *); +extern int isns_security_protected_entity(isns_security_t *, + const char *); + +extern isns_keystore_t * isns_create_keystore(const char *); +extern isns_keystore_t * isns_create_simple_keystore(const char *); +extern isns_keystore_t * isns_create_db_keystore(isns_db_t *); + +extern int isns_authblock_encode(buf_t *, + const struct isns_authblk *); +extern int isns_authblock_decode(buf_t *, + struct isns_authblk *); + +extern isns_policy_t * __isns_policy_alloc(const char *, size_t); +extern isns_policy_t * isns_policy_bind(const isns_message_t *); +extern void isns_principal_set_policy(isns_principal_t *, + isns_policy_t *); +extern void isns_policy_release(isns_policy_t *); +extern int isns_policy_validate_function(const isns_policy_t *, + const isns_message_t *); +extern int isns_policy_validate_source(const isns_policy_t *, + const isns_source_t *); +extern int isns_policy_validate_object_access(const isns_policy_t *, + const isns_source_t *, + const isns_object_t *, + unsigned int); +extern int isns_policy_validate_object_update(const isns_policy_t *, + const isns_source_t *, + const isns_object_t *, + const isns_attr_list_t *, + unsigned int); +extern int isns_policy_validate_object_creation(const isns_policy_t *, + const isns_source_t *, + isns_object_template_t *, + const isns_attr_list_t *, + const isns_attr_list_t *, + unsigned int); +extern int isns_policy_validate_object_type(const isns_policy_t *, + isns_object_template_t *, + unsigned int function); +extern int isns_policy_validate_node_type(const isns_policy_t *, + uint32_t type); +extern int isns_policy_validate_entity(const isns_policy_t *, + const char *); +extern int isns_policy_validate_node_name(const isns_policy_t *, + const char *); +extern int isns_policy_validate_scn_bitmap(const isns_policy_t *, + uint32_t); +extern const char * isns_policy_default_entity(const isns_policy_t *); +extern isns_policy_t * isns_policy_default(const char *, size_t); +extern isns_policy_t * isns_policy_server(void); + +extern EVP_PKEY * isns_dsa_decode_public(const void *, size_t); +extern int isns_dsa_encode_public(EVP_PKEY *, + void **, size_t *); +extern EVP_PKEY * isns_dsa_load_public(const char *); +extern int isns_dsa_store_private(const char *, EVP_PKEY *); +extern EVP_PKEY * isns_dsa_generate_key(void); +extern int isns_dsa_init_params(const char *); +extern int isns_dsa_init_key(const char *); + +#endif /* ISNS_SECURITY_H */ diff --git a/utils/open-isns/server.c b/utils/open-isns/server.c new file mode 100644 index 0000000..0f1c937 --- /dev/null +++ b/utils/open-isns/server.c @@ -0,0 +1,236 @@ +/* + * iSNS server side functions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include "isns.h" +#include "util.h" +#include "security.h" +#include "message.h" + +static int isns_not_supported(isns_server_t *, isns_simple_t *, isns_simple_t **); + +struct isns_service_ops isns_default_service_ops = { + .process_registration = isns_process_registration, + .process_query = isns_process_query, + .process_getnext = isns_process_getnext, + .process_deregistration = isns_process_deregistration, + .process_scn_registration = isns_process_scn_register, + .process_scn_deregistration = isns_process_scn_deregistration, + .process_scn_event = isns_not_supported, + .process_dd_registration = isns_process_dd_registration, + .process_dd_deregistration= isns_process_dd_deregistration, +}; + +struct isns_service_ops isns_callback_service_ops = { + .process_esi = isns_process_esi, + .process_scn = isns_process_scn, +}; + +/* + * Create a server object + */ +isns_server_t * +isns_create_server(isns_source_t *source, isns_db_t *db, + struct isns_service_ops *ops) +{ + isns_server_t *srv; + + if (source == NULL) { + isns_error("%s: source name not set\n", __FUNCTION__); + return NULL; + } + + srv = isns_calloc(1, sizeof(*srv)); + srv->is_source = isns_source_get(source); + srv->is_db = db; + srv->is_ops = ops; + + return srv; +} + +void +isns_server_set_scn_callback(isns_server_t *srv, isns_scn_callback_fn_t *func) +{ + srv->is_scn_callback = func; +} + +/* + * Try to handle transactions safely. + * This isn't perfect, because there's state outside the DB (for instance + * the DD information) + */ +static int +isns_begin_write_operation(isns_server_t *srv, isns_simple_t *msg, int *status) +{ + isns_db_begin_transaction(srv->is_db); + return 1; +} + +static void +isns_end_write_operation(isns_server_t *srv, isns_simple_t *msg, int *status) +{ + if (*status == ISNS_SUCCESS) + isns_db_commit(srv->is_db); + else + isns_db_rollback(srv->is_db); +} + +static inline int +isns_begin_read_operation(isns_server_t *srv, isns_simple_t *msg, int *status) +{ + return 1; +} + +static void +isns_end_read_operation(isns_server_t *srv, isns_simple_t *msg, int *status) +{ +} + +/* + * Process an incoming message + */ +isns_message_t * +isns_process_message(isns_server_t *srv, isns_message_t *msg) +{ + struct isns_service_ops *ops = srv->is_ops; + uint16_t function = msg->im_header.i_function; + int status = ISNS_SUCCESS; + isns_simple_t *call = NULL, *reply = NULL; + isns_message_t *res_msg = NULL; + isns_db_t *db = srv->is_db; + + status = isns_simple_decode(msg, &call); + if (status) { + isns_debug_message("Failed to decode %s request: %s\n", + isns_function_name(msg->im_header.i_function), + isns_strerror(status)); + goto reply; + } + + isns_simple_print(call, isns_debug_message); + + /* Set policy and privileges based on the + * sender's identity. */ + if (!(call->is_policy = isns_policy_bind(msg))) + goto err_unauthorized; + + if (!isns_policy_validate_function(call->is_policy, msg)) + goto err_unauthorized; + + /* Checks related to the message source. + * Note - some messages do not use a source. + */ + if (call->is_source) { + /* Validate the message source. This checks whether the client + * is permitted to use this source node name. + * Beware - not all messages include a source. + */ + if (!isns_policy_validate_source(call->is_policy, call->is_source)) + goto err_unauthorized; + + /* This may fail if the source node isn't in the DB yet. */ + isns_source_set_node(call->is_source, db); + + /* + * 6.2.6. Registration Period + * + * The registration SHALL be removed from the iSNS database + * if an iSNS Protocol message is not received from the + * iSNS client before the registration period has expired. + * Receipt of any iSNS Protocol message from the iSNS client + * automatically refreshes the Entity Registration Period and + * Entity Registration Timestamp. To prevent a registration + * from expiring, the iSNS client should send an iSNS Protocol + * message to the iSNS server at intervals shorter than the + * registration period. Such a message can be as simple as a + * query for one of its own attributes, using its associated + * iSCSI Name or FC Port Name WWPN as the Source attribute. + */ + /* Thusly, we update the timestamps of all entities + * registered by this source. */ + isns_entity_touch(call->is_source->is_entity); + } + + /* Handle the requested function. If the function vector is + * NULL, silently discard the message. */ + switch (function) { +#define DO(rw, FUNCTION, __function) \ + case FUNCTION: \ + if (!ops->__function) \ + goto no_reply; \ + \ + if (!isns_begin_##rw##_operation(srv, call, &status)) \ + break; \ + status = ops->__function(srv, call, &reply); \ + isns_end_##rw##_operation(srv, call, &status); \ + break + + DO(write, ISNS_DEVICE_ATTRIBUTE_REGISTER, process_registration); + DO(read, ISNS_DEVICE_ATTRIBUTE_QUERY, process_query); + DO(read, ISNS_DEVICE_GET_NEXT, process_getnext); + DO(write, ISNS_DEVICE_DEREGISTER, process_deregistration); + DO(write, ISNS_DD_REGISTER, process_dd_registration); + DO(write, ISNS_DD_DEREGISTER, process_dd_deregistration); + DO(read, ISNS_SCN_REGISTER, process_scn_registration); + DO(read, ISNS_SCN_DEREGISTER, process_scn_deregistration); + DO(read, ISNS_SCN_EVENT, process_scn_event); + DO(read, ISNS_STATE_CHANGE_NOTIFICATION, process_scn); + DO(read, ISNS_ENTITY_STATUS_INQUIRY, process_esi); + DO(read, ISNS_HEARTBEAT, process_heartbeat); +#undef DO + + default: + isns_error("Function %s not supported\n", + isns_function_name(function)); + status = ISNS_MESSAGE_NOT_SUPPORTED; + break; + } + +reply: + /* Commit any changes to the DB before we reply */ + if (db) + isns_db_sync(db); + + /* Send out SCN notifications */ + isns_flush_events(); + + if (reply != NULL) { + reply->is_function |= 0x8000; + isns_simple_print(reply, isns_debug_message); + + /* Encode the whole thing */ + status = isns_simple_encode_response(reply, msg, &res_msg); + } + + /* No reply, or error when encoding it: + * just send the error, nothing else. */ + if (res_msg == NULL) { + res_msg = isns_create_reply(msg); + if (status == ISNS_SUCCESS) + status = ISNS_INTERNAL_ERROR; + } + + isns_debug_message("response status 0x%04x (%s)\n", + status, isns_strerror(status)); + + if (status != ISNS_SUCCESS) + isns_message_set_error(res_msg, status); + +no_reply: + isns_simple_free(call); + if (reply) + isns_simple_free(reply); + return res_msg; + +err_unauthorized: + status = ISNS_SOURCE_UNAUTHORIZED; + goto reply; +} + +int +isns_not_supported(isns_server_t *srv, isns_simple_t *call, isns_simple_t **replyp) +{ + return ISNS_MESSAGE_NOT_SUPPORTED; +} diff --git a/utils/open-isns/simple.c b/utils/open-isns/simple.c new file mode 100644 index 0000000..97e181f --- /dev/null +++ b/utils/open-isns/simple.c @@ -0,0 +1,725 @@ +/* + * Common handling for iSNS message parsing + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + * + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "attrs.h" +#include "message.h" +#include "objects.h" +#include "security.h" +#include "socket.h" +#include "util.h" + +typedef void isns_simple_callback_fn_t(uint32_t, isns_simple_t *); + +static int isns_attr_list_scanner_get_pg(struct isns_attr_list_scanner *st); + +/* + * Allocate an empty simple message + */ +static isns_simple_t * +__isns_alloc_simple(void) +{ + isns_simple_t *simp; + + simp = isns_calloc(1, sizeof(*simp)); + + isns_attr_list_init(&simp->is_message_attrs); + isns_attr_list_init(&simp->is_operating_attrs); + + return simp; +} + +/* + * Create a simple message, and set the source name + */ +isns_simple_t * +isns_simple_create(uint32_t function, isns_source_t *source, + const isns_attr_list_t *key) +{ + isns_simple_t *simp; + + simp = __isns_alloc_simple(); + simp->is_function = function; + simp->is_source = source; + if (source != NULL) + source->is_users++; + + if (key) + isns_attr_list_copy(&simp->is_message_attrs, key); + + return simp; +} + +/* + * Perform a call to the server, waiting for the response. + */ +int +isns_simple_call(isns_socket_t *sock, isns_simple_t **inout) +{ + isns_simple_t *simp = *inout; + isns_message_t *msg, *resp; + int status; + + isns_simple_print(simp, isns_debug_message); + + status = isns_simple_encode(simp, &msg); + if (status != ISNS_SUCCESS) { + isns_error("Unable to encode %s: %s\n", + isns_function_name(simp->is_function), + isns_strerror(status)); + return status; + } + + isns_debug_message("Sending request, len=%d\n", + buf_avail(msg->im_payload)); + + resp = isns_socket_call(sock, msg, + isns_config.ic_network.call_timeout); + isns_assert(msg->im_users == 1); + isns_message_release(msg); + + if (resp == NULL) { + isns_error("Timed out while waiting for reply\n"); + return ISNS_INTERNAL_ERROR; + } + + isns_debug_message("Received reply, len=%d\n", + buf_avail(resp->im_payload)); + isns_assert(resp->im_users == 1); + + status = isns_message_status(resp); + if (status != ISNS_SUCCESS) { + isns_message_release(resp); + return status; + } + + status = isns_simple_decode(resp, &simp); + isns_message_release(resp); + + if (status) { + isns_error("Unable to decode server response: %s (status 0x%04x)\n", + isns_strerror(status), status); + return status; + } + + isns_simple_print(simp, isns_debug_message); + + isns_simple_free(*inout); + *inout = simp; + return ISNS_SUCCESS; +} + +/* + * This callback is invoked from the network layer when + * we received a response to an async message + */ +static void +isns_simple_recv_response(isns_message_t *cmsg, isns_message_t *rmsg) +{ + isns_simple_callback_fn_t *user_callback; + isns_simple_t *resp = NULL; + int status; + + /* rmsg being NULL means the call timed out. */ + if (rmsg == NULL) + goto callback; + + status = isns_message_status(rmsg); + if (status != ISNS_SUCCESS) { + isns_error("Server flags error: %s (status 0x%04x)\n", + isns_strerror(status), status); + return; + } + + status = isns_simple_decode(rmsg, &resp); + if (status) { + isns_error("Unable to decode server response: %s (status 0x%04x)\n", + isns_strerror(status), status); + return; + } + + isns_simple_print(resp, isns_debug_message); + +callback: + user_callback = cmsg->im_calldata; + if (user_callback) + user_callback(cmsg->im_xid, resp); + isns_simple_free(resp); +} + +/* + * Transmit a call, without waiting for the response. + */ +int +isns_simple_transmit(isns_socket_t *sock, isns_simple_t *call, + const isns_portal_info_t *dest, + unsigned int timeout, + isns_simple_callback_fn_t *user_callback) +{ + isns_message_t *msg; + int status; + + isns_simple_print(call, isns_debug_message); + + status = isns_simple_encode(call, &msg); + if (status != ISNS_SUCCESS) { + isns_error("Unable to encode %s: %s\n", + isns_function_name(call->is_function), + isns_strerror(status)); + return status; + } + + isns_debug_message("Sending message, len=%d\n", + buf_avail(msg->im_payload)); + + if (user_callback) { + msg->im_callback = isns_simple_recv_response; + msg->im_calldata = user_callback; + } + + if (!isns_socket_submit(sock, msg, timeout)) + status = ISNS_INTERNAL_ERROR; + isns_message_release(msg); + return status; +} + +/* + * Delete the simple message object + */ +void +isns_simple_free(isns_simple_t *simp) +{ + if (simp == NULL) + return; + + isns_attr_list_destroy(&simp->is_message_attrs); + isns_attr_list_destroy(&simp->is_operating_attrs); + isns_source_release(simp->is_source); + isns_policy_release(simp->is_policy); + isns_free(simp); +} + +/* + * Get the source associated with this simple message + */ +isns_source_t * +isns_simple_get_source(isns_simple_t *simp) +{ + return simp->is_source; +} + +const isns_attr_list_t * +isns_simple_get_attrs(isns_simple_t *simp) +{ + return &simp->is_operating_attrs; +} + +/* + * Determine whether message includes a source attr. + */ +static inline int +isns_simple_include_source(uint16_t function) +{ + if (function & 0x8000) + return 0; + switch (function) { + case ISNS_STATE_CHANGE_NOTIFICATION: + case ISNS_ENTITY_STATUS_INQUIRY: + return 0; + } + return 1; +} + +/* + * Decode a simple message + */ +int +isns_simple_decode(isns_message_t *msg, isns_simple_t **result) +{ + isns_simple_t *simp = __isns_alloc_simple(); + buf_t *bp = msg->im_payload; + int status = ISNS_SUCCESS; + + simp->is_function = msg->im_header.i_function; + simp->is_xid = msg->im_xid; + + if (isns_simple_include_source(simp->is_function)) { + status = isns_source_decode(bp, &simp->is_source); + if (status != ISNS_SUCCESS) + goto out; + } + + switch (simp->is_function & 0x7FFF) { + case ISNS_ENTITY_STATUS_INQUIRY: + case ISNS_STATE_CHANGE_NOTIFICATION: + /* Server messages do not include a source */ + status = isns_attr_list_decode(bp, + &simp->is_message_attrs); + break; + + default: + status = isns_attr_list_decode_delimited(bp, + &simp->is_message_attrs); + if (status == ISNS_SUCCESS) + status = isns_attr_list_decode(bp, + &simp->is_operating_attrs); + } + + if (msg->im_header.i_flags & ISNS_F_REPLACE) + simp->is_replace = 1; + +out: + if (status == ISNS_SUCCESS) { + *result = simp; + } else { + isns_simple_free(simp); + *result = NULL; + } + return status; +} + +/* + * Encode a simple message reply or response + */ +static int +__isns_simple_encode(isns_simple_t *simp, buf_t *bp) +{ + int status = ISNS_SUCCESS; + + if (isns_simple_include_source(simp->is_function)) { + if (simp->is_source == NULL) { + isns_error("Cannot encode %s message - caller forgot to set source\n", + isns_function_name(simp->is_function)); + return ISNS_SOURCE_UNKNOWN; + } + status = isns_source_encode(bp, simp->is_source); + } + + if (status == ISNS_SUCCESS) + status = isns_attr_list_encode(bp, &simp->is_message_attrs); + + /* Some functions have just one set of attrs. */ + switch (simp->is_function & 0x7fff) { + /* It's not entirely clear which calls actually have the delimiter. + * The spec is sometimes a little vague on this. */ + case ISNS_SCN_DEREGISTER: + case ISNS_ENTITY_STATUS_INQUIRY: + case ISNS_STATE_CHANGE_NOTIFICATION: + break; + + default: + if (status == ISNS_SUCCESS) + status = isns_encode_delimiter(bp); + if (status == ISNS_SUCCESS) + status = isns_attr_list_encode(bp, &simp->is_operating_attrs); + break; + } + + return status; +} + +int +isns_simple_encode(isns_simple_t *simp, isns_message_t **result) +{ + isns_message_t *msg; + int status, flags; + + flags = ISNS_F_CLIENT; + if (simp->is_replace) + flags |= ISNS_F_REPLACE; + msg = isns_create_message(simp->is_function, flags); + + /* FIXME: for UDP sockets, isns_simple_t may contain a + destination address. */ + + status = __isns_simple_encode(simp, msg->im_payload); + if (status != ISNS_SUCCESS) { + isns_message_release(msg); + msg = NULL; + } + + /* Report the XID to the caller */ + simp->is_xid = msg->im_xid; + + *result = msg; + return status; +} + +int +isns_simple_encode_response(isns_simple_t *reg, + const isns_message_t *request, isns_message_t **result) +{ + isns_message_t *msg; + int status; + + msg = isns_create_reply(request); + + status = __isns_simple_encode(reg, msg->im_payload); + if (status != ISNS_SUCCESS) { + isns_message_release(msg); + msg = NULL; + } + + *result = msg; + return status; +} + +int +isns_simple_decode_response(isns_message_t *resp, isns_simple_t **result) +{ + return isns_simple_decode(resp, result); +} + +/* + * Extract the list of objects from a DevAttrReg/DevAttrQry + * response or similar. + */ +int +isns_simple_response_get_objects(isns_simple_t *resp, + isns_object_list_t *result) +{ + struct isns_attr_list_scanner state; + int status = ISNS_SUCCESS; + + isns_attr_list_scanner_init(&state, NULL, &resp->is_operating_attrs); + while (1) { + isns_object_t *obj; + + status = isns_attr_list_scanner_next(&state); + if (status == ISNS_NO_SUCH_ENTRY) { + status = ISNS_SUCCESS; + break; + } + if (status) + break; + + obj = isns_create_object(state.tmpl, &state.keys, NULL); + + isns_object_set_attrlist(obj, &state.attrs); + if (obj != state.key_obj) + isns_object_list_append(result, obj); + isns_object_release(obj); + } + + isns_attr_list_scanner_destroy(&state); + return status; +} + +/* + * Print a simple message object + */ +void +isns_simple_print(isns_simple_t *simp, isns_print_fn_t *fn) +{ + char buffer[256]; + + if (fn == isns_debug_message + && !isns_debug_enabled(DBG_MESSAGE)) + return; + + fn("---%s%s---\n", + isns_function_name(simp->is_function), + simp->is_replace? "[REPLACE]" : ""); + if (simp->is_source) { + fn("Source:\n", buffer); + isns_attr_print(simp->is_source->is_attr, fn); + } else { + fn("Source: <empty>\n"); + } + + if (simp->is_message_attrs.ial_count == 0) { + fn("Message attributes: <empty list>\n"); + } else { + fn("Message attributes:\n"); + isns_attr_list_print(&simp->is_message_attrs, fn); + } + if (simp->is_operating_attrs.ial_count == 0) { + fn("Operating attributes: <empty list>\n"); + } else { + fn("Operating attributes:\n"); + isns_attr_list_print(&simp->is_operating_attrs, fn); + } +} + +/* + * This set of functions analyzes the operating attrs of a registration, + * or a query response, and chops it up into separate chunks, one + * per objects. + * + * It always returns the keys and attrs for one object, + * following the ordering constraints laid out in the RFC. + */ +void +isns_attr_list_scanner_init(struct isns_attr_list_scanner *st, + isns_object_t *key_obj, + const isns_attr_list_t *attrs) +{ + memset(st, 0, sizeof(*st)); + st->orig_attrs = *attrs; + st->key_obj = key_obj; +} + +void +isns_attr_list_scanner_destroy(struct isns_attr_list_scanner *st) +{ + isns_attr_list_destroy(&st->keys); + isns_attr_list_destroy(&st->attrs); + memset(st, 0, sizeof(*st)); +} + +int +isns_attr_list_scanner_next(struct isns_attr_list_scanner *st) +{ + isns_attr_t *attr; + unsigned int i, pos = st->pos; + + isns_attr_list_destroy(&st->keys); + isns_attr_list_destroy(&st->attrs); + + if (st->orig_attrs.ial_count <= pos) + return ISNS_NO_SUCH_ENTRY; + + attr = st->orig_attrs.ial_data[pos]; + + /* handle those funky inlined PGT definitions */ + if (st->pgt_next_attr && attr->ia_tag_id == st->pgt_next_attr) + return isns_attr_list_scanner_get_pg(st); + + /* This isn't really structured programming anymore */ + if (st->index_acceptable + && (st->tmpl = isns_object_template_for_index_tag(attr->ia_tag_id))) + goto copy_attrs; + + /* + * Find the object template for the given key attr(s). + * This function also enforces restrictions on the + * order of key attributes. + */ + st->tmpl = isns_object_template_find(attr->ia_tag_id); + if (st->tmpl == NULL) { + isns_debug_protocol("%s: attr %u is not a key attr\n", + __FUNCTION__, attr->ia_tag_id); + return ISNS_INVALID_REGISTRATION; + } + + /* Copy the key attrs */ + for (i = 0; i < st->tmpl->iot_num_keys; ++i, ++pos) { + if (pos >= st->orig_attrs.ial_count) { + isns_debug_protocol("%s: incomplete %s object " + "(key attr %u missing)\n", + __FUNCTION__, st->tmpl->iot_name, pos); + return ISNS_INVALID_REGISTRATION; + } + attr = st->orig_attrs.ial_data[pos]; + + /* Make sure key attrs are complete and in order */ + if (attr->ia_tag_id != st->tmpl->iot_keys[i]) { + isns_debug_protocol("%s: incomplete %s object " + "(key attr %u missing)\n", + __FUNCTION__, st->tmpl->iot_name, pos); + return ISNS_INVALID_REGISTRATION; + } + + isns_attr_list_append_attr(&st->keys, attr); + } + + /* + * Consume all non-key attributes corresponding to the + * object class. We stop whenever we hit another + * key attribute, or an attribute that does not belong to + * the object type (eg when a storage node is followed by + * a PGT attribute, as described in section 5.6.5.1). + */ +copy_attrs: + while (pos < st->orig_attrs.ial_count) { + uint32_t tag; + + attr = st->orig_attrs.ial_data[pos]; + tag = attr->ia_tag_id; + + if (!isns_object_attr_valid(st->tmpl, tag) + || isns_object_template_find(tag) != NULL) + break; + + pos++; + isns_attr_list_append_attr(&st->attrs, attr); + } + st->pos = pos; + + return ISNS_SUCCESS; +} + +int +isns_attr_list_scanner_get_pg(struct isns_attr_list_scanner *st) +{ + isns_attr_t *attr, *next = NULL; + unsigned int pos = st->pos; + + + attr = st->orig_attrs.ial_data[st->pos++]; + if (st->pgt_next_attr == ISNS_TAG_PG_TAG) { + isns_object_t *base = st->pgt_base_object; + + if (ISNS_ATTR_IS_NIL(attr)) + st->pgt_value = 0; + else if (ISNS_ATTR_IS_UINT32(attr)) + st->pgt_value = attr->ia_value.iv_uint32; + else + return ISNS_INVALID_REGISTRATION; + + if (ISNS_IS_PORTAL(base) + && isns_portal_from_object(&st->pgt_portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + base)) { + st->pgt_next_attr = ISNS_TAG_PG_ISCSI_NAME; + } else + if (ISNS_IS_ISCSI_NODE(base) + && isns_object_get_string(base, + ISNS_TAG_ISCSI_NAME, + &st->pgt_iscsi_name)) { + st->pgt_next_attr = ISNS_TAG_PORTAL_IP_ADDRESS; + } else { + return ISNS_INTERNAL_ERROR; + } + + /* Trailing PGT at end of list. Shrug. */ + if (st->pos >= st->orig_attrs.ial_count) + return ISNS_NO_SUCH_ENTRY; + + attr = st->orig_attrs.ial_data[st->pos++]; + if (attr->ia_tag_id != st->pgt_next_attr) { + /* Some clients may do this; catch them so + * we can fix it. */ + isns_error("Oops, client sends PGT followed by <%s>\n", + attr->ia_tag->it_name); + return ISNS_INVALID_REGISTRATION; + } + } + + st->tmpl = &isns_iscsi_pg_template; + if (st->pgt_next_attr == ISNS_TAG_PG_ISCSI_NAME) { + isns_attr_list_append_attr(&st->keys, attr); + isns_portal_to_attr_list(&st->pgt_portal_info, + ISNS_TAG_PG_PORTAL_IP_ADDR, + ISNS_TAG_PG_PORTAL_TCP_UDP_PORT, + &st->keys); + } else + if (st->pgt_next_attr == ISNS_TAG_PG_PORTAL_IP_ADDR) { + if (st->pos >= st->orig_attrs.ial_count) + return ISNS_INVALID_REGISTRATION; + + next = st->orig_attrs.ial_data[st->pos++]; + if (next->ia_tag_id != ISNS_TAG_PORTAL_TCP_UDP_PORT) + return ISNS_INVALID_REGISTRATION; + + isns_attr_list_append_string(&st->keys, + ISNS_TAG_PG_ISCSI_NAME, + st->pgt_iscsi_name); + isns_attr_list_append_attr(&st->keys, attr); + isns_attr_list_append_attr(&st->keys, next); + } else { + return ISNS_INTERNAL_ERROR; + } + + isns_attr_list_append_uint32(&st->attrs, + ISNS_TAG_PG_TAG, + st->pgt_value); + + /* Copy other PG attributes if present */ + for (pos = st->pos; pos < st->orig_attrs.ial_count; ++pos) { + uint32_t tag; + + attr = st->orig_attrs.ial_data[pos]; + tag = attr->ia_tag_id; + + /* + * Additional sets of PGTs and PG iSCSI Names to be + * associated to the registered Portal MAY follow. + */ + if (tag == ISNS_TAG_PG_TAG) { + st->pgt_next_attr = tag; + break; + } + + if (tag == ISNS_TAG_PG_ISCSI_NAME + || tag == ISNS_TAG_PG_PORTAL_IP_ADDR + || tag == ISNS_TAG_PG_PORTAL_TCP_UDP_PORT + || !isns_object_attr_valid(st->tmpl, tag)) + break; + + isns_attr_list_append_attr(&st->attrs, attr); + } + st->pos = pos; + + return ISNS_SUCCESS; +} + +/* + * Get the name of a function + */ +#define __ISNS_MAX_FUNCTION 16 +static const char * isns_req_function_names[__ISNS_MAX_FUNCTION] = { +[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg", +[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQry", +[ISNS_DEVICE_GET_NEXT] = "DevGetNext", +[ISNS_DEVICE_DEREGISTER] = "DevDereg", +[ISNS_SCN_REGISTER] = "SCNReg", +[ISNS_SCN_DEREGISTER] = "SCNDereg", +[ISNS_SCN_EVENT] = "SCNEvent", +[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN", +[ISNS_DD_REGISTER] = "DDReg", +[ISNS_DD_DEREGISTER] = "DDDereg", +[ISNS_DDS_REGISTER] = "DDSReg", +[ISNS_DDS_DEREGISTER] = "DDSDereg", +[ISNS_ENTITY_STATUS_INQUIRY] = "ESI", +[ISNS_HEARTBEAT] = "Heartbeat", +}; +static const char * isns_resp_function_names[__ISNS_MAX_FUNCTION] = { +[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrRegResp", +[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQryResp", +[ISNS_DEVICE_GET_NEXT] = "DevGetNextResp", +[ISNS_DEVICE_DEREGISTER] = "DevDeregResp", +[ISNS_SCN_REGISTER] = "SCNRegResp", +[ISNS_SCN_DEREGISTER] = "SCNDeregResp", +[ISNS_SCN_EVENT] = "SCNEventResp", +[ISNS_STATE_CHANGE_NOTIFICATION]= "SCNResp", +[ISNS_DD_REGISTER] = "DDRegResp", +[ISNS_DD_DEREGISTER] = "DDDeregResp", +[ISNS_DDS_REGISTER] = "DDSRegResp", +[ISNS_DDS_DEREGISTER] = "DDSDeregResp", +[ISNS_ENTITY_STATUS_INQUIRY] = "ESIRsp", +/* No response code for heartbeat */ +}; + +const char * +isns_function_name(uint32_t function) +{ + static char namebuf[32]; + const char **names, *name; + unsigned int num = function; + + names = isns_req_function_names; + if (num & 0x8000) { + names = isns_resp_function_names; + num &= 0x7fff; + } + name = NULL; + if (num < __ISNS_MAX_FUNCTION) + name = names[num]; + if (name == NULL) { + snprintf(namebuf, sizeof(namebuf), + "<function %08x>", + function); + name = namebuf; + } + + return name; +} + diff --git a/utils/open-isns/slp.c b/utils/open-isns/slp.c new file mode 100644 index 0000000..43075b3 --- /dev/null +++ b/utils/open-isns/slp.c @@ -0,0 +1,242 @@ +/* + * SLP registration and query of iSNS + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include "config.h" +#include <stdlib.h> +#ifdef HAVE_SLP_H +# include <slp.h> +#endif + +#include "isns.h" +#include "util.h" +#include "internal.h" + +#define ISNS_SLP_SERVICE_NAME "iscsi:sms" +/* + * RFC 4018 says we would use scope initiator-scope-list. + * But don't we want targets to find the iSNS server, too? + */ +#define ISNS_SLP_SCOPE "initiator-scope-list" + +#ifdef WITH_SLP + +struct isns_slp_url_state { + SLPError slp_err; + char * slp_url; +}; + +static void +isns_slp_report(SLPHandle handle, SLPError err, void *cookie) +{ + *(SLPError *) cookie = err; +} + +/* + * Register a service with SLP + */ +int +isns_slp_register(const char *url) +{ + SLPError err, callbackerr; + SLPHandle handle = NULL; + + err = SLPOpen("en", SLP_FALSE, &handle); + if(err != SLP_OK) { + isns_error("Unable to obtain SLP handle (err %d)\n", err); + return 0; + } + + err = SLPReg(handle, url, SLP_LIFETIME_MAXIMUM, + ISNS_SLP_SCOPE, + "(description=iSNS Server),(protocols=isns)", + SLP_TRUE, + isns_slp_report, &callbackerr); + + SLPClose(handle); + + if (err == SLP_OK) + err = callbackerr; + if (err != SLP_OK) { + isns_error("Failed to register with SLP (err %d)\n", err); + return 0; + } + + return 1; +} + +/* + * DeRegister a service + */ +int +isns_slp_unregister(const char *url) +{ + SLPError err, callbackerr; + SLPHandle handle = NULL; + + isns_debug_general("SLP: Unregistering \"%s\"\n", url); + + err = SLPOpen("en", SLP_FALSE, &handle); + if(err != SLP_OK) { + isns_error("Unable to obtain SLP handle (err %d)\n", err); + return 0; + } + + err = SLPDereg(handle, url, isns_slp_report, &callbackerr); + + SLPClose(handle); + + if (err == SLP_OK) + err = callbackerr; + if (err != SLP_OK) { + isns_error("Failed to deregister with SLP (err %d)\n", err); + return 0; + } + + return 1; +} + +/* + * Find an iSNS server through SLP + */ +static SLPBoolean +isns_slp_url_callback(SLPHandle handle, + const char *url, unsigned short lifetime, + SLPError err, void *cookie) +{ + struct isns_slp_url_state *sp = cookie; + SLPSrvURL *parsed_url = NULL; + int want_more = SLP_TRUE; + char buffer[1024]; + + if (err != SLP_OK && err != SLP_LAST_CALL) + return SLP_FALSE; + + if (!url) + goto out; + + isns_debug_general("SLP: Found URL \"%s\"\n", url); + err = SLPParseSrvURL(url, &parsed_url); + if (err != SLP_OK) { + isns_error("Error parsing SLP service URL \"%s\"\n", url); + goto out; + } + + if (parsed_url->s_pcNetFamily + && parsed_url->s_pcNetFamily[0] + && strcasecmp(parsed_url->s_pcNetFamily, "ip")) { + isns_error("Ignoring SLP service URL \"%s\"\n", url); + goto out; + } + + if (parsed_url->s_iPort) { + snprintf(buffer, sizeof(buffer), "%s:%u", + parsed_url->s_pcHost, + parsed_url->s_iPort); + isns_assign_string(&sp->slp_url, buffer); + } else { + isns_assign_string(&sp->slp_url, + parsed_url->s_pcHost); + } + want_more = SLP_FALSE; + +out: + if (parsed_url) + SLPFree(parsed_url); + sp->slp_err = SLP_OK; + + return want_more; +} + +/* + * Locate the iSNS server using SLP. + * This is not really an instantaneous process. Maybe we could + * speed this up by using a cache. + */ +char * +isns_slp_find(void) +{ + static struct isns_slp_url_state state; + SLPHandle handle = NULL; + SLPError err; + + if (state.slp_url) + return state.slp_url; + + isns_debug_general("Using SLP to locate iSNS server\n"); + + err = SLPOpen("en", SLP_FALSE, &handle); + if(err != SLP_OK) { + isns_error("Unable to obtain SLP handle (err %d)\n", err); + return NULL; + } + + err = SLPFindSrvs(handle, ISNS_SLP_SERVICE_NAME, + NULL, "(protocols=isns)", + isns_slp_url_callback, &state); + + SLPClose(handle); + + if (err == SLP_OK) + err = state.slp_err; + if (err != SLP_OK) { + isns_error("Failed to find service in SLP (err %d)\n", err); + return NULL; + } + + if (state.slp_url == NULL) { + isns_error("Service %s not registered with SLP\n", + ISNS_SLP_SERVICE_NAME); + return NULL; + + } + + isns_debug_general("Using iSNS server at %s\n", state.slp_url); + return state.slp_url; +} + +#else /* WITH_SLP */ + +int +isns_slp_register(const char *url) +{ + isns_error("SLP support disabled in this build\n"); + return 0; +} + +int +isns_slp_unregister(const char *url) +{ + isns_error("SLP support disabled in this build\n"); + return 0; +} + +char * +isns_slp_find(void) +{ + isns_error("SLP support disabled in this build\n"); + return NULL; +} + +#endif /* WITH_SLP */ + +char * +isns_slp_build_url(uint16_t port) +{ + char buffer[1024]; + + if (port) + snprintf(buffer, sizeof(buffer), + "service:%s://%s:%u", + ISNS_SLP_SERVICE_NAME, + isns_config.ic_host_name, port); + else + snprintf(buffer, sizeof(buffer), + "service:%s://%s", + ISNS_SLP_SERVICE_NAME, + isns_config.ic_host_name); + return isns_strdup(buffer); +} + diff --git a/utils/open-isns/socket.c b/utils/open-isns/socket.c new file mode 100644 index 0000000..f0a758b --- /dev/null +++ b/utils/open-isns/socket.c @@ -0,0 +1,2289 @@ +/* + * Socket handling code + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <sys/socket.h> +#include <sys/poll.h> +#include <sys/time.h> +#include <sys/un.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +#include <unistd.h> +#include <errno.h> +#include <netdb.h> +#include <fcntl.h> + +#include "buffer.h" +#include "isns.h" +#include "socket.h" +#include "security.h" +#include "util.h" +#include "config.h" + +#define SOCK_DEBUG_VERBOSE 0 + +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif +#ifndef AI_V4MAPPED +# define AI_V4MAPPED 0 +#endif + +enum { + ISNS_MSG_DISCARD, + ISNS_MSG_DONE, + ISNS_MSG_RETURN +}; + +static isns_socket_t *__isns_create_socket(struct addrinfo *src, + struct addrinfo *dst, + int sock_type); +static struct addrinfo *isns_get_address_list(const char *, const char *, + int, int, int); +static void release_addrinfo(struct addrinfo *); +static void isns_net_dgram_recv(isns_socket_t *); +static void isns_net_dgram_xmit(isns_socket_t *); +static void isns_net_stream_accept(isns_socket_t *); +static void isns_net_stream_recv(isns_socket_t *); +static void isns_net_stream_xmit(isns_socket_t *); +static void isns_net_stream_hup(isns_socket_t *); +static void isns_net_stream_error(isns_socket_t *, int); +static void isns_net_stream_reconnect(isns_socket_t *); +static void isns_net_stream_disconnect(isns_socket_t *); +static isns_socket_t *isns_net_alloc(int); +static int isns_socket_open(isns_socket_t *); +static int isns_socket_queue_message(isns_socket_t *, isns_message_t *); +static int isns_socket_retransmit_queued(isns_socket_t *); + +static ISNS_LIST_DECLARE(all_sockets); + +#define debug_verbose(args ...) do { \ + if (SOCK_DEBUG_VERBOSE >= 1) isns_debug_socket(args); \ +} while (0) +#define debug_verbose2(args ...) do { \ + if (SOCK_DEBUG_VERBOSE >= 2) isns_debug_socket(args); \ +} while (0) + +/* + * Helper function for looking at incoming PDUs + */ +static inline buf_t * +isns_socket_next_pdu(isns_socket_t *sock) +{ + buf_t *bp = sock->is_recv_buf; + unsigned int avail; + struct isns_hdr *hdr; + uint32_t pdu_len = 0; + + if (bp == NULL) + return NULL; + + avail = buf_avail(bp); + if (avail < sizeof(*hdr)) + return NULL; + hdr = buf_head(bp); + pdu_len = sizeof(*hdr) + ntohs(hdr->i_length); + + if (avail < pdu_len) + return NULL; + + /* Check for presence of authentication block */ + if (hdr->i_flags & htons(ISNS_F_AUTHBLK_PRESENT)) { + uint32_t *authblk, authlen; + + authblk = (uint32_t *) ((char *) hdr + pdu_len); + if (avail < pdu_len + ISNS_AUTHBLK_SIZE) + return NULL; + + authlen = ntohl(authblk[1]); + if (authlen < 20 || authlen > ISNS_MAX_MESSAGE) { + /* The authblock is garbage. + * The only reliable way to signal such a problem + * is by dropping the connection. + */ + isns_error("socket error: bad auth block\n"); + sock->is_state = ISNS_SOCK_DEAD; + return NULL; + } + + pdu_len += authlen; + if (avail < pdu_len) + return NULL; + } + + return buf_split(&sock->is_recv_buf, pdu_len); +} + +/* + * Try to assemble the message from PDUs + */ +static inline int +isns_msg_complete(struct isns_partial_msg *msg) +{ + buf_t *msg_buf, **chain, *bp; + + /* Return if we haven't seen first and last frag */ + if (((~msg->imp_flags) & (ISNS_F_FIRST_PDU|ISNS_F_LAST_PDU))) + return 0; + + /* Simple - unfragmented case: just move + * the PDU on the chain to the payload */ + if (msg->imp_first_seq == msg->imp_last_seq) { + msg->imp_payload = msg->imp_chain; + buf_pull(msg->imp_payload, sizeof(struct isns_hdr)); + msg->imp_chain = NULL; + return 1; + } + + /* Do we have all fragments? */ + if (msg->imp_last_seq - msg->imp_first_seq + 1 + != msg->imp_pdu_count) + return 0; + + msg_buf = buf_alloc(msg->imp_msg_size); + + chain = &msg->imp_chain; + while ((bp = *chain) != NULL) { + /* Pull the header off */ + buf_pull(bp, sizeof(struct isns_hdr)); + buf_put(msg_buf, buf_head(bp), buf_avail(bp)); + + *chain = bp->next; + buf_free(bp); + } + + return 0; +} + +/* + * Clear the "partial" part of the message + */ +static void +__isns_msg_clear_partial(struct isns_partial_msg *msg) +{ + buf_list_free(msg->imp_chain); + msg->imp_chain = NULL; +} + +/* + * Add an authentication block to an outgoing PDU + */ +#ifdef WITH_SECURITY +static int +isns_pdu_seal(isns_security_t *ctx, buf_t *pdu) +{ + struct isns_authblk auth; + isns_principal_t *self; + + if (!(self = ctx->is_self)) { + isns_error("Cannot sign PDU: no sender identity for socket\n"); + return 0; + } + + auth.iab_bsd = ctx->is_type; + auth.iab_timestamp = time(NULL); + auth.iab_spi = self->is_name; + auth.iab_spi_len = strlen(self->is_name); + + if (!isns_security_sign(ctx, self, pdu, &auth)) { + isns_error("Cannot sign PDU: error creating signature\n"); + return 0; + } + + auth.iab_length = ISNS_AUTHBLK_SIZE + + auth.iab_spi_len + + auth.iab_sig_len; + if (!isns_authblock_encode(pdu, &auth)) + return 0; + + isns_debug_message("Successfully signed message (authlen=%u, spilen=%u, siglen=%u)\n", + auth.iab_length, auth.iab_spi_len, auth.iab_sig_len); + + return 1; +} + +/* + * Authenticate a PDU + * + * The RFC is doing a bit of handwaving around the + * authentication issue. For example, it never + * spells out exactly which parts of the message + * are included in the SHA1 hash to be signed. + * + * It also says that the auth block "is identical in format + * to the SLP authentication block", but all fields + * are twice as wide. + * + * There's not even an error code to tell the client + * we were unable to authenticate him :-( + * + * Interoperability problems, here I come... + */ +static int +isns_pdu_authenticate(isns_security_t *sec, + struct isns_partial_msg *msg, buf_t *bp) +{ + struct isns_hdr *hdr = buf_head(bp); + unsigned int pdu_len, avail; + struct isns_authblk authblk; + isns_principal_t * peer = NULL; + buf_t auth_buf; + + isns_debug_auth("Message has authblock; trying to authenticate\n"); + + /* In the TCP path, we checked this before, but + * better safe than sorry. */ + avail = buf_avail(bp); + pdu_len = sizeof(*hdr) + ntohs(hdr->i_length); + if (avail < pdu_len + ISNS_AUTHBLK_SIZE) { + isns_debug_auth("authblock truncated\n"); + return 0; + } + + /* Get the auth block */ + buf_set(&auth_buf, buf_head(bp) + pdu_len, avail - pdu_len); + if (!isns_authblock_decode(&auth_buf, &authblk)) { + isns_debug_auth("error decoding authblock\n"); + return 0; + } + + /* Truncate the buffer (this just sets the + * tail pointer, but doesn't free memory */ + if (!buf_truncate(bp, pdu_len)) { + isns_debug_auth("buf_truncate failed - cosmic particles?\n"); + return 0; + } + + /* If the socket doesn't have a security context, + * just ignore the auth block. */ + if (sec == NULL) { + msg->imp_header.i_flags &= ~ISNS_F_AUTHBLK_PRESENT; + return 1; + } + + if (authblk.iab_bsd != sec->is_type) + goto failed; + + peer = isns_get_principal(sec, authblk.iab_spi, authblk.iab_spi_len); + if (peer == NULL) { + /* If the admin allows unknown peers, we must make + * sure, however, to not allow an unauthenticated + * PDU to be inserted into an authenticated message. + */ + if (isns_config.ic_auth.allow_unknown_peers + && msg->imp_security == NULL) { + isns_debug_message( + "Accepting unknown peer spi=\"%.*s\" as " + "anonymous peer\n", + authblk.iab_spi_len, authblk.iab_spi); + return 1; + } + + isns_debug_message( + "Unable to create security peer for spi=%.*s\n", + authblk.iab_spi_len, authblk.iab_spi); + + goto failed; + } + + if (!isns_security_verify(sec, peer, bp, &authblk)) { + /* Authentication failed */ + goto failed; + } + + /* The RFC doesn't say how to deal with fragmented + * messages with different BSDs or SPIs. + * kickban seems the right approach. + * We discard this segment rather than failing + * the entire message. + */ + if (msg->imp_chain == NULL) { + msg->imp_security = peer; + peer->is_users++; + } else + if (msg->imp_security != peer) { + goto failed; + } + + isns_principal_free(peer); + return 1; + +failed: + isns_principal_free(peer); + return 0; +} +#else /* WITH_SECURITY */ +static int +isns_pdu_authenticate(isns_security_t *sec, + struct isns_partial_msg *msg, buf_t *bp) +{ + return 0; +} + +#endif + +/* + * Enqueue an incoming PDU on the socket. + * + * A single iSNS message may be split up into + * several PDUs, so we need to perform + * reassembly here. + * + * This function also verifies the authentication + * block, if present. + */ +static void +isns_pdu_enqueue(isns_socket_t *sock, + struct sockaddr_storage *addr, socklen_t alen, + buf_t *segment, struct ucred *creds) +{ + isns_message_queue_t *q = &sock->is_partial; + struct isns_partial_msg *msg; + buf_t **chain, *bp; + struct isns_hdr *hdr; + uint32_t xid, seq, flags; + + hdr = (struct isns_hdr *) buf_head(segment); + xid = ntohs(hdr->i_xid); + seq = ntohs(hdr->i_seq); + flags = ntohs(hdr->i_flags); + + isns_debug_socket("Incoming PDU xid=%04x seq=%u len=%u func=%s%s%s%s%s%s\n", + xid, seq, ntohs(hdr->i_length), + isns_function_name(ntohs(hdr->i_function)), + (flags & ISNS_F_CLIENT)? " client" : "", + (flags & ISNS_F_SERVER)? " server" : "", + (flags & ISNS_F_AUTHBLK_PRESENT)? " authblk" : "", + (flags & ISNS_F_FIRST_PDU)? " first" : "", + (flags & ISNS_F_LAST_PDU)? " last" : ""); + + /* Find the message matching (addr, xid) */ + msg = (struct isns_partial_msg *) isns_message_queue_find(q, xid, addr, alen); + if (msg != NULL) { + if (msg->imp_creds + && (!creds || memcmp(msg->imp_creds, creds, sizeof(*creds)))) { + isns_warning("socket: credentials mismatch! Dropping PDU\n"); + goto drop; + } + hdr = &msg->imp_header; + goto found; + } + + msg = (struct isns_partial_msg *) __isns_alloc_message(xid, sizeof(*msg), + (void (*)(isns_message_t *)) __isns_msg_clear_partial); + memcpy(&msg->imp_addr, addr, alen); + msg->imp_addrlen = alen; + + msg->imp_header = *hdr; + msg->imp_header.i_seq = 0; + + isns_message_queue_append(q, &msg->imp_base); + isns_message_release(&msg->imp_base); + /* Message is owned by is_partial now */ + + /* Fix up the PDU header */ + hdr = &msg->imp_header; + hdr->i_version = ntohs(hdr->i_version); + hdr->i_function = ntohs(hdr->i_function); + hdr->i_length = ntohs(hdr->i_length); + hdr->i_flags = ntohs(hdr->i_flags); + hdr->i_xid = ntohs(hdr->i_xid); + hdr->i_seq = ntohs(hdr->i_seq); + + if (creds) { + msg->imp_credbuf = *creds; + msg->imp_creds = &msg->imp_credbuf; + } + +found: + if (flags & ISNS_F_AUTHBLK_PRESENT) { + /* When authentication fails - should we drop the + * message or treat it as unauthenticated? + * For now we drop it, but a more user friendly + * approach might be to just treat it as + * unauthenticated. + */ + if (!isns_pdu_authenticate(sock->is_security, msg, segment)) + goto drop; + } else + if (msg->imp_header.i_flags & ISNS_F_AUTHBLK_PRESENT) { + /* Oops, unauthenticated fragment in an + * authenticated message. */ + isns_debug_message( + "Oops, unauthenticated fragment in an " + "authenticated message!\n"); + goto drop; + } + + if ((flags & ISNS_F_FIRST_PDU) + && !(msg->imp_flags & ISNS_F_FIRST_PDU)) { + /* FIXME: first seq must be zero */ + msg->imp_first_seq = seq; + msg->imp_flags |= ISNS_F_FIRST_PDU; + } + if ((flags & ISNS_F_LAST_PDU) + && !(msg->imp_flags & ISNS_F_LAST_PDU)) { + msg->imp_last_seq = seq; + msg->imp_flags |= ISNS_F_LAST_PDU; + } + + chain = &msg->imp_chain; + while ((bp = *chain) != NULL) { + struct isns_hdr *ohdr = buf_head(bp); + + /* Duplicate? Drop it! */ + if (seq == ohdr->i_seq) + goto drop; + if (seq < ohdr->i_seq) + break; + chain = &bp->next; + } + segment->next = *chain; + *chain = segment; + + msg->imp_msg_size += buf_avail(segment) - sizeof(*hdr); + msg->imp_pdu_count++; + + /* We received first and last PDU - check if the + * chain is complete */ + if (isns_msg_complete(msg)) { + /* Remove from partial queue. + * We clean the part of the message that is + * not in imp_base, so that we can pass this + * to the caller and have him call + * isns_message_release on it. + */ + __isns_msg_clear_partial(msg); + + /* Move from partial queue to complete queue. */ + isns_message_queue_move(&sock->is_complete, + &msg->imp_base); + msg->imp_base.im_socket = sock; + } + + return; + +drop: + buf_free(segment); + return; +} + +/* + * Send side handling + */ +static void +isns_send_update(isns_socket_t *sock) +{ + buf_t *bp = sock->is_xmit_buf; + + if (bp && buf_avail(bp) == 0) { + sock->is_xmit_buf = bp->next; + buf_free(bp); + } + + if (sock->is_xmit_buf) + sock->is_poll_mask |= POLLOUT; + else + sock->is_poll_mask &= ~POLLOUT; +} + +/* + * Close the socket + */ +static void +isns_net_close(isns_socket_t *sock, int next_state) +{ + if (sock->is_desc >= 0) { + close(sock->is_desc); + sock->is_desc = -1; + } + sock->is_poll_mask &= ~(POLLIN|POLLOUT); + sock->is_state = next_state; + + buf_list_free(sock->is_xmit_buf); + sock->is_xmit_buf = NULL; + + buf_free(sock->is_recv_buf); + sock->is_recv_buf = NULL; + + isns_message_queue_destroy(&sock->is_partial); + isns_message_queue_destroy(&sock->is_complete); +} + +static void +isns_net_set_timeout(isns_socket_t *sock, + void (*func)(isns_socket_t *), + unsigned int timeout) +{ + gettimeofday(&sock->is_deadline, NULL); + sock->is_deadline.tv_sec += timeout; + sock->is_timeout = func; +} + +static void +isns_net_cancel_timeout(isns_socket_t *sock) +{ + timerclear(&sock->is_deadline); +} + +void +isns_net_error(isns_socket_t *sock, int err_code) +{ + if (sock->is_error) + sock->is_error(sock, err_code); +} + +/* + * Create a passive socket (server side) + */ +isns_socket_t * +isns_create_server_socket(const char *src_spec, const char *portspec, int af_hint, int sock_type) +{ + struct addrinfo *src; + + src = isns_get_address_list(src_spec, portspec, + af_hint, sock_type, AI_PASSIVE); + if (src == NULL) + return NULL; + + return __isns_create_socket(src, NULL, sock_type); +} + +/* + * Accept incoming connections. + */ +void +isns_net_stream_accept(isns_socket_t *sock) +{ + isns_socket_t *child; + size_t optlen; + int fd, passcred = 0; + + fd = accept(sock->is_desc, NULL, NULL); + if (fd < 0) { + if (errno != EINTR) + isns_error("Error accepting connection: %m\n"); + return; + } + + optlen = sizeof(passcred); + if (getsockopt(sock->is_desc, SOL_SOCKET, SO_PASSCRED, + &passcred, &optlen) >= 0) { + setsockopt(fd, SOL_SOCKET, SO_PASSCRED, + &passcred, sizeof(passcred)); + } + + child = isns_net_alloc(fd); + child->is_type = SOCK_STREAM; + child->is_autoclose = 1; + child->is_disconnect_fatal = 1; + child->is_poll_in = isns_net_stream_recv; + child->is_poll_out = isns_net_stream_xmit; + child->is_poll_hup = isns_net_stream_hup; + child->is_error = isns_net_stream_error; + child->is_poll_mask = POLLIN|POLLHUP; + child->is_security = sock->is_security; + + if (isns_config.ic_network.idle_timeout) + isns_net_set_timeout(child, + isns_net_stream_disconnect, + isns_config.ic_network.idle_timeout); + + isns_list_append(&all_sockets, &child->is_list); +} + +/* + * This is called from the socket code when it detects + * an error condition. + */ +static void +isns_net_stream_error(isns_socket_t *sock, int err_code) +{ + int timeo = 0, next_state = ISNS_SOCK_DEAD; + + if (err_code == EAGAIN) + return; + + isns_debug_socket("isns_net_stream_error: %s\n", strerror(err_code)); + + switch (err_code) { + case EINTR: /* ignored */ + return; + + case ECONNREFUSED: + case ECONNRESET: + case EHOSTUNREACH: + case ENETUNREACH: + case ENOTCONN: + case EPIPE: + if (sock->is_disconnect_fatal) { + isns_warning("socket disconnect, killing socket\n"); + break; + } + + /* fallthrough to disconnect */ + timeo = isns_config.ic_network.reconnect_timeout; + + case ETIMEDOUT: + /* Disconnect and try to reconnect */ + if (sock->is_client) { + /* FIXME: We don't want this warning for ESI and + * SCN sockets on the server side. */ + isns_warning("socket disconnect, retrying in %u sec\n", + timeo); + isns_net_set_timeout(sock, + isns_net_stream_reconnect, + timeo); + next_state = ISNS_SOCK_DISCONNECTED; + break; + } + + /* fallthru */ + + default: + isns_error("socket error: %s\n", strerror(err_code)); + } + + /* Close the socket right away */ + isns_net_close(sock, next_state); +} + +/* + * recvmsg wrapper handling SCM_CREDENTIALS passing + */ +static int +isns_net_recvmsg(isns_socket_t *sock, + void *buffer, size_t count, + struct sockaddr *addr, socklen_t *alen, + struct ucred **cred) +{ + static struct ucred cred_buf; + unsigned int control[128]; + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov; + int len; + + *cred = NULL; + + iov.iov_base = buffer; + iov.iov_len = count; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = addr; + msg.msg_namelen = *alen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + len = recvmsg(sock->is_desc, &msg, MSG_DONTWAIT); + + if (len < 0) + return len; + + cmsg = CMSG_FIRSTHDR(&msg); + while (cmsg) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_CREDENTIALS) { + memcpy(&cred_buf, CMSG_DATA(cmsg), sizeof(cred_buf)); + *cred = &cred_buf; + break; + } + + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + + *alen = msg.msg_namelen; + return len; +} + +void +isns_net_stream_recv(isns_socket_t *sock) +{ + unsigned char buffer[ISNS_MAX_BUFFER]; + struct sockaddr_storage addr; + struct ucred *creds = NULL; + socklen_t alen = sizeof(addr); + buf_t *bp; + size_t count, total = 0; + int len; + +again: + if ((bp = sock->is_recv_buf) == NULL) { + bp = buf_alloc(ISNS_MAX_MESSAGE); + sock->is_recv_buf = bp; + } + + if ((count = buf_tailroom(bp)) > sizeof(buffer)) + count = sizeof(buffer); + + if (count == 0) { + /* Message too large */ + isns_net_stream_error(sock, EMSGSIZE); + return; + } + +#if 0 + len = recvfrom(sock->is_desc, buffer, count, MSG_DONTWAIT, + (struct sockaddr *) &addr, &alen); +#else + len = isns_net_recvmsg(sock, buffer, count, + (struct sockaddr *) &addr, &alen, + &creds); +#endif + if (len < 0) { + isns_net_stream_error(sock, errno); + return; + } + if (len == 0) { + if (total == 0) + sock->is_poll_mask &= ~POLLIN; + return; + } + + /* We received some data from client, re-arm the + * idle disconnect timer */ + if (sock->is_autoclose + && isns_config.ic_network.idle_timeout) + isns_net_set_timeout(sock, + isns_net_stream_disconnect, + isns_config.ic_network.idle_timeout); + + buf_put(bp, buffer, len); + total += len; + + /* Chop up the recv buffer into PDUs */ + while ((bp = isns_socket_next_pdu(sock)) != NULL) { + /* We have a full PDU; enqueue it */ + /* We shouldn't have more than one partial message + * on a TCP connection; we could check this here. + */ + isns_pdu_enqueue(sock, &addr, alen, bp, creds); + } + + goto again; +} + +void +isns_net_stream_xmit(isns_socket_t *sock) +{ + unsigned int count; + buf_t *bp = sock->is_xmit_buf; + int len; + + /* If a connecting socket can send, it has + * the TCP three-way handshake. */ + if (sock->is_state == ISNS_SOCK_CONNECTING) { + sock->is_state = ISNS_SOCK_IDLE; + sock->is_poll_mask |= POLLIN; + isns_net_cancel_timeout(sock); + } + + if (bp == NULL) + return; + + count = buf_avail(bp); + len = send(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT); + if (len < 0) { + isns_net_stream_error(sock, errno); + return; + } + + debug_verbose("isns_net_stream_xmit(%p, count=%u): transmitted %d\n", + sock, count, len); + buf_pull(bp, len); + isns_send_update(sock); +} + +void +isns_net_stream_hup(isns_socket_t *sock) +{ + sock->is_poll_mask &= ~POLLIN; + /* POLLHUP while connecting means we failed */ + if (sock->is_state == ISNS_SOCK_CONNECTING) + isns_net_stream_error(sock, ECONNREFUSED); +} + +/* + * Clone an addrinfo list + */ +static struct addrinfo * +clone_addrinfo(const struct addrinfo *ai) +{ + struct addrinfo *res = NULL, **p; + + p = &res; + for (; ai; ai = ai->ai_next) { + struct addrinfo *new; + + if (ai->ai_addrlen > sizeof(struct sockaddr_storage)) + continue; + + new = isns_calloc(1, sizeof(*new) + ai->ai_addrlen); + new->ai_family = ai->ai_family; + new->ai_socktype = ai->ai_socktype; + new->ai_protocol = ai->ai_protocol; + new->ai_addrlen = ai->ai_addrlen; + new->ai_addr = (struct sockaddr *) (new + 1); + memcpy(new->ai_addr, ai->ai_addr, new->ai_addrlen); + + *p = new; + p = &new->ai_next; + } + + return res; +} + +static struct addrinfo * +__make_addrinfo(const struct sockaddr *ap, socklen_t alen, int socktype) +{ + struct addrinfo *new; + + new = isns_calloc(1, sizeof(*new) + alen); + new->ai_family = ap->sa_family; + new->ai_socktype = socktype; + new->ai_protocol = 0; + new->ai_addrlen = alen; + new->ai_addr = (struct sockaddr *) (new + 1); + memcpy(new->ai_addr, ap, alen); + + return new; +} + +static struct addrinfo * +make_addrinfo_unix(const char *pathname, int socktype) +{ + unsigned int len = strlen(pathname); + struct sockaddr_un sun; + + if (len + 1 > sizeof(sun.sun_path)) { + isns_error("Can't set AF_LOCAL address: path too long!\n"); + return NULL; + } + + sun.sun_family = AF_LOCAL; + strcpy(sun.sun_path, pathname); + return __make_addrinfo((struct sockaddr *) &sun, SUN_LEN(&sun) + 1, socktype); +} + +static struct addrinfo * +make_addrinfo_any(int family, int socktype) +{ + struct sockaddr_storage addr = { .ss_family = AF_UNSPEC }; + struct addrinfo *res; + + if (family != AF_UNSPEC) { + addr.ss_family = family; + res = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype); + } else { + addr.ss_family = AF_INET6; + res = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype); + addr.ss_family = AF_INET; + res->ai_next = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype); + } + + return res; +} + +/* + * Release addrinfo created by functions above. + * We cannot use freeaddrinfo, as we don't know how it + * is implemented. + */ +static void +release_addrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + for (; ai; ai = next) { + next = ai->ai_next; + isns_free(ai); + } +} + +static void +__isns_sockaddr_set_current(struct __isns_socket_addr *info, + const struct addrinfo *ai) +{ + if (!ai) + return; + + /* Cannot overflow; we check addrlen in clone_addrinfo */ + memcpy(&info->addr, ai->ai_addr, ai->ai_addrlen); + info->addrlen = ai->ai_addrlen; +} + +static void +isns_sockaddr_init(struct __isns_socket_addr *info, + struct addrinfo *ai) +{ + if (ai == NULL) + return; + + __isns_sockaddr_set_current(info, ai); + + /* keep a copy so that we can loop through + * all addrs */ + info->list = ai; + + /* Make the list circular */ + while (ai->ai_next) + ai = ai->ai_next; + ai->ai_next = info->list; +} + +static void +isns_sockaddr_destroy(struct __isns_socket_addr *info) +{ + struct addrinfo *ai, *next; + + if ((ai = info->list) != NULL) { + /* Break the circular list */ + info->list = NULL; + next = ai->ai_next; + ai->ai_next = NULL; + isns_assert(next); + + /* Can't use freeaddrinfo on homegrown + * addrinfo lists. */ + release_addrinfo(next); + } +} + +static int +isns_sockaddr_set_next(struct __isns_socket_addr *info) +{ + struct addrinfo *ai; + + if (!(ai = info->list)) + return 0; + + info->list = ai->ai_next; + __isns_sockaddr_set_current(info, info->list); + return 1; +} + +/* + * This function is used to pick a matching source address + * when connecting to some server. + */ +static int +isns_sockaddr_select(struct __isns_socket_addr *info, + const struct sockaddr_storage *hint) +{ + struct addrinfo *head = info->list, *ai; + + if (info->list == NULL) + return 0; + + if (hint->ss_family == AF_INET6) { + struct addrinfo *good = NULL, *best = NULL; + + ai = head; + do { + if (ai->ai_family == AF_INET) { + /* Possible improvement: when + * destination is not a private network, + * prefer non-private source. */ + good = ai; + } else + if (ai->ai_family == AF_INET6) { + /* Possible improvement: prefer IPv6 addr + * with same address scope (local, global) + */ + best = ai; + break; + } + + ai = ai->ai_next; + } while (ai != head); + + if (!best) + best = good; + if (best) { + __isns_sockaddr_set_current(info, best); + return 1; + } + } else + if (hint->ss_family == AF_INET || hint->ss_family == AF_LOCAL) { + ai = head; + do { + if (ai->ai_family == hint->ss_family) { + __isns_sockaddr_set_current(info, ai); + return 1; + } + ai = ai->ai_next; + } while (ai != head); + } + + return 0; +} + +void +isns_net_stream_reconnect(isns_socket_t *sock) +{ + struct sockaddr *addr = (struct sockaddr *) &sock->is_dst.addr; + + debug_verbose("isns_net_stream_reconnect(%p)\n", sock); + + /* If we timed out while connecting, close the socket + * and try again. */ + if (sock->is_state == ISNS_SOCK_CONNECTING) { + isns_net_close(sock, ISNS_SOCK_DISCONNECTED); + isns_sockaddr_set_next(&sock->is_dst); + } + + if (!isns_socket_open(sock)) { + isns_error("isns_net_stream_reconnect: cannot create socket\n"); + sock->is_state = ISNS_SOCK_DEAD; + return; + } + + if (connect(sock->is_desc, addr, sock->is_dst.addrlen) >= 0) { + sock->is_state = ISNS_SOCK_IDLE; + sock->is_poll_mask |= POLLIN; + } else + if (errno == EINTR || errno == EINPROGRESS) { + sock->is_state = ISNS_SOCK_CONNECTING; + isns_net_set_timeout(sock, + isns_net_stream_reconnect, + isns_config.ic_network.connect_timeout); + sock->is_poll_mask |= POLLOUT; + } else { + isns_net_stream_error(sock, errno); + return; + } + + /* We're connected, or in the process of doing so. + * Check if there are any pending messages, and + * retransmit them. */ + isns_socket_retransmit_queued(sock); +} + +void +isns_net_stream_disconnect(isns_socket_t *sock) +{ + isns_debug_socket("Disconnecting idle socket\n"); + isns_net_close(sock, ISNS_SOCK_DEAD); +} + +/* + * Datagram send/recv + */ +static int +isns_net_dgram_connect(isns_socket_t *sock) +{ + return connect(sock->is_desc, + (struct sockaddr *) &sock->is_dst.addr, + sock->is_dst.addrlen); +} + +void +isns_net_dgram_recv(isns_socket_t *sock) +{ + unsigned char buffer[ISNS_MAX_BUFFER]; + struct sockaddr_storage addr; + socklen_t alen = sizeof(addr); + buf_t *bp; + int len; + + len = recvfrom(sock->is_desc, buffer, sizeof(buffer), + MSG_DONTWAIT, (struct sockaddr *) &addr, &alen); + if (len < 0) { + isns_error("recv: %m\n"); + return; + } + if (len == 0) + return; + + bp = buf_alloc(len); + if (bp == NULL) + return; + + buf_put(bp, buffer, len); + isns_pdu_enqueue(sock, &addr, alen, bp, NULL); +} + +void +isns_net_dgram_xmit(isns_socket_t *sock) +{ + unsigned int count; + buf_t *bp = sock->is_xmit_buf; + int len; + + count = buf_avail(bp); + if (bp->addrlen) { + len = sendto(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT, + (struct sockaddr *) &bp->addr, bp->addrlen); + } else { + len = sendto(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT, + NULL, 0); + } + + /* Even if sendto failed, we will pull the pending buffer + * off the send chain. Else we'll loop forever on an + * unreachable host. */ + if (len < 0) + isns_error("send: %m\n"); + + buf_pull(bp, count); + isns_send_update(sock); +} + +/* + * Bind socket to random port + */ +static int +__isns_socket_bind_random(int fd, + const struct sockaddr *orig_addr, + socklen_t src_len) +{ + struct sockaddr_storage addr; + struct sockaddr *src_addr; + uint16_t min = 888, max = 1024; + unsigned int loop = 0; + + /* Copy the address to a writable location */ + isns_assert(src_len <= sizeof(addr)); + memcpy(&addr, orig_addr, src_len); + src_addr = (struct sockaddr *) &addr; + + /* Bind to a random port */ + do { + uint16_t port; + + port = random(); + port = min + (port % (max - min)); + + isns_addr_set_port(src_addr, port); + + if (bind(fd, src_addr, src_len) == 0) + return 1; + + if (errno == EACCES && min < 1024) { + min = 1024; + max = 65535; + continue; + } + } while (errno == EADDRINUSE && ++loop < 128); + + isns_error("Unable to bind socket\n"); + return 0; +} + +/* + * Create a socket + */ +isns_socket_t * +__isns_create_socket(struct addrinfo *src, struct addrinfo *dst, int sock_type) +{ + isns_socket_t *sock; + + sock = isns_net_alloc(-1); + sock->is_type = sock_type; + + /* Set address lists */ + isns_sockaddr_init(&sock->is_dst, dst); + isns_sockaddr_init(&sock->is_src, src); + + if (dst) { + /* This is an outgoing connection. */ + sock->is_client = 1; + + if (!isns_socket_open(sock)) + goto failed; + + if (sock_type == SOCK_DGRAM) { + sock->is_poll_in = isns_net_dgram_recv; + sock->is_poll_out = isns_net_dgram_xmit; + sock->is_poll_mask = POLLIN; + + sock->is_retrans_timeout = isns_config.ic_network.udp_retrans_timeout; + + while (isns_net_dgram_connect(sock) < 0) { + if (isns_sockaddr_set_next(&sock->is_dst) + && sock->is_dst.list != dst) + continue; + isns_error("Unable to connect: %m\n"); + goto failed; + } + } else { + /* Stream socket */ + sock->is_poll_in = isns_net_stream_recv; + sock->is_poll_out = isns_net_stream_xmit; + sock->is_poll_hup = isns_net_stream_hup; + sock->is_error = isns_net_stream_error; + sock->is_poll_mask = POLLHUP; + + sock->is_retrans_timeout = isns_config.ic_network.tcp_retrans_timeout; + + isns_net_stream_reconnect(sock); + } + } else { + if (!isns_socket_open(sock)) + goto failed; + + if (sock_type == SOCK_DGRAM) { + sock->is_poll_in = isns_net_dgram_recv; + sock->is_poll_out = isns_net_dgram_xmit; + sock->is_state = ISNS_SOCK_IDLE; + } else { + sock->is_poll_in = isns_net_stream_accept; + sock->is_error = isns_net_stream_error; + sock->is_state = ISNS_SOCK_LISTENING; + } + sock->is_poll_mask = POLLIN; + } + + isns_list_append(&all_sockets, &sock->is_list); + return sock; + +failed: + isns_socket_free(sock); + return NULL; +} + +/* + * Connect to the master process + */ +isns_socket_t * +isns_create_bound_client_socket(const char *src_spec, const char *dst_spec, + const char *portspec, int af_hint, int sock_type) +{ + struct addrinfo *src = NULL, *dst; + + if (src_spec) { + src = isns_get_address_list(src_spec, NULL, af_hint, sock_type, 0); + if (src == NULL) + return NULL; + } + + dst = isns_get_address_list(dst_spec, portspec, af_hint, sock_type, 0); + if (dst == NULL) { + release_addrinfo(src); + return NULL; + } + + return __isns_create_socket(src, dst, sock_type); +} + +isns_socket_t * +isns_create_client_socket(const char *dst_spec, const char *portspec, int af_hint, int sock_type) +{ + return isns_create_bound_client_socket(NULL, dst_spec, portspec, af_hint, sock_type); +} + +static inline int +isns_socket_type_from_portal(const isns_portal_info_t *info) +{ + switch (info->proto) { + case IPPROTO_TCP: + return SOCK_STREAM; + case IPPROTO_UDP: + return SOCK_DGRAM; + default: + isns_error("Unknown protocol %d in portal\n", info->proto); + } + return -1; +} + +isns_socket_t * +isns_connect_to_portal(const isns_portal_info_t *info) +{ + struct sockaddr_storage dst_addr; + struct addrinfo *ai; + int dst_alen, sock_type; + + if ((sock_type = isns_socket_type_from_portal(info)) < 0) + return NULL; + + dst_alen = isns_portal_to_sockaddr(info, &dst_addr); + ai = __make_addrinfo((struct sockaddr *) &dst_addr, dst_alen, sock_type); + + return __isns_create_socket(NULL, ai, sock_type); +} + +/* + * Make server side disconnects isns_fatal. + * Nice for command line apps. + */ +void +isns_socket_set_disconnect_fatal(isns_socket_t *sock) +{ + sock->is_disconnect_fatal = 1; +} + +/* + * Set the socket's security context + */ +void +isns_socket_set_security_ctx(isns_socket_t *sock, + isns_security_t *ctx) +{ + sock->is_security = ctx; +} + +/* + * Create a socket + */ +static isns_socket_t * +isns_net_alloc(int fd) +{ + isns_socket_t *new; + + new = isns_calloc(1, sizeof(*new)); + new->is_desc = fd; + if (fd >= 0) + new->is_state = ISNS_SOCK_IDLE; + else + new->is_state = ISNS_SOCK_DISCONNECTED; + + isns_message_queue_init(&new->is_partial); + isns_message_queue_init(&new->is_complete); + isns_message_queue_init(&new->is_pending); + isns_list_init(&new->is_list); + + return new; +} + +/* + * Open the socket + */ +static int +isns_socket_open(isns_socket_t *sock) +{ + int af, fd, state = ISNS_SOCK_IDLE; + + if (sock->is_desc >= 0) + return 1; + + af = sock->is_dst.addr.ss_family; + if (af != AF_UNSPEC) { + /* Select a matching source address */ + if (sock->is_src.list + && !isns_sockaddr_select(&sock->is_src, &sock->is_dst.addr)) { + isns_warning("No matching source address for given destination\n"); + return 0; + } + } else { + af = sock->is_src.addr.ss_family; + if (af == AF_UNSPEC) + return 0; + } + + if ((fd = socket(af, sock->is_type, 0)) < 0) { + isns_error("Unable to create socket: %m\n"); + return 0; + } + + if (sock->is_src.addr.ss_family != AF_UNSPEC) { + const struct sockaddr *src_addr; + int src_len, on = 1, bound = 0; + + src_addr = (struct sockaddr *) &sock->is_src.addr; + src_len = sock->is_src.addrlen; + + /* For debugging only! */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + isns_error("setsockopt(SO_REUSEADDR) failed: %m\n"); + goto failed; + } + + switch (af) { + case AF_LOCAL: + unlink(((struct sockaddr_un *) src_addr)->sun_path); + + if (sock->is_type == SOCK_STREAM + && setsockopt(fd, SOL_SOCKET, SO_PASSCRED, + &on, sizeof(on)) < 0) { + isns_error("setsockopt(SO_PASSCRED) failed: %m\n"); + goto failed; + } + break; + + case AF_INET: + case AF_INET6: + if (isns_addr_get_port(src_addr) == 0) { + if (!__isns_socket_bind_random(fd, src_addr, src_len)) + goto failed; + bound++; + } + break; + } + + if (!bound && bind(fd, src_addr, src_len) < 0) { + isns_error("Unable to bind socket: %m\n"); + goto failed; + } + } + + if (sock->is_client) { + /* Set to nonblocking behavior; makes the connect + * call return instantly. */ + fcntl(fd, F_SETFL, O_NONBLOCK); + } else { + if (sock->is_type == SOCK_STREAM) { + if (listen(fd, 128) < 0) { + isns_error("Unable to listen on socket: %m\n"); + goto failed; + } + state = ISNS_SOCK_LISTENING; + } + } + + sock->is_desc = fd; + sock->is_state = state; + return 1; + +failed: + close(fd); + return 0; +} + +/* + * Destroy a socket + */ +static inline void +isns_socket_destroy(isns_socket_t *sock) +{ + isns_sockaddr_destroy(&sock->is_dst); + isns_sockaddr_destroy(&sock->is_src); + isns_free(sock); +} + +void +isns_socket_free(isns_socket_t *sock) +{ + isns_net_close(sock, ISNS_SOCK_DEAD); + isns_list_del(&sock->is_list); + + sock->is_destroy = 1; + if (sock->is_users == 0) + isns_socket_destroy(sock); +} + +int +isns_socket_release(isns_socket_t *sock) +{ + isns_assert(sock->is_users); + sock->is_users -= 1; + + if (sock->is_destroy) { + if (!sock->is_users) + isns_socket_destroy(sock); + return 0; + } + return 1; +} + +/* + * Display a socket + */ +#if SOCK_DEBUG_VERBOSE > 0 +static const char * +isns_socket_state_name(int state) +{ + static char xbuf[16]; + + switch (state) { + case ISNS_SOCK_LISTENING: + return "listening"; + case ISNS_SOCK_CONNECTING: + return "connecting"; + case ISNS_SOCK_IDLE: + return "idle"; + case ISNS_SOCK_FAILED: + return "failed"; + case ISNS_SOCK_DISCONNECTED: + return "disconnected"; + case ISNS_SOCK_DEAD: + return "dead"; + } + snprintf(xbuf, sizeof(xbuf), "<%u>", state); + return xbuf; +} + +static void +isns_print_socket(const isns_socket_t *sock) +{ + isns_message_t *msg = NULL; + char buffer[8192]; + size_t pos = 0, size = sizeof(buffer); + + snprintf(buffer + pos, size - pos, + "socket %p desc %d state %s", + sock, sock->is_desc, + isns_socket_state_name(sock->is_state)); + pos = strlen(buffer); + + if (timerisset(&sock->is_deadline)) { + snprintf(buffer + pos, size - pos, " deadline=%ldms", + __timeout_millisec(NULL, &sock->is_deadline)); + pos = strlen(buffer); + } + + if ((msg = isns_message_queue_head(&sock->is_pending)) != NULL) { + snprintf(buffer + pos, size - pos, " msg timeout=%ldms", + __timeout_millisec(NULL, &msg->im_timeout)); + pos = strlen(buffer); + } + + isns_debug_socket("%s\n", buffer); +} +#else +#define isns_print_socket(p) do { } while (0) +#endif + +/* + * Process incoming messages, and timeouts + */ +static int +isns_net_validate(isns_socket_t *sock, isns_message_t *msg, + const isns_message_t *check_msg) +{ + isns_message_t *orig = NULL; + int verdict = ISNS_MSG_DISCARD; + + if (sock->is_security && !msg->im_security) { + /* Rude server, or malicious man in the + * middle. */ + isns_debug_message("Ignoring unauthenticated message\n"); + goto out; + } + + /* If this is a request, return it. */ + if (!(msg->im_header.i_function & 0x8000)) { + if (check_msg == NULL) { + verdict = ISNS_MSG_RETURN; + } else { + /* Else: see if there's a server attached to this + * socket. */ + } + goto out; + } + + orig = isns_message_queue_find(&sock->is_pending, msg->im_xid, NULL, 0); + if (orig == NULL) { + isns_debug_message("Ignoring spurious response message (xid=%04x)\n", + msg->im_xid); + goto out; + } + + isns_message_unlink(orig); + if (orig->im_header.i_function != (msg->im_header.i_function & 0x7FFF)) { + isns_debug_message("Response message doesn't match function\n"); + goto out; + } + + if (check_msg == orig) { + verdict = ISNS_MSG_RETURN; + } else { + isns_debug_message("Received response for pending message 0x%x\n", + msg->im_xid); + if (orig->im_callback) + orig->im_callback(orig, msg); + verdict = ISNS_MSG_DONE; + } + +out: + isns_message_release(orig); + return verdict; +} + +static void +isns_net_timeout(isns_socket_t *sock, isns_message_t *msg) +{ + if (msg->im_callback) + msg->im_callback(msg, NULL); + isns_message_release(msg); +} + +/* + * Helper function to update timeout + */ +static inline void +__set_timeout(struct timeval *end, unsigned long timeout) +{ + gettimeofday(end, NULL); + end->tv_sec += timeout; +} + +static inline int +__timeout_expired(const struct timeval *now, const struct timeval *expires) +{ + /* FIXME: Should ignore sub-millisecond remainder */ + return timercmp(now, expires, >=); +} + +static long +__timeout_millisec(const struct timeval *now, const struct timeval *expires) +{ + struct timeval __now, delta = { 0, 0 }; + + if (now == NULL) { + gettimeofday(&__now, NULL); + now = &__now; + } + + timersub(expires, now, &delta); + + return delta.tv_sec * 1000 + delta.tv_usec / 1000; +} + +static inline void +__update_timeout(struct timeval *end, const struct timeval *timeout) +{ + if (!timerisset(end) || timercmp(timeout, end, <)) + *end = *timeout; +} + +/* + * Get the next iSNS message + */ +isns_message_t * +__isns_recv_message(const struct timeval *end_time, isns_message_t *watch_msg) +{ + isns_socket_t *sock, **sock_list; + isns_list_t *pos, *next; + struct pollfd *pfd; + unsigned int i, count, max_sockets; + struct timeval now, this_end; + int r; + + max_sockets = isns_config.ic_network.max_sockets; + sock_list = alloca(max_sockets * sizeof(sock_list[0])); + pfd = alloca(max_sockets * sizeof(pfd[0])); + +again: + timerclear(&this_end); + gettimeofday(&now, NULL); + + if (end_time) { + if (__timeout_expired(&now, end_time)) + return NULL; + this_end = *end_time; + } + + i = 0; + isns_list_foreach(&all_sockets, pos, next) { + isns_socket_t *sock = isns_list_item(isns_socket_t, is_list, pos); + isns_message_t *msg = NULL; + + /* We need to be a little careful here; callbacks may + * mark the socket for destruction. + * Bumping is_users while we're busy with the socket + * prevents mayhem. */ + sock->is_users++; + + while ((msg = isns_message_dequeue(&sock->is_complete)) != NULL) { + switch (isns_net_validate(sock, msg, watch_msg)) { + case ISNS_MSG_RETURN: + isns_assert(!sock->is_destroy); + isns_socket_release(sock); + return msg; + + default: + isns_message_release(msg); + } + } + + /* This will return 0 if the socket was marked for + * destruction. */ + if (!isns_socket_release(sock)) + continue; + + isns_print_socket(sock); + + /* This handles reconnect, idle disconnect etc. */ + while (timerisset(&sock->is_deadline)) { + if (__timeout_expired(&now, &sock->is_deadline)) { + timerclear(&sock->is_deadline); + sock->is_timeout(sock); + isns_print_socket(sock); + continue; + } + __update_timeout(&this_end, &sock->is_deadline); + break; + } + + /* No more input and output means closed&dead */ + if (sock->is_state == ISNS_SOCK_IDLE + && !(sock->is_poll_mask & (POLLIN|POLLOUT))) { + isns_debug_socket("connection closed by peer, killing socket\n"); + isns_net_close(sock, ISNS_SOCK_FAILED); + } + + /* Check whether pending messages have timed out. */ + while (sock->is_state == ISNS_SOCK_IDLE + && (msg = isns_message_queue_head(&sock->is_pending)) != NULL) { + if (__timeout_expired(&now, &msg->im_timeout)) { + isns_debug_socket("sock %p message %04x timed out\n", + sock, msg->im_xid); + isns_message_unlink(msg); + if (msg == watch_msg) { + isns_message_release(msg); + return NULL; + } + isns_net_timeout(sock, msg); + continue; + } + + if (!__timeout_expired(&now, &msg->im_resend_timeout)) { + __update_timeout(&this_end, + &msg->im_resend_timeout); + /* In odd configurations, the call_timeout + * may be lower than the resend_timeout */ + __update_timeout(&this_end, + &msg->im_timeout); + break; + } + + isns_debug_socket("sock %p message %04x - " + "minor timeout, resending.\n", + sock, msg->im_xid); + + /* If a TCP socket times out, something is + * fishy. Force a reconnect, which will resend + * all pending messages. */ + if (sock->is_type == SOCK_STREAM) { + isns_net_close(sock, ISNS_SOCK_DISCONNECTED); + isns_net_set_timeout(sock, + isns_net_stream_reconnect, + 0); + break; + } + + /* UDP socket - retransmit this one message */ + isns_message_queue_remove(&sock->is_pending, msg); + isns_socket_queue_message(sock, msg); + isns_message_release(msg); + } + + /* + * If the socket on which we're waiting right + * now got disconnected, or had any other kind of + * error, return right away to let the caller know. + */ + if (sock->is_state == ISNS_SOCK_FAILED) { + if (sock->is_disconnect_fatal) + goto kill_socket; + if (sock->is_report_failure) + return NULL; + sock->is_state = ISNS_SOCK_DISCONNECTED; + continue; + } + + if (sock->is_state == ISNS_SOCK_DEAD) { +kill_socket: + isns_list_del(&sock->is_list); + if (sock->is_report_failure) + return NULL; + if (!sock->is_client) + isns_socket_free(sock); + continue; + } + + /* should not happen */ + if (i >= max_sockets) + break; + + pfd[i].fd = sock->is_desc; + pfd[i].events = sock->is_poll_mask; + sock_list[i] = sock; + i++; + } + count = i; + + if (timerisset(&this_end)) { + long millisec; + + /* timeval arithmetic can yield sub-millisecond timeouts. + * Round up to prevent looping. */ + millisec = __timeout_millisec(&now, &this_end); + if (millisec == 0) + millisec += 1; + + debug_verbose2("poll(%p, %u, %d)\n", pfd, count, millisec); + r = poll(pfd, count, millisec); + } else { + r = poll(pfd, count, -1); + } + + if (r < 0) { + if (errno != EINTR) + isns_error("poll returned error: %m\n"); + return NULL; + } + + /* Any new incoming connections will be added to the + * head of the list. */ + for (i = 0; i < count; ++i) { + sock = sock_list[i]; + if (pfd[i].revents & POLLIN) + sock->is_poll_in(sock); + if (pfd[i].revents & POLLOUT) + sock->is_poll_out(sock); + if (pfd[i].revents & POLLHUP) + sock->is_poll_hup(sock); + } + + goto again; +} + +isns_message_t * +isns_recv_message(struct timeval *timeout) +{ + isns_message_t *msg; + struct timeval end; + + if (timeout == NULL) + return __isns_recv_message(NULL, NULL); + + gettimeofday(&end, NULL); + timeradd(&end, timeout, &end); + msg = __isns_recv_message(&end, NULL); + + if (msg == NULL) + return msg; + isns_debug_socket("Next message xid=%04x\n", msg->im_xid); + if (msg->im_security) { + isns_debug_message("Received authenticated message from \"%s\"\n", + isns_principal_name(msg->im_security)); + } else if (isns_config.ic_security) { + isns_debug_message("Received unauthenticated message\n"); + } else { + isns_debug_message("Received message\n"); + } + return msg; +} + +int +isns_socket_send(isns_socket_t *sock, isns_message_t *msg) +{ + struct isns_hdr *hdr; + size_t pdu_len; + buf_t *bp; + + /* If the socket is disconnected, and the + * reconnect timeout is not set, force a + * reconnect right away. */ + if (sock->is_state == ISNS_SOCK_DISCONNECTED + && !timerisset(&sock->is_deadline)) { + isns_net_set_timeout(sock, + isns_net_stream_reconnect, 0); + } + + if (!(bp = msg->im_payload)) + return 0; + + pdu_len = buf_avail(bp); + if (pdu_len < sizeof(*hdr)) + return 0; + + /* Pad PDU to multiple of 4 bytes, if needed */ + if (pdu_len & 3) { + unsigned int pad = 4 - (pdu_len & 3); + + if (!buf_put(bp, "\0\0\0", pad)) + return 0; + pdu_len += pad; + } + + if (!(bp = buf_dup(bp))) + return 0; + + hdr = buf_head(bp); + + hdr->i_version = htons(msg->im_header.i_version); + hdr->i_function = htons(msg->im_header.i_function); + hdr->i_flags = htons(msg->im_header.i_flags); + hdr->i_length = htons(pdu_len - sizeof(*hdr)); + hdr->i_xid = htons(msg->im_header.i_xid); + hdr->i_seq = htons(msg->im_header.i_seq); + + /* For now, we deal with unfragmented messages only. */ + hdr->i_flags |= htons(ISNS_F_FIRST_PDU|ISNS_F_LAST_PDU); + + if (sock->is_security) { +#ifdef WITH_SECURITY + hdr->i_flags |= htons(ISNS_F_AUTHBLK_PRESENT); + if (!isns_pdu_seal(sock->is_security, bp)) { + isns_debug_message("Error adding auth block to outgoing PDU\n"); + goto error; + } +#else + isns_debug_message("%s: Authentication not supported\n", + __FUNCTION__); + goto error; +#endif + } + + bp->addr = msg->im_addr; + bp->addrlen = msg->im_addrlen; + + buf_list_append(&sock->is_xmit_buf, bp); + sock->is_poll_mask |= POLLOUT; + + /* Set the retransmit timeout */ + __set_timeout(&msg->im_resend_timeout, sock->is_retrans_timeout); + return 1; + +error: + buf_free(bp); + return 0; +} + +/* + * Queue a message to a socket + */ +int +isns_socket_queue_message(isns_socket_t *sock, isns_message_t *msg) +{ + if (!isns_socket_send(sock, msg)) + return 0; + + /* Insert sorted by timeout. For now, this amounts to + * appending at the end of the list, but that may change + * if we implement exponential backoff for UDP */ + isns_message_queue_insert_sorted(&sock->is_pending, + ISNS_MQ_SORT_RESEND_TIMEOUT, msg); + msg->im_socket = sock; + return 1; +} + +/* + * Retransmit any queued messages + */ +int +isns_socket_retransmit_queued(isns_socket_t *sock) +{ + isns_message_t *msg; + isns_list_t *pos; + + isns_debug_socket("%s(%p)\n", __FUNCTION__, sock); + isns_message_queue_foreach(&sock->is_pending, pos, msg) { + if (!isns_socket_send(sock, msg)) + isns_warning("Unable to retransmit message\n"); + } + return 1; +} + +/* + * Submit a message to the socket, for asynchronous calls + */ +int +isns_socket_submit(isns_socket_t *sock, isns_message_t *msg, long timeout) +{ + if (timeout <= 0) + timeout = isns_config.ic_network.call_timeout; + + __set_timeout(&msg->im_timeout, timeout); + return isns_socket_queue_message(sock, msg); +} + +/* + * Transmit a message and wait for a response. + */ +isns_message_t * +isns_socket_call(isns_socket_t *sock, isns_message_t *msg, long timeout) +{ + isns_message_t *resp; + + debug_verbose("isns_socket_call(sock=%p, msg=%p, timeout=%ld)\n", + sock, msg, timeout); + if (timeout <= 0) + timeout = isns_config.ic_network.call_timeout; + + __set_timeout(&msg->im_timeout, timeout); + if (!isns_socket_queue_message(sock, msg)) + return NULL; + + sock->is_report_failure = 1; + resp = __isns_recv_message(NULL, msg); + sock->is_report_failure = 0; + + if (isns_message_unlink(msg)) { + /* We can get here if __isns_recv_message returned + * due to a fatal socket error. */ + isns_debug_socket("%s: msg not unlinked!\n", __FUNCTION__); + isns_message_release(msg); + } + + if (resp == NULL && sock->is_type == SOCK_STREAM) + isns_net_close(sock, ISNS_SOCK_DISCONNECTED); + + return resp; +} + +/* + * Resolve a hostname + */ +struct addrinfo * +isns_get_address_list(const char *addrspec, const char *port, + int af_hint, int sock_type, int flags) +{ + struct addrinfo hints, *found = NULL, *res = NULL; + char *copy = NULL, *host = NULL, *s; + int rv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + + if (addrspec && addrspec[0] == '/') { + if (af_hint != AF_LOCAL && af_hint != AF_UNSPEC) { + isns_debug_socket("Path as address, but af_hint=%d\n", + af_hint); + goto bad_address; + } + + res = make_addrinfo_unix(addrspec, SOCK_STREAM); + goto out; + } + + if (addrspec) { + copy = host = isns_strdup(addrspec); + if (*host == '[') { + hints.ai_flags |= AI_NUMERICHOST; + if ((s = strchr(host, ']')) == NULL) + goto bad_address; + + *s++ = '\0'; + if (*s == ':') + port = ++s; + else if (*s) + goto bad_address; + } else if ((s = strchr(host, ':')) != NULL) { + *s++ = '\0'; + if (!*s) + goto bad_address; + port = s; + } + + if (*host == '\0') + host = NULL; + } else if (port == NULL) { + /* Just wildcard */ + res = make_addrinfo_any(af_hint, sock_type); + goto out; + } + + hints.ai_family = af_hint; + hints.ai_flags |= flags; + hints.ai_socktype = sock_type; + if (af_hint == AF_INET6) + hints.ai_flags |= AI_V4MAPPED; + + rv = getaddrinfo(host, port, &hints, &found); + if (rv) { + isns_error("Cannot resolve address \"%s\": %s\n", + addrspec, gai_strerror(rv)); + goto out; + } + + if (found == NULL) { + isns_error("No useable addresses returned.\n"); + goto out; + } + + res = clone_addrinfo(found); + +out: + if (found) + freeaddrinfo(found); + isns_free(copy); + return res; + +bad_address: + isns_error("Cannot parse address spec \"%s\"\n", + addrspec); + goto out; +} + +int +isns_get_address(struct sockaddr_storage *result, + const char *addrspec, + const char *port, + int af_hint, int sock_type, int flags) +{ + struct addrinfo *ai; + int alen; + + if (!(ai = isns_get_address_list(addrspec, port, af_hint, sock_type, flags))) + return -1; + + alen = ai->ai_addrlen; + if (alen > sizeof(*result)) + return -1; + memcpy(result, ai->ai_addr, alen); + release_addrinfo(ai); + return alen; +} + +/* + * Get the canonical hostname + */ +char * +isns_get_canon_name(const char *hostname) +{ + struct addrinfo hints, *res = NULL; + char *fqdn = NULL; + int rv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + rv = getaddrinfo(hostname, NULL, &hints, &res); + if (rv) { + isns_error("Cannot resolve hostname \"%s\": %s\n", + hostname, gai_strerror(rv)); + goto out; + } + + if (res == NULL) { + isns_error("No useable addresses returned.\n"); + goto out; + } + + + fqdn = isns_strdup(res->ai_canonname); + +out: + if (res) + freeaddrinfo(res); + return fqdn; +} + +int +isns_socket_get_local_addr(const isns_socket_t *sock, + struct sockaddr_storage *addr) +{ + socklen_t alen; + + if (sock->is_desc < 0) + return 0; + if (getsockname(sock->is_desc, + (struct sockaddr *) addr, &alen) < 0) { + isns_error("getsockname: %m\n"); + return 0; + } + + return 1; +} + +int +isns_socket_get_portal_info(const isns_socket_t *sock, + isns_portal_info_t *portal) +{ + struct sockaddr_storage addr; + socklen_t alen; + int fd, success = 0; + + memset(portal, 0, sizeof(*portal)); + + /* If the socket is currently closed (eg because the + * server shut down the connection), we cannot get the + * local address easily. Create a temporary UDP socket, + * connect it, and query that socket. */ + if ((fd = sock->is_desc) < 0) { + const struct sockaddr *daddr; + + daddr = (struct sockaddr *) &sock->is_dst.addr; + fd = socket(daddr->sa_family, SOCK_DGRAM, 0); + if (fd < 0) + goto out; + if (connect(fd, daddr, sizeof(sock->is_dst.addr)) < 0) + goto out; + } + + alen = sizeof(addr); + if (getsockname(fd, (struct sockaddr *) &addr, &alen) < 0) { + isns_error("getsockname: %m\n"); + goto out; + } + + if (!isns_portal_from_sockaddr(portal, &addr)) + goto out; + if (sock->is_type == SOCK_STREAM) + portal->proto = IPPROTO_TCP; + else + portal->proto = IPPROTO_UDP; + + debug_verbose("socket_get_portal: %s\n", isns_portal_string(portal)); + success = 1; + +out: + /* If we used a temp UDP socket, close it */ + if (fd >= 0 && fd != sock->is_desc) + close(fd); + return success; +} + +isns_socket_t * +isns_socket_find_server(const isns_portal_info_t *portal) +{ + struct sockaddr_storage bound_addr; + int sock_type, addr_len; + isns_list_t *pos, *next; + + addr_len = isns_portal_to_sockaddr(portal, &bound_addr); + if ((sock_type = isns_socket_type_from_portal(portal)) < 0) + return NULL; + + isns_list_foreach(&all_sockets, pos, next) { + isns_socket_t *sock = isns_list_item(isns_socket_t, is_list, pos); + + if (!sock->is_client + && sock->is_type == sock_type + && sock->is_dst.addrlen == addr_len + && !memcmp(&sock->is_dst.addr, &bound_addr, addr_len)) { + sock->is_users++; + return sock; + } + } + + return NULL; +} + +int +isns_addr_get_port(const struct sockaddr *addr) +{ + const struct sockaddr_in *sin; + const struct sockaddr_in6 *six; + + switch (addr->sa_family) { + case AF_INET: + sin = (const struct sockaddr_in *) addr; + return ntohs(sin->sin_port); + + case AF_INET6: + six = (const struct sockaddr_in6 *) addr; + return ntohs(six->sin6_port); + } + return 0; +} + +void +isns_addr_set_port(struct sockaddr *addr, unsigned int port) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *six; + + switch (addr->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *) addr; + sin->sin_port = htons(port); + break; + + case AF_INET6: + six = (struct sockaddr_in6 *) addr; + six->sin6_port = htons(port); + break; + } +} diff --git a/utils/open-isns/socket.h b/utils/open-isns/socket.h new file mode 100644 index 0000000..cc63d23 --- /dev/null +++ b/utils/open-isns/socket.h @@ -0,0 +1,95 @@ +/* + * iSNS network code + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_SOCKET_H +#define ISNS_SOCKET_H + +#include "isns.h" +#include "buffer.h" +#include "message.h" + +struct isns_partial_msg { + isns_message_t imp_base; + uint32_t imp_flags; + uint32_t imp_first_seq; + uint32_t imp_last_seq; + unsigned int imp_pdu_count; + unsigned int imp_msg_size; + buf_t * imp_chain; + + struct ucred imp_credbuf; +}; + +#define imp_users imp_base.im_users +#define imp_list imp_base.im_list +#define imp_xid imp_base.im_xid +#define imp_header imp_base.im_header +#define imp_addr imp_base.im_addr +#define imp_addrlen imp_base.im_addrlen +#define imp_header imp_base.im_header +#define imp_payload imp_base.im_payload +#define imp_security imp_base.im_security +#define imp_creds imp_base.im_creds + +enum { + ISNS_SOCK_LISTENING, + ISNS_SOCK_CONNECTING, + ISNS_SOCK_IDLE, + ISNS_SOCK_FAILED, + ISNS_SOCK_DISCONNECTED, + ISNS_SOCK_DEAD, +}; + +/* Helper class */ +struct __isns_socket_addr { + struct sockaddr_storage addr; + socklen_t addrlen; + struct addrinfo * list; +}; + +struct isns_socket { + isns_list_t is_list; + int is_desc; + int is_type; + unsigned int is_client : 1, + is_autoclose : 1, + is_disconnect_fatal : 1, + is_report_failure : 1, + is_destroy : 1; + unsigned int is_users; + int is_poll_mask; + int is_state; + + isns_security_t * is_security; + + struct __isns_socket_addr is_src, is_dst; + + unsigned int is_retrans_timeout; + + /* If we're past this time, is_timeout() is called. */ + struct timeval is_deadline; + + buf_t * is_recv_buf; + buf_t * is_xmit_buf; + + size_t is_queue_size; + isns_message_queue_t is_partial; + isns_message_queue_t is_complete; + isns_message_queue_t is_pending; + + void (*is_poll_in)(isns_socket_t *); + void (*is_poll_out)(isns_socket_t *); + void (*is_poll_hup)(isns_socket_t *); + void (*is_poll_err)(isns_socket_t *); + void (*is_timeout)(isns_socket_t *); + void (*is_error)(isns_socket_t *, int); +}; + +extern int isns_socket_submit(isns_socket_t *, + isns_message_t *, + long); + +#endif /* ISNS_SOCKET_H */ diff --git a/utils/open-isns/source.h b/utils/open-isns/source.h new file mode 100644 index 0000000..59fb662 --- /dev/null +++ b/utils/open-isns/source.h @@ -0,0 +1,32 @@ +/* + * iSNS source attribute handling + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_SOURCE_H +#define ISNS_SOURCE_H + +#include "attrs.h" + +struct isns_source { + unsigned int is_users; + isns_attr_t * is_attr; + unsigned int is_untrusted : 1; + + isns_object_t * is_node; + unsigned int is_node_type; + + isns_object_t * is_entity; +}; + +extern int isns_source_encode(buf_t *, const isns_source_t *); +extern int isns_source_decode(buf_t *, isns_source_t **); +extern int isns_source_set_node(isns_source_t *, isns_db_t *); +extern void isns_source_set_entity(isns_source_t *, isns_object_t *); +extern isns_source_t * isns_source_dummy(void); + +extern char * isns_build_source_pattern(const char *); +extern int isns_source_pattern_match(const char *, const char *); + +#endif /* ISNS_SOURCE_H */ diff --git a/utils/open-isns/storage-node.c b/utils/open-isns/storage-node.c new file mode 100644 index 0000000..97e54d1 --- /dev/null +++ b/utils/open-isns/storage-node.c @@ -0,0 +1,202 @@ +/* + * iSNS object model - storage node + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "isns.h" +#include "objects.h" +#include "util.h" + +isns_object_t * +isns_create_storage_node(const char *name, uint32_t type, + isns_object_t *parent) +{ + isns_object_t *obj; + + if (parent && !ISNS_IS_ENTITY(parent)) { + isns_warning("Invalid container type \"%s\" for storage node: " + "should be \"%s\"\n", + parent->ie_template->iot_name, + isns_entity_template.iot_name); + return NULL; + } + + obj = isns_create_object(&isns_iscsi_node_template, NULL, parent); + isns_object_set_string(obj, + ISNS_TAG_ISCSI_NAME, name); + isns_object_set_uint32(obj, + ISNS_TAG_ISCSI_NODE_TYPE, type); + + return obj; +} + +isns_object_t * +isns_create_storage_node2(const isns_source_t *source, + uint32_t type, + isns_object_t *parent) +{ + isns_attr_t *name_attr; + isns_object_t *obj; + + if (parent && !ISNS_IS_ENTITY(parent)) { + isns_warning("Invalid container type \"%s\" for storage node: " + "should be \"%s\"\n", + parent->ie_template->iot_name, + isns_entity_template.iot_name); + return NULL; + } + if ((name_attr = isns_source_attr(source)) == NULL) { + isns_warning("No source attribute\n"); + return NULL; + } + + if (name_attr->ia_tag_id == ISNS_TAG_ISCSI_NAME) { + obj = isns_create_object(&isns_iscsi_node_template, NULL, parent); + isns_attr_list_update_attr(&obj->ie_attrs, name_attr); + isns_object_set_uint32(obj, + ISNS_TAG_ISCSI_NODE_TYPE, type); + } else { + /* No iFCP yet, sorry */ + isns_warning("%s: source tag type %u not supported\n", + __FUNCTION__); + return NULL; + } + + return obj; +} + +isns_object_t * +isns_create_iscsi_initiator(const char *name, + isns_object_t *parent) +{ + return isns_create_storage_node(name, + 1 << ISNS_ISCSI_NODE_TYPE_INITIATOR, + parent); +} + +isns_object_t * +isns_create_iscsi_target(const char *name, + isns_object_t *parent) +{ + return isns_create_storage_node(name, + 1 << ISNS_ISCSI_NODE_TYPE_TARGET, + parent); +} + +const char * +isns_storage_node_name(const isns_object_t *node) +{ + const isns_attr_t *attr; + + if (node->ie_attrs.ial_count == 0) + return NULL; + attr = node->ie_attrs.ial_data[0]; + if (attr->ia_value.iv_type != &isns_attr_type_string) + return NULL; + + switch (attr->ia_tag_id) { + case ISNS_TAG_ISCSI_NAME: + case ISNS_TAG_FC_PORT_NAME_WWPN: + return attr->ia_value.iv_string; + } + + return 0; + +} + +isns_attr_t * +isns_storage_node_key_attr(const isns_object_t *node) +{ + if (node->ie_attrs.ial_count == 0) + return NULL; + return node->ie_attrs.ial_data[0]; +} + +static uint32_t iscsi_node_attrs[] = { + ISNS_TAG_ISCSI_NAME, + ISNS_TAG_ISCSI_NODE_TYPE, + ISNS_TAG_ISCSI_ALIAS, + ISNS_TAG_ISCSI_SCN_BITMAP, + ISNS_TAG_ISCSI_NODE_INDEX, + ISNS_TAG_WWNN_TOKEN, + ISNS_TAG_ISCSI_AUTHMETHOD, + /* RFC 4171 lists a "iSCSI node certificate" + * as an option attribute of an iSCSI + * storage node, but doesn't define it anywhere + * in the spec. + */ +}; + +static uint32_t iscsi_node_key_attrs[] = { + ISNS_TAG_ISCSI_NAME, +}; + +isns_object_template_t isns_iscsi_node_template = { + .iot_name = "iSCSI Storage Node", + .iot_handle = ISNS_OBJECT_TYPE_NODE, + .iot_attrs = iscsi_node_attrs, + .iot_num_attrs = array_num_elements(iscsi_node_attrs), + .iot_keys = iscsi_node_key_attrs, + .iot_num_keys = array_num_elements(iscsi_node_key_attrs), + .iot_index = ISNS_TAG_ISCSI_NODE_INDEX, + .iot_next_index = ISNS_TAG_ISCSI_NODE_NEXT_INDEX, + .iot_container = &isns_entity_template, +}; + +static uint32_t fc_port_attrs[] = { + ISNS_TAG_FC_PORT_NAME_WWPN, + ISNS_TAG_PORT_ID, + ISNS_TAG_FC_PORT_TYPE, + ISNS_TAG_SYMBOLIC_PORT_NAME, + ISNS_TAG_FABRIC_PORT_NAME, + ISNS_TAG_HARD_ADDRESS, + ISNS_TAG_PORT_IP_ADDRESS, + ISNS_TAG_CLASS_OF_SERVICE, + ISNS_TAG_FC4_TYPES, + ISNS_TAG_FC4_DESCRIPTOR, + ISNS_TAG_FC4_FEATURES, + ISNS_TAG_IFCP_SCN_BITMAP, + ISNS_TAG_PORT_ROLE, + ISNS_TAG_PERMANENT_PORT_NAME, +}; + +static uint32_t fc_port_key_attrs[] = { + ISNS_TAG_FC_PORT_NAME_WWPN, +}; + +isns_object_template_t isns_fc_port_template = { + .iot_name = "iFCP Port", + .iot_handle = ISNS_OBJECT_TYPE_FC_PORT, + .iot_attrs = fc_port_attrs, + .iot_num_attrs = array_num_elements(fc_port_attrs), + .iot_keys = fc_port_key_attrs, + .iot_num_keys = array_num_elements(fc_port_key_attrs), + .iot_container = &isns_entity_template, +}; + +static uint32_t fc_node_attrs[] = { + ISNS_TAG_FC_NODE_NAME_WWNN, + ISNS_TAG_SYMBOLIC_NODE_NAME, + ISNS_TAG_NODE_IP_ADDRESS, + ISNS_TAG_NODE_IPA, + ISNS_TAG_PROXY_ISCSI_NAME, +}; + +static uint32_t fc_node_key_attrs[] = { + ISNS_TAG_FC_NODE_NAME_WWNN, +}; + +isns_object_template_t isns_fc_node_template = { + .iot_name = "iFCP Device Node", + .iot_handle = ISNS_OBJECT_TYPE_FC_NODE, + .iot_attrs = fc_node_attrs, + .iot_num_attrs = array_num_elements(fc_node_attrs), + .iot_keys = fc_node_key_attrs, + .iot_num_keys = array_num_elements(fc_node_key_attrs), + .iot_container = &isns_fc_port_template, +}; + diff --git a/utils/open-isns/sysdep-unix.c b/utils/open-isns/sysdep-unix.c new file mode 100644 index 0000000..8c601a7 --- /dev/null +++ b/utils/open-isns/sysdep-unix.c @@ -0,0 +1,132 @@ +/* + * System dependent stuff + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <net/if.h> +#include <sys/ioctl.h> +#include <string.h> +#include <unistd.h> +#include "isns.h" +#include "util.h" + +int +isns_enumerate_portals(isns_portal_info_t *result, unsigned int max) +{ + char buffer[8192], *end, *ptr; + struct ifconf ifc; + unsigned int nportals = 0; + int fd = -1; + + if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + isns_error("%s: no socket - %m\n", __FUNCTION__); + return 0; + } + + ifc.ifc_buf = buffer; + ifc.ifc_len = sizeof(buffer); + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + isns_error("ioctl(SIOCGIFCONF): %m\n"); + goto out; + } + + ptr = buffer; + end = buffer + ifc.ifc_len; + while (ptr < end) { + struct ifreq ifr; + struct sockaddr_storage ifaddr; + isns_portal_info_t portal; + int ifflags; + + memcpy(&ifr, ptr, sizeof(ifr)); + ptr += sizeof(ifr); + + /* Get the interface addr */ + memcpy(&ifaddr, &ifr.ifr_addr, sizeof(ifr.ifr_addr)); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + isns_error("ioctl(%s, SIOCGIFFLAGS): %m\n", + ifr.ifr_name); + continue; + } + ifflags = ifr.ifr_flags; + + if ((ifflags & IFF_UP) == 0) + continue; + if ((ifflags & IFF_LOOPBACK) != 0) + continue; + + if (!isns_portal_from_sockaddr(&portal, &ifaddr)) + continue; + + isns_debug_socket("Got interface %u: %s %s\n", + nportals, ifr.ifr_name, + isns_portal_string(&portal)); + if (nportals < max) + result[nportals++] = portal; + } + +out: + if (fd >= 0) + close(fd); + return nportals; +} + +int +isns_portal_from_sockaddr(isns_portal_info_t *portal, + const struct sockaddr_storage *addr) +{ + struct sockaddr_in6 *six; + struct sockaddr_in *sin; + + memset(portal, 0, sizeof(*portal)); + + /* May have to convert AF_INET to AF_INET6 */ + six = &portal->addr; + switch (addr->ss_family) { + case AF_INET6: + memcpy(six, addr, sizeof(*six)); + break; + + case AF_INET: + sin = (struct sockaddr_in *) addr; + six->sin6_family = AF_INET6; + six->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; + six->sin6_port = sin->sin_port; + break; + + default: + return 0; + } + + return 1; +} + +int +isns_portal_to_sockaddr(const isns_portal_info_t *portal, + struct sockaddr_storage *addr) +{ + const struct sockaddr_in6 *six = &portal->addr; + struct sockaddr_in *sin; + + /* Check if this is really a v4 address is disguise. + * If so, explicitly use an AF_INET socket - the + * stack may not support IPv6. + */ + if (IN6_IS_ADDR_V4MAPPED(&six->sin6_addr) + || IN6_IS_ADDR_V4COMPAT(&six->sin6_addr)) { + sin = (struct sockaddr_in *) addr; + + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = six->sin6_addr.s6_addr32[3]; + sin->sin_port = six->sin6_port; + + return sizeof(*sin); + } + + /* This is the genuine article */ + memcpy(addr, six, sizeof(*six)); + return sizeof(*six); +} diff --git a/utils/open-isns/tags.c b/utils/open-isns/tags.c new file mode 100644 index 0000000..7413cee --- /dev/null +++ b/utils/open-isns/tags.c @@ -0,0 +1,740 @@ +/* + * Define all iSNS tags with their types, etc. + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include "isns-proto.h" +#include "vendor.h" +#include "attrs.h" +#include "security.h" +#include "objects.h" +#include "util.h" + +#define ISNS_MAX_BUILTIN_TAG 4096 + + +static void print_bitfield(unsigned long, char **, char *, size_t); +static int parse_bitfield( char **, const char *, uint32_t *); +static const char *help_bitfield(char **); + +#define DECLARE_VALIDATOR(name) \ +static int isns_##name##_validate(const isns_value_t *, const isns_policy_t *); +#define DECLARE_ACCESSORS(name) \ +static int isns_##name##_parse(isns_value_t *, const char *buf); \ +static void isns_##name##_print(const isns_value_t *, char *buf, size_t size); \ +static const char * isns_##name##_help(void) +#define USE_VALIDATOR(name) \ + .it_validate = isns_##name##_validate +#define USE_ACCESSORS(name) \ + .it_parse = isns_##name##_parse, \ + .it_print = isns_##name##_print, \ + .it_help = isns_##name##_help + +DECLARE_VALIDATOR(entity_protocol); +DECLARE_ACCESSORS(entity_protocol); +DECLARE_ACCESSORS(tcpudp_port); +DECLARE_VALIDATOR(iscsi_node_type); +DECLARE_ACCESSORS(iscsi_node_type); +DECLARE_ACCESSORS(timestamp); +DECLARE_ACCESSORS(portal_secbitmap); +DECLARE_ACCESSORS(scn_bitmap); +DECLARE_ACCESSORS(dd_features); +DECLARE_ACCESSORS(policy_object_type); +DECLARE_ACCESSORS(policy_function); + +static const char *isns_authmethod_help(void); + +#define TAG(ID, name, type, args...) \ +[ISNS_TAG_##ID] = { \ + .it_id = ISNS_TAG_##ID, \ + .it_name = name, \ + .it_type = &isns_attr_type_##type, \ + args \ +} + +static isns_tag_type_t isns_tags[ISNS_MAX_BUILTIN_TAG] = { +TAG(DELIMITER, "Delimiter", nil), +TAG(ENTITY_IDENTIFIER, "Entity identifier", string), +TAG(ENTITY_PROTOCOL, "Entity protocol", uint32, + USE_VALIDATOR(entity_protocol), + USE_ACCESSORS(entity_protocol)), +TAG(MGMT_IP_ADDRESS, "Mgmt IP address", ipaddr), +TAG(TIMESTAMP, "Timestamp", uint64, + USE_ACCESSORS(timestamp), + .it_readonly = 1), +TAG(PROTOCOL_VERSION_RANGE, "Protocol version range", range16), +TAG(REGISTRATION_PERIOD, "Registration Period", uint32), +TAG(ENTITY_INDEX, "Entity index", uint32, + .it_readonly = 1), +TAG(ENTITY_NEXT_INDEX, "Entity next index", uint32, + .it_readonly = 1), +TAG(PORTAL_IP_ADDRESS, "Portal IP address", ipaddr), +TAG(PORTAL_TCP_UDP_PORT, "Portal TCP/UDP port", uint32, + USE_ACCESSORS(tcpudp_port)), +TAG(ESI_INTERVAL, "ESI interval", uint32), +TAG(ESI_PORT, "ESI port", uint32, + USE_ACCESSORS(tcpudp_port)), +TAG(PORTAL_SYMBOLIC_NAME, "Portal name", string), +TAG(PORTAL_INDEX, "Portal index", uint32), +TAG(SCN_PORT, "SCN port", uint32, + USE_ACCESSORS(tcpudp_port)), +TAG(PORTAL_SECURITY_BITMAP, "Portal security bitmap", uint32, + USE_ACCESSORS(portal_secbitmap)), +TAG(PORTAL_NEXT_INDEX, "Portal next index", uint32, + .it_readonly = 1), + +TAG(ISCSI_NAME, "iSCSI name", string), +TAG(ISCSI_NODE_TYPE, "iSCSI node type", uint32, + USE_VALIDATOR(iscsi_node_type), + USE_ACCESSORS(iscsi_node_type)), +TAG(ISCSI_ALIAS, "iSCSI alias", string), +TAG(ISCSI_SCN_BITMAP, "iSCSI SCN bitmap", uint32, + USE_ACCESSORS(scn_bitmap)), +TAG(ISCSI_NODE_INDEX, "iSCSI node index", uint32, + .it_readonly = 1), +TAG(WWNN_TOKEN, "WWNN token", uint64), +TAG(ISCSI_NODE_NEXT_INDEX, "iSCSI node next index",uint32, + .it_readonly = 1), +TAG(ISCSI_AUTHMETHOD, "iSCSI auth method", string, + .it_help = isns_authmethod_help), + +TAG(PG_ISCSI_NAME, "Portal group name", string), +TAG(PG_PORTAL_IP_ADDR, "Portal group address", ipaddr), +TAG(PG_PORTAL_TCP_UDP_PORT, "Portal group port", uint32, + USE_ACCESSORS(tcpudp_port)), +TAG(PG_TAG, "Portal group tag", uint32), +TAG(PG_INDEX, "Portal group index", uint32, + .it_readonly = 1), +TAG(PG_NEXT_INDEX, "Portal group next index",uint32, + .it_readonly = 1), + +/* FC Port */ +TAG(FC_PORT_NAME_WWPN, "FC port name WWPN", uint64), +TAG(PORT_ID, "FC port ID", uint32), +TAG(FC_PORT_TYPE, "FC port type", uint32), +TAG(SYMBOLIC_PORT_NAME, "FC symbolic port name",string), +TAG(FABRIC_PORT_NAME, "FC fabric port name", uint64), +TAG(HARD_ADDRESS, "FC hard", uint32), +TAG(PORT_IP_ADDRESS, "FC Port IP address", ipaddr), +TAG(CLASS_OF_SERVICE, "FC service class", uint32), +TAG(FC4_TYPES, "FC4 types", opaque), +TAG(FC4_DESCRIPTOR, "FC4 descriptor", string), +TAG(FC4_FEATURES, "FC4 features", opaque), +TAG(IFCP_SCN_BITMAP, "iFCP SCN bitmap", uint32, + USE_ACCESSORS(scn_bitmap)), +TAG(PORT_ROLE, "FC port role", uint32), +TAG(PERMANENT_PORT_NAME, "FC permanent port name",uint64), +TAG(FC4_TYPE_CODE, "FC4 type code", uint32), + +/* FC Node */ +TAG(FC_NODE_NAME_WWNN, "FC node name", uint64), +TAG(SYMBOLIC_NODE_NAME, "FC symbolic node name",string), +TAG(NODE_IP_ADDRESS, "FC node IP address", ipaddr), +TAG(NODE_IPA, "FC node IPA", uint64), +TAG(PROXY_ISCSI_NAME, "FC node proxy iSCSI name",string), + +/* Other FC tags to go here */ + +/* Discovery domain set */ +TAG(DD_SET_ID, "DD set ID", uint32), +TAG(DD_SET_SYMBOLIC_NAME, "DD set name", string), +TAG(DD_SET_STATUS, "DD set status", uint32), +TAG(DD_SET_NEXT_ID, "DD set next ID", uint32, + .it_readonly = 1), + +/* Discovery domain */ +TAG(DD_ID, "DD ID", uint32), +TAG(DD_SYMBOLIC_NAME, "DD name", string), +TAG(DD_MEMBER_ISCSI_INDEX, "DD member iSCSI index",uint32, + .it_multiple = 1), +TAG(DD_MEMBER_ISCSI_NAME, "DD member iSCSI name", string, + .it_multiple = 1), +TAG(DD_MEMBER_FC_PORT_NAME, "DD member FC WWPN", string, + .it_multiple = 1), +TAG(DD_MEMBER_PORTAL_INDEX, "DD member portal index",uint32, + .it_multiple = 1), +TAG(DD_MEMBER_PORTAL_IP_ADDR, "DD member portal addr",ipaddr, + .it_multiple = 1), +TAG(DD_MEMBER_PORTAL_TCP_UDP_PORT,"DD member portal port",uint32, + USE_ACCESSORS(tcpudp_port), + .it_multiple = 1), +TAG(DD_FEATURES, "DD features", uint32, + USE_ACCESSORS(dd_features)), +TAG(DD_NEXT_ID, "DD next ID", uint32, + .it_readonly = 1), +}; + +/* + * End of RFC defined tags + */ +#undef TAG + +/* + * Open-iSNS vendor specific tags + */ +#define TAG(ID, name, type, args...) \ +{ \ + .it_id = OPENISNS_TAG_##ID, \ + .it_name = name, \ + .it_type = &isns_attr_type_##type, \ + args \ +} + +static isns_tag_type_t isns_vendor_tags[] = { +TAG(POLICY_SPI, "Security Policy Index", string), +TAG(POLICY_KEY, "DSA security key", opaque), +TAG(POLICY_ENTITY, "Policy allowed entity name", string), +TAG(POLICY_OBJECT_TYPE, "Policy allowed object types", uint32, + USE_ACCESSORS(policy_object_type)), +TAG(POLICY_NODE_NAME, "Policy allowed node name", string, + .it_multiple = 1), +TAG(POLICY_NODE_TYPE, "Policy allowed node type", uint32, + USE_VALIDATOR(iscsi_node_type), + USE_ACCESSORS(iscsi_node_type)), +TAG(POLICY_FUNCTIONS, "Policy allowed functions", uint32, + USE_ACCESSORS(policy_function)), +TAG(POLICY_VISIBLE_DD, "Visible Discovery Domain", string, + .it_multiple = 1), +TAG(POLICY_DEFAULT_DD, "Default Discovery Domain", string), + +{ 0 } +}; + +/* + * End of vendor-specific tags + */ + +static isns_tag_type_t isns_unknown_tag = { + .it_id = 0xffff, + .it_name = "unknown", + .it_type = &isns_attr_type_opaque, +}; + +/* + * Map iSNS attribute tag to its data type + */ +const isns_tag_type_t * +isns_tag_type_by_id(uint32_t id) +{ + isns_tag_type_t *tag; + + if (id < ISNS_MAX_BUILTIN_TAG) { + tag = &isns_tags[id]; + if (tag->it_type == NULL) { + *tag = isns_unknown_tag; + tag->it_id = id; + } + return tag; + } + + for (tag = isns_vendor_tags; tag->it_name; ++tag) { + if (tag->it_id == id) + return tag; + } + + return &isns_unknown_tag; +} + +/* + * Specific validators/pretty printers + */ +int +isns_entity_protocol_validate(const isns_value_t *value, const isns_policy_t *policy) +{ + enum isns_entity_protocol protocol = value->iv_uint32; + + switch (protocol) { + case ISNS_ENTITY_PROTOCOL_NONE: + case ISNS_ENTITY_PROTOCOL_ISCSI: + case ISNS_ENTITY_PROTOCOL_IFCP: + return 1; + } + return 0; +} + +int +isns_entity_protocol_parse(isns_value_t *value, const char *string) +{ + uint32_t prot; + + if (!strcasecmp(string, "none")) + prot = ISNS_ENTITY_PROTOCOL_NONE; + else if (!strcasecmp(string, "iscsi")) + prot = ISNS_ENTITY_PROTOCOL_ISCSI; + else if (!strcasecmp(string, "ifcp")) + prot = ISNS_ENTITY_PROTOCOL_IFCP; + else + return 0; + value->iv_uint32 = prot; + return 1; +} + +void +isns_entity_protocol_print(const isns_value_t *value, char *buf, size_t size) +{ + enum isns_entity_protocol protocol = value->iv_uint32; + const char *prot_name; + + switch (protocol) { + case ISNS_ENTITY_PROTOCOL_NONE: + prot_name = "None"; + break; + + case ISNS_ENTITY_PROTOCOL_ISCSI: + prot_name = "iSCSI"; + break; + + case ISNS_ENTITY_PROTOCOL_IFCP: + prot_name = "iFCP"; + break; + + default: + prot_name = "Unknown"; + } + snprintf(buf, size, "%s (%u)", prot_name, protocol); +} + +const char * +isns_entity_protocol_help(void) +{ + return "one of None, iSCSI, iFCP"; +} + +/* + * TCP/UDP port + */ +int +isns_tcpudp_port_parse(isns_value_t *value, const char *string) +{ + uint32_t num; + const char *ep; + + num = strtoul(string, (char **) &ep, 0); + if (ep && *ep) { + if (!strcasecmp(ep, "/udp")) + num |= ISNS_PORTAL_PORT_UDP_MASK; + else + if (!strcasecmp(ep, "/tcp")) + /* nothing */; + else { + isns_error("Cannot parse port spec \"%s\"\n", + string); + return 0; + } + } + value->iv_uint32 = num; + return 1; +} + +void +isns_tcpudp_port_print(const isns_value_t *value, char *buf, size_t size) +{ + uint32_t portspec = value->iv_uint32, num; + + if (portspec == 0) { + snprintf(buf, size, "[default]"); + } else { + num = portspec & 0xffff; + if (portspec & ISNS_PORTAL_PORT_UDP_MASK) { + snprintf(buf, size, "%u/udp", num); + } else { + snprintf(buf, size, "%u/tcp", num); + } + } +} + +const char * +isns_tcpudp_port_help(void) +{ + return "<port>/tcp, <port>/udp, or <port> (defaults to TCP)"; +} + +int +isns_timestamp_parse(isns_value_t *value, const char *string) +{ + isns_error("Timestamp parsing not implemented\n"); + return 0; +} + +void +isns_timestamp_print(const isns_value_t *value, char *buf, size_t size) +{ + time_t timestamp = value->iv_uint64; + char *str, *s; + + str = ctime(×tamp); + if ((s = strchr(str, '\n')) != NULL) + *s = '\0'; + + snprintf(buf, size, "%s", str); +} + +const char * +isns_timestamp_help(void) +{ + return NULL; +} + +/* + * Helper macros to implement the off-the-shelf bitfield + * accessors. + */ +#define IMPLEMENT_BITFIELD_ACCESSORS(name) \ +int isns_##name##_parse(isns_value_t *value, const char *string) \ +{ \ + return parse_bitfield(name##_bit_names, string, \ + &value->iv_uint32); \ +} \ + \ +void \ +isns_##name##_print(const isns_value_t *value, char *buf, size_t size) \ +{ \ + print_bitfield(value->iv_uint32, name##_bit_names, \ + buf, size); \ +} \ + \ +const char * \ +isns_##name##_help(void) \ +{ \ + return help_bitfield(name##_bit_names); \ +} + + +static char * iscsi_node_type_bit_names[32] = { +[ISNS_ISCSI_NODE_TYPE_TARGET] = "Target", +[ISNS_ISCSI_NODE_TYPE_INITIATOR] = "Initiator", +[ISNS_ISCSI_NODE_TYPE_CONTROL] = "Control", +}; + +int +isns_iscsi_node_type_validate(const isns_value_t *value, const isns_policy_t *policy) +{ + uint32_t bits = value->iv_uint32, permitted; + + permitted = ISNS_ISCSI_INITIATOR_MASK | + ISNS_ISCSI_TARGET_MASK | + ISNS_ISCSI_CONTROL_MASK; + if (bits & ~permitted) + return 0; + + if (policy && !isns_policy_validate_node_type(policy, bits)) + return 0; + + return 1; +} + +IMPLEMENT_BITFIELD_ACCESSORS(iscsi_node_type); + +/* + * Portal Security Bitmap + */ +static char * portal_secbitmap_bit_names[32] = { +[ISNS_PORTAL_SEC_BITMAP_VALID] = "bitmap valid", +[ISNS_PORTAL_SEC_IPSEC_ENABLED] = "ipsec enabled", +[ISNS_PORTAL_SEC_MAIN_MODE_ENABLED] = "main mode enabled", +[ISNS_PORTAL_SEC_AGGR_MODE_ENABLED] = "aggressive mode enabled", +[ISNS_PORTAL_SEC_PFS_ENABLED] = "pfs enabled", +[ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED] = "transport mode preferred", +[ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED] = "tunnel mode preferred", +}; + +IMPLEMENT_BITFIELD_ACCESSORS(portal_secbitmap); + +/* + * SCN bitmap + */ +static char * scn_bitmap_bit_names[32] = { +[ISNS_SCN_DD_MEMBER_ADDED] = "DD/DDS member added", +[ISNS_SCN_DD_MEMBER_REMOVED] = "DD/DDS member removed", +[ISNS_SCN_OBJECT_UPDATED] = "object updated", +[ISNS_SCN_OBJECT_ADDED] = "object added", +[ISNS_SCN_OBJECT_REMOVED] = "object removed", +[ISNS_SCN_MANAGEMENT_REGISTRATION] = "management registration", +[ISNS_SCN_TARGET_AND_SELF_ONLY] = "target and self information only", +[ISNS_SCN_INITIATOR_AND_SELF_ONLY] = "initiator and self information only", +}; + +IMPLEMENT_BITFIELD_ACCESSORS(scn_bitmap); + +/* + * DD features bitmap + */ +static char * dd_features_bit_names[32] = { +[ISNS_DD_BOOT_LIST_ENABLED] = "Boot list enabled", +}; + +IMPLEMENT_BITFIELD_ACCESSORS(dd_features); + +/* + * Policy: list of allowed functions + */ +static char * policy_function_bit_names[32] = { +[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg", +[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQry", +[ISNS_DEVICE_GET_NEXT] = "DevGetNext", +[ISNS_DEVICE_DEREGISTER] = "DevDereg", +[ISNS_SCN_REGISTER] = "SCNReg", +[ISNS_SCN_DEREGISTER] = "SCNDereg", +[ISNS_SCN_EVENT] = "SCNEvent", +[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN", +[ISNS_DD_REGISTER] = "DDReg", +[ISNS_DD_DEREGISTER] = "DDDereg", +[ISNS_DDS_REGISTER] = "DDSReg", +[ISNS_DDS_DEREGISTER] = "DDSDereg", +[ISNS_ENTITY_STATUS_INQUIRY] = "ESI", +[ISNS_HEARTBEAT] = "Heartbeat", +}; + +IMPLEMENT_BITFIELD_ACCESSORS(policy_function); + +/* + * Policy: list of allowed node types + */ +static char * policy_object_type_bit_names[32] = { +[ISNS_OBJECT_TYPE_ENTITY] = "entity", +[ISNS_OBJECT_TYPE_NODE] = "iscsi-node", +[ISNS_OBJECT_TYPE_PORTAL] = "portal", +[ISNS_OBJECT_TYPE_PG] = "portal-group", +[ISNS_OBJECT_TYPE_DD] = "dd", +[ISNS_OBJECT_TYPE_DDSET] = "ddset", +[ISNS_OBJECT_TYPE_POLICY] = "policy", +}; + +static int +isns_policy_object_type_parse(isns_value_t *vp, const char *buf) +{ + char *copy, *s, *next; + int rv = 0; + + if (!strcasecmp(buf, "ALL")) { + vp->iv_uint32 = ~0; + return 1; + } + if (!strcasecmp(buf, "DEFAULT")) { + vp->iv_uint32 = ISNS_DEFAULT_OBJECT_ACCESS; + return 1; + } + + vp->iv_uint32 = 0; + copy = isns_strdup(buf); + for (s = copy; s; s = next) { + char *perm; + int bit, mask = 0; + + while (1) { + unsigned int n; + + n = strcspn(s, ",+;|"); + if (n) { + next = s + n; + if (*next) + *next++ = '\0'; + break; + } + ++n; + } + + mask = ISNS_PERMISSION_READ; + if ((perm = strchr(s, ':')) != NULL) { + *perm++ = '\0'; + mask = 0; + while (*perm) { + switch (*perm++) { + case 'R': case 'r': + mask = ISNS_PERMISSION_READ; + break; + case 'W': case 'w': + mask = ISNS_PERMISSION_READ; + break; + default: + goto failed; + } + } + } + + for (bit = 0; bit < 32; ++bit) { + if (policy_object_type_bit_names[bit] + && !strcasecmp(policy_object_type_bit_names[bit], s)) + goto found; + } + goto failed; + +found: vp->iv_uint32 |= ISNS_ACCESS(bit, mask); + } + rv = 1; + +failed: + isns_free(copy); + return rv; +} + +static void +isns_policy_object_type_print(const isns_value_t *vp, char *buf, size_t size) +{ + unsigned int i, pos = 0; + uint32_t mask; + const char *sepa = ""; + + mask = vp->iv_uint32; + if (mask == 0) { + snprintf(buf, size, "<empty>"); + return; + } + + for (i = 0; i < 32; ++i, mask >>= 2) { + const char *name; + + if (!(mask & 3)) + continue; + + name = policy_object_type_bit_names[i]; + if (name) + snprintf(buf + pos, size - pos, "%s%s:%s%s", sepa, name, + (mask & ISNS_PERMISSION_READ)? "r" : "", + (mask & ISNS_PERMISSION_WRITE)? "w" : ""); + else + snprintf(buf + pos, size - pos, "%sbit%u:%s%s",sepa, i, + (mask & ISNS_PERMISSION_READ)? "r" : "", + (mask & ISNS_PERMISSION_WRITE)? "w" : ""); + sepa = ", "; + pos = strlen(buf); + } +} + +static const char * +isns_policy_object_type_help(void) +{ + static char buffer[256]; + unsigned int i, n; + char *sepa = ""; + + strcpy(buffer, "bitfield (type:perm): perm=R, W, or RW; type="); + n = strlen(buffer); + + for (i = 0; i < 32; ++i) { + if (policy_object_type_bit_names[i]) { + snprintf(buffer + n, sizeof(buffer) - n, + "%s%s", sepa, + policy_object_type_bit_names[i]); + sepa = ", "; + } + } + return buffer; +} + +/* + * Help message for AuthMethod + */ +const char * +isns_authmethod_help(void) +{ + return "comma separated list, including of KRB5, SPKM1, SPKM2, SRP, CHAP, none"; +} + +/* + * Helper functions to deal with bitfields + */ +static void +print_bitfield(unsigned long value, char **bit_names, + char *buf, size_t size) +{ + unsigned int bit, mask; + const char *sepa = ""; + char *buf_end; + + if (value == 0) { + snprintf(buf, size, "<NIL>"); + return; + } + + buf_end = buf + size; + for (bit = 0, mask = 1; mask; ++bit, mask <<= 1) { + char namebuf[16], *name; + + if (!(value & mask)) + continue; + + if ((name = bit_names[bit]) == NULL) { + sprintf(namebuf, "bit%u", bit); + name = namebuf; + } + + snprintf(buf, buf_end - buf, "%s%s", sepa, name); + buf += strlen(buf); + sepa = ", "; + } +} + +static int +parse_bitfield(char **bit_names, + const char *string, + uint32_t *result) +{ + *result = 0; + + if (!strcasecmp(string, "ALL")) { + unsigned int bit; + + for (bit = 0; bit < 32; ++bit) { + if (bit_names[bit]) + *result |= 1 << bit; + } + return 1; + } + + if (!strcasecmp(string, "NONE")) + return 1; + + while (*string) { + unsigned int n, bit, match = 0; + + n = strcspn(string, ",+;|"); + if (n == 0) + goto next; + + for (bit = 0; bit < 32; ++bit) { + if (!bit_names[bit]) + continue; + if (!strncasecmp(bit_names[bit], string, n)) { + *result |= 1 << bit; + match++; + } + } + if (!match) + return 0; + +next: + string += n; + string += strspn(string, ",+;|"); + } + + return 1; +} + +static const char * +help_bitfield(char **bit_names) +{ + static char buffer[1024]; + char *pos, sepa = ':'; + unsigned int bit; + + strcpy(buffer, "bitfield"); + pos = strchr(buffer, '\0'); + + for (bit = 0; bit < 32; ++bit) { + if (bit_names[bit] == NULL) + continue; + + snprintf(pos, sizeof(buffer) - (pos - buffer), + "%c %s", sepa, bit_names[bit]); + + pos += strlen(pos); + sepa = ','; + } + return buffer; +} + diff --git a/utils/open-isns/tests/.cvsignore b/utils/open-isns/tests/.cvsignore new file mode 100644 index 0000000..fa1eb3c --- /dev/null +++ b/utils/open-isns/tests/.cvsignore @@ -0,0 +1,2 @@ +*.swp +pauw[1-9] diff --git a/utils/open-isns/tests/Makefile b/utils/open-isns/tests/Makefile new file mode 100644 index 0000000..778195a --- /dev/null +++ b/utils/open-isns/tests/Makefile @@ -0,0 +1,40 @@ +# +# Simple makefile to run regression tests, and to +# document how to run them. + +# +# Each test case is a perl script, testXX.pl. Run as +# perl testXX.pl +# Optionally followed by +# -q quiet - just print a header line, and the overall result +# -v verbose - display more detailed information, including the +# commands being run +# -f fast - skip tests that take more than 15 seconds +# +# The default is to be slightly verbose, and display a comment +# about each stage of the test. + +# All test related data is kept in /tmp/isns-test, with a +# subdirectory for each test. +# For instance, test01 will create +# /tmp/isns-test/test01/server0 +# /tmp/isns-test/test01/client1 +# /tmp/isns-test/test01/dump +# +# The server and client directories will contain configuration +# data, logfiles, and (for the server) the Unix socket, the +# PID file, and the database. +# +# The dump directory contains snapshots of the on-disk database +# for each test stage (if the test stage involves a verification +# of the database). + +tests: + @for test in test*.pl; do \ + perl $$test -q; \ + done + +quick: + @for test in test*.pl; do \ + perl $$test -q --fast; \ + done diff --git a/utils/open-isns/tests/client.conf b/utils/open-isns/tests/client.conf new file mode 100644 index 0000000..034a739 --- /dev/null +++ b/utils/open-isns/tests/client.conf @@ -0,0 +1,8 @@ +SourceName = @NOT_SET@ +AuthName = @NOT_SET@ +ServerAddress = @NOT_SET@ +BindAddress = @NOT_SET@ +Security = @NOT_SET@ +AuthKeyFile = @NOT_SET@ +ServerKeyFile = @NOT_SET@ +ControlSocket = @NOT_SET@ diff --git a/utils/open-isns/tests/data/test01/01-enroll b/utils/open-isns/tests/data/test01/01-enroll new file mode 100644 index 0000000..f59329b --- /dev/null +++ b/utils/open-isns/tests/data/test01/01-enroll @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test01/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test01/02-registration b/utils/open-isns/tests/data/test01/02-registration new file mode 100644 index 0000000..fd26f3c --- /dev/null +++ b/utils/open-isns/tests/data/test01/02-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test01/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 12:40:58 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test01/03-query b/utils/open-isns/tests/data/test01/03-query new file mode 100644 index 0000000..8208f83 --- /dev/null +++ b/utils/open-isns/tests/data/test01/03-query @@ -0,0 +1,20 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 12:41:41 2007 + 0007 uint32 : Entity index = 4 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test01/03-registration b/utils/open-isns/tests/data/test01/03-registration new file mode 100644 index 0000000..affd69a --- /dev/null +++ b/utils/open-isns/tests/data/test01/03-registration @@ -0,0 +1,20 @@ +Dumping database contents +Backend: /tmp/isns-test/test01/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "isns.client1" + 0602v string : Policy allowed source name = "isns.client1" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "isns.client2" + 0602v string : Policy allowed source name = "isns.client2" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... diff --git a/utils/open-isns/tests/data/test01/99-unregistration b/utils/open-isns/tests/data/test01/99-unregistration new file mode 100644 index 0000000..c7518ff --- /dev/null +++ b/utils/open-isns/tests/data/test01/99-unregistration @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test01/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test02/01-enroll b/utils/open-isns/tests/data/test02/01-enroll new file mode 100644 index 0000000..e91fa0b --- /dev/null +++ b/utils/open-isns/tests/data/test02/01-enroll @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test02/02-enroll b/utils/open-isns/tests/data/test02/02-enroll new file mode 100644 index 0000000..dbcb735 --- /dev/null +++ b/utils/open-isns/tests/data/test02/02-enroll @@ -0,0 +1,24 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 5 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target diff --git a/utils/open-isns/tests/data/test02/03-registration b/utils/open-isns/tests/data/test02/03-registration new file mode 100644 index 0000000..ec607e6 --- /dev/null +++ b/utils/open-isns/tests/data/test02/03-registration @@ -0,0 +1,72 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 13 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:53 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:53 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 diff --git a/utils/open-isns/tests/data/test02/04-query b/utils/open-isns/tests/data/test02/04-query new file mode 100644 index 0000000..fbdb0c0 --- /dev/null +++ b/utils/open-isns/tests/data/test02/04-query @@ -0,0 +1,20 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 5 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 diff --git a/utils/open-isns/tests/data/test02/05-query b/utils/open-isns/tests/data/test02/05-query new file mode 100644 index 0000000..a35db9e --- /dev/null +++ b/utils/open-isns/tests/data/test02/05-query @@ -0,0 +1,20 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 9 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 diff --git a/utils/open-isns/tests/data/test02/06-dd-registration b/utils/open-isns/tests/data/test02/06-dd-registration new file mode 100644 index 0000000..833f62a --- /dev/null +++ b/utils/open-isns/tests/data/test02/06-dd-registration @@ -0,0 +1,81 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 14 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=13 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 6 + 0814 string : DD member iSCSI name = "isns.client1" + 0813 uint32 : DD member iSCSI index = 10 + 0814 string : DD member iSCSI name = "isns.client2" diff --git a/utils/open-isns/tests/data/test02/07-query b/utils/open-isns/tests/data/test02/07-query new file mode 100644 index 0000000..de13226 --- /dev/null +++ b/utils/open-isns/tests/data/test02/07-query @@ -0,0 +1,40 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 5 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +object[4] = <Network Entity> + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 9 +object[5] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +object[6] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +object[7] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 diff --git a/utils/open-isns/tests/data/test02/08-query b/utils/open-isns/tests/data/test02/08-query new file mode 100644 index 0000000..de13226 --- /dev/null +++ b/utils/open-isns/tests/data/test02/08-query @@ -0,0 +1,40 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 5 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +object[4] = <Network Entity> + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 9 +object[5] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +object[6] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +object[7] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 diff --git a/utils/open-isns/tests/data/test02/09-query b/utils/open-isns/tests/data/test02/09-query new file mode 100644 index 0000000..a35db9e --- /dev/null +++ b/utils/open-isns/tests/data/test02/09-query @@ -0,0 +1,20 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 9 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 diff --git a/utils/open-isns/tests/data/test02/10-dd-registration b/utils/open-isns/tests/data/test02/10-dd-registration new file mode 100644 index 0000000..69bf9f6 --- /dev/null +++ b/utils/open-isns/tests/data/test02/10-dd-registration @@ -0,0 +1,87 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 15 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:18:54 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=13 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 6 + 0814 string : DD member iSCSI name = "isns.client1" + 0813 uint32 : DD member iSCSI index = 10 + 0814 string : DD member iSCSI name = "isns.client2" + 0813 uint32 : DD member iSCSI index = 14 + 0814 string : DD member iSCSI name = "iqn.com.foobar:disk1" +-------------- +Object: index=14 type=<iSCSI Storage Node> state=limbo + 0020 string : iSCSI name = "iqn.com.foobar:disk1" + 0024 uint32 : iSCSI node index = 14 diff --git a/utils/open-isns/tests/data/test02/11-query b/utils/open-isns/tests/data/test02/11-query new file mode 100644 index 0000000..5b4c49d --- /dev/null +++ b/utils/open-isns/tests/data/test02/11-query @@ -0,0 +1,10 @@ +object[0] = <Discovery Domain> + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 6 + 0814 string : DD member iSCSI name = "isns.client1" + 0813 uint32 : DD member iSCSI index = 10 + 0814 string : DD member iSCSI name = "isns.client2" + 0813 uint32 : DD member iSCSI index = 14 + 0814 string : DD member iSCSI name = "iqn.com.foobar:disk1" diff --git a/utils/open-isns/tests/data/test02/12-dd-deregistration b/utils/open-isns/tests/data/test02/12-dd-deregistration new file mode 100644 index 0000000..d330b2a --- /dev/null +++ b/utils/open-isns/tests/data/test02/12-dd-deregistration @@ -0,0 +1,85 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 15 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:20:33 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:20:33 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=13 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 6 + 0814 string : DD member iSCSI name = "isns.client1" + 0813 uint32 : DD member iSCSI index = 14 + 0814 string : DD member iSCSI name = "iqn.com.foobar:disk1" +-------------- +Object: index=14 type=<iSCSI Storage Node> state=limbo + 0020 string : iSCSI name = "iqn.com.foobar:disk1" + 0024 uint32 : iSCSI node index = 14 diff --git a/utils/open-isns/tests/data/test02/13-dd-deregistration b/utils/open-isns/tests/data/test02/13-dd-deregistration new file mode 100644 index 0000000..4175e8e --- /dev/null +++ b/utils/open-isns/tests/data/test02/13-dd-deregistration @@ -0,0 +1,83 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 15 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=13 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 6 + 0814 string : DD member iSCSI name = "isns.client1" +-------------- +Object: index=14 type=<iSCSI Storage Node> state=limbo + 0020 string : iSCSI name = "iqn.com.foobar:disk1" + 0024 uint32 : iSCSI node index = 14 diff --git a/utils/open-isns/tests/data/test02/14-dd-registration b/utils/open-isns/tests/data/test02/14-dd-registration new file mode 100644 index 0000000..56cfac3 --- /dev/null +++ b/utils/open-isns/tests/data/test02/14-dd-registration @@ -0,0 +1,85 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 15 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=13 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 6 + 0814 string : DD member iSCSI name = "isns.client1" + 0813 uint32 : DD member iSCSI index = 10 + 0814 string : DD member iSCSI name = "isns.client2" +-------------- +Object: index=14 type=<iSCSI Storage Node> state=limbo + 0020 string : iSCSI name = "iqn.com.foobar:disk1" + 0024 uint32 : iSCSI node index = 14 diff --git a/utils/open-isns/tests/data/test02/15-dd-deregistration b/utils/open-isns/tests/data/test02/15-dd-deregistration new file mode 100644 index 0000000..d9b420f --- /dev/null +++ b/utils/open-isns/tests/data/test02/15-dd-deregistration @@ -0,0 +1,76 @@ +Dumping database contents +Backend: /tmp/isns-test/test02/server0/database +Last EID: 1 +Last Index: 15 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client2.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client2" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... + 0608v uint32 : Policy allowed node type = Target +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 6 +-------------- +Object: index=7 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 127.1.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 7 +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.1.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 +-------------- +Object: index=9 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client2.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:24:04 2007 + 0007 uint32 : Entity index = 9 +-------------- +Object: index=10 type=<iSCSI Storage Node> state=mature parent=9 + 0020 string : iSCSI name = "isns.client2" + 0021 uint32 : iSCSI node type = Target + 0024 uint32 : iSCSI node index = 10 +-------------- +Object: index=11 type=<Portal> state=mature parent=9 + 0010 ipaddr : Portal IP address = 127.1.0.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=9 + 0030 string : Portal group name = "isns.client2" + 0031 ipaddr : Portal group address = 127.1.0.2 + 0032 uint32 : Portal group port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=14 type=<iSCSI Storage Node> state=limbo + 0020 string : iSCSI name = "iqn.com.foobar:disk1" + 0024 uint32 : iSCSI node index = 14 diff --git a/utils/open-isns/tests/data/test03/01-enroll b/utils/open-isns/tests/data/test03/01-enroll new file mode 100644 index 0000000..0046b41 --- /dev/null +++ b/utils/open-isns/tests/data/test03/01-enroll @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test03/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test03/02-registration b/utils/open-isns/tests/data/test03/02-registration new file mode 100644 index 0000000..11062a6 --- /dev/null +++ b/utils/open-isns/tests/data/test03/02-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test03/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:36:35 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test03/03-unregistration b/utils/open-isns/tests/data/test03/03-unregistration new file mode 100644 index 0000000..874235b --- /dev/null +++ b/utils/open-isns/tests/data/test03/03-unregistration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test03/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:25:47 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=limbo + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test03/04-unregistration b/utils/open-isns/tests/data/test03/04-unregistration new file mode 100644 index 0000000..efe63e4 --- /dev/null +++ b/utils/open-isns/tests/data/test03/04-unregistration @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test03/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test03/99-unregistration b/utils/open-isns/tests/data/test03/99-unregistration new file mode 100644 index 0000000..9ea63b1 --- /dev/null +++ b/utils/open-isns/tests/data/test03/99-unregistration @@ -0,0 +1,13 @@ +Dumping database contents +Backend: /tmp/isns-test/test03/server0/database +Last EID: 1 +Last Index: 7 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... diff --git a/utils/open-isns/tests/data/test04/01-enroll b/utils/open-isns/tests/data/test04/01-enroll new file mode 100644 index 0000000..3caf7a2 --- /dev/null +++ b/utils/open-isns/tests/data/test04/01-enroll @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test04/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test04/02-registration b/utils/open-isns/tests/data/test04/02-registration new file mode 100644 index 0000000..0780d23 --- /dev/null +++ b/utils/open-isns/tests/data/test04/02-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test04/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:38:41 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test04/03-restart b/utils/open-isns/tests/data/test04/03-restart new file mode 100644 index 0000000..0780d23 --- /dev/null +++ b/utils/open-isns/tests/data/test04/03-restart @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test04/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:38:41 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test04/04-query b/utils/open-isns/tests/data/test04/04-query new file mode 100644 index 0000000..3320a8c --- /dev/null +++ b/utils/open-isns/tests/data/test04/04-query @@ -0,0 +1,20 @@ +object[0] = <Network Entity> + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:38:42 2007 + 0007 uint32 : Entity index = 4 +object[1] = <iSCSI Storage Node> + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +object[2] = <Portal> + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +object[3] = <iSCSI Portal Group> + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test05/01-enroll b/utils/open-isns/tests/data/test05/01-enroll new file mode 100644 index 0000000..421d125 --- /dev/null +++ b/utils/open-isns/tests/data/test05/01-enroll @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test05/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test05/02-registration b/utils/open-isns/tests/data/test05/02-registration new file mode 100644 index 0000000..8be9f56 --- /dev/null +++ b/utils/open-isns/tests/data/test05/02-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test05/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 20 + 0004 uint64 : Timestamp = Fri Sep 14 13:40:40 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test05/03-expired b/utils/open-isns/tests/data/test05/03-expired new file mode 100644 index 0000000..1c8b6ea --- /dev/null +++ b/utils/open-isns/tests/data/test05/03-expired @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test05/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test06/01-enroll b/utils/open-isns/tests/data/test06/01-enroll new file mode 100644 index 0000000..5ededae --- /dev/null +++ b/utils/open-isns/tests/data/test06/01-enroll @@ -0,0 +1,18 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test06/02-registration b/utils/open-isns/tests/data/test06/02-registration new file mode 100644 index 0000000..5b6ab13 --- /dev/null +++ b/utils/open-isns/tests/data/test06/02-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:51 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test06/03-registration b/utils/open-isns/tests/data/test06/03-registration new file mode 100644 index 0000000..ad555dc --- /dev/null +++ b/utils/open-isns/tests/data/test06/03-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 12 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=8 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:51 2007 + 0007 uint32 : Entity index = 8 +-------------- +Object: index=9 type=<iSCSI Storage Node> state=mature parent=8 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 9 +-------------- +Object: index=10 type=<Portal> state=mature parent=8 + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 10 +-------------- +Object: index=11 type=<iSCSI Portal Group> state=mature parent=8 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 11 diff --git a/utils/open-isns/tests/data/test06/04-registration b/utils/open-isns/tests/data/test06/04-registration new file mode 100644 index 0000000..477248d --- /dev/null +++ b/utils/open-isns/tests/data/test06/04-registration @@ -0,0 +1,42 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 16 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=12 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:51 2007 + 0007 uint32 : Entity index = 12 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=12 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=14 type=<Portal> state=mature parent=12 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 14 +-------------- +Object: index=15 type=<iSCSI Portal Group> state=mature parent=12 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 15 diff --git a/utils/open-isns/tests/data/test06/05-dd-registration b/utils/open-isns/tests/data/test06/05-dd-registration new file mode 100644 index 0000000..01776db --- /dev/null +++ b/utils/open-isns/tests/data/test06/05-dd-registration @@ -0,0 +1,49 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 17 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=12 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007 + 0007 uint32 : Entity index = 12 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=12 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=14 type=<Portal> state=mature parent=12 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 14 +-------------- +Object: index=15 type=<iSCSI Portal Group> state=mature parent=12 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 15 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" diff --git a/utils/open-isns/tests/data/test06/06-registration b/utils/open-isns/tests/data/test06/06-registration new file mode 100644 index 0000000..6da36e2 --- /dev/null +++ b/utils/open-isns/tests/data/test06/06-registration @@ -0,0 +1,49 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 20 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=17 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007 + 0007 uint32 : Entity index = 17 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=17 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=18 type=<Portal> state=mature parent=17 + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 18 +-------------- +Object: index=19 type=<iSCSI Portal Group> state=mature parent=17 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 19 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" diff --git a/utils/open-isns/tests/data/test06/07-dd-registration b/utils/open-isns/tests/data/test06/07-dd-registration new file mode 100644 index 0000000..b3201d2 --- /dev/null +++ b/utils/open-isns/tests/data/test06/07-dd-registration @@ -0,0 +1,52 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 20 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=17 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007 + 0007 uint32 : Entity index = 17 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=17 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=18 type=<Portal> state=mature parent=17 + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 18 +-------------- +Object: index=19 type=<iSCSI Portal Group> state=mature parent=17 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 19 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" + 0816 uint32 : DD member portal index = 18 + 0817 ipaddr : DD member portal addr = 192.168.1.1 + 0818 uint32 : DD member portal port = 860/tcp diff --git a/utils/open-isns/tests/data/test06/08-registration b/utils/open-isns/tests/data/test06/08-registration new file mode 100644 index 0000000..f965777 --- /dev/null +++ b/utils/open-isns/tests/data/test06/08-registration @@ -0,0 +1,64 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 22 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=17 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007 + 0007 uint32 : Entity index = 17 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=17 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=18 type=<Portal> state=limbo + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 18 +-------------- +Object: index=19 type=<iSCSI Portal Group> state=mature parent=17 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 19 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" + 0816 uint32 : DD member portal index = 18 + 0817 ipaddr : DD member portal addr = 192.168.1.1 + 0818 uint32 : DD member portal port = 860/tcp +-------------- +Object: index=20 type=<Portal> state=mature parent=17 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 20 +-------------- +Object: index=21 type=<iSCSI Portal Group> state=mature parent=17 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 21 diff --git a/utils/open-isns/tests/data/test06/09-registration b/utils/open-isns/tests/data/test06/09-registration new file mode 100644 index 0000000..1308d3c --- /dev/null +++ b/utils/open-isns/tests/data/test06/09-registration @@ -0,0 +1,64 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 22 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=17 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:41:52 2007 + 0007 uint32 : Entity index = 17 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=17 + 0020 string : iSCSI name = "isns.client1" + 0021 uint32 : iSCSI node type = Initiator + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=18 type=<Portal> state=mature parent=17 + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 18 +-------------- +Object: index=19 type=<iSCSI Portal Group> state=mature parent=17 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 19 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" + 0816 uint32 : DD member portal index = 18 + 0817 ipaddr : DD member portal addr = 192.168.1.1 + 0818 uint32 : DD member portal port = 860/tcp +-------------- +Object: index=20 type=<Portal> state=limbo + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 20 +-------------- +Object: index=21 type=<iSCSI Portal Group> state=mature parent=17 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 21 diff --git a/utils/open-isns/tests/data/test06/10-unregistration b/utils/open-isns/tests/data/test06/10-unregistration new file mode 100644 index 0000000..0c42d1c --- /dev/null +++ b/utils/open-isns/tests/data/test06/10-unregistration @@ -0,0 +1,37 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 22 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=limbo + 0020 string : iSCSI name = "isns.client1" + 0024 uint32 : iSCSI node index = 13 +-------------- +Object: index=18 type=<Portal> state=limbo + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 18 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" + 0816 uint32 : DD member portal index = 18 + 0817 ipaddr : DD member portal addr = 192.168.1.1 + 0818 uint32 : DD member portal port = 860/tcp diff --git a/utils/open-isns/tests/data/test06/11-registration b/utils/open-isns/tests/data/test06/11-registration new file mode 100644 index 0000000..d8d03f8 --- /dev/null +++ b/utils/open-isns/tests/data/test06/11-registration @@ -0,0 +1,52 @@ +Dumping database contents +Backend: /tmp/isns-test/test06/server0/database +Last EID: 1 +Last Index: 24 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=22 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 21 11:23:54 2007 + 0007 uint32 : Entity index = 22 +-------------- +Object: index=13 type=<iSCSI Storage Node> state=mature parent=22 + 0020 string : iSCSI name = "isns.client1" + 0024 uint32 : iSCSI node index = 13 + 0021 uint32 : iSCSI node type = Initiator +-------------- +Object: index=18 type=<Portal> state=mature parent=22 + 0010 ipaddr : Portal IP address = 192.168.1.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 18 +-------------- +Object: index=23 type=<iSCSI Portal Group> state=mature parent=22 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 192.168.1.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 23 +-------------- +Object: index=16 type=<Discovery Domain> state=mature + 0811 uint32 : DD ID = 1 + 0812 string : DD name = "isns.dd1" + 081e uint32 : DD features = <NIL> + 0813 uint32 : DD member iSCSI index = 13 + 0814 string : DD member iSCSI name = "isns.client1" + 0816 uint32 : DD member portal index = 18 + 0817 ipaddr : DD member portal addr = 192.168.1.1 + 0818 uint32 : DD member portal port = 860/tcp diff --git a/utils/open-isns/tests/data/test07/01-enroll b/utils/open-isns/tests/data/test07/01-enroll new file mode 100644 index 0000000..ad2eaf4 --- /dev/null +++ b/utils/open-isns/tests/data/test07/01-enroll @@ -0,0 +1,19 @@ +Dumping database contents +Backend: /tmp/isns-test/test07/server0/database +Last EID: 1 +Last Index: 4 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 + 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test07/02-registration b/utils/open-isns/tests/data/test07/02-registration new file mode 100644 index 0000000..da8962a --- /dev/null +++ b/utils/open-isns/tests/data/test07/02-registration @@ -0,0 +1,45 @@ +Dumping database contents +Backend: /tmp/isns-test/test07/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 + 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=4 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007 + 0007 uint32 : Entity index = 4 +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=4 + 0020 string : iSCSI name = "isns.client1" + 0024 uint32 : iSCSI node index = 5 + 0021 uint32 : iSCSI node type = Initiator +-------------- +Object: index=6 type=<Portal> state=mature parent=4 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 6 + 0014 uint32 : ESI port = 65535/tcp + 0013 uint32 : ESI interval = 5 +-------------- +Object: index=7 type=<iSCSI Portal Group> state=mature parent=4 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 7 diff --git a/utils/open-isns/tests/data/test07/03-expired b/utils/open-isns/tests/data/test07/03-expired new file mode 100644 index 0000000..edb9ea4 --- /dev/null +++ b/utils/open-isns/tests/data/test07/03-expired @@ -0,0 +1,19 @@ +Dumping database contents +Backend: /tmp/isns-test/test07/server0/database +Last EID: 1 +Last Index: 8 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 + 0004 uint64 : Timestamp = Fri Sep 14 13:42:57 2007 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test07/04-registration b/utils/open-isns/tests/data/test07/04-registration new file mode 100644 index 0000000..de57cbd --- /dev/null +++ b/utils/open-isns/tests/data/test07/04-registration @@ -0,0 +1,57 @@ +Dumping database contents +Backend: /tmp/isns-test/test07/server0/database +Last EID: 1 +Last Index: 14 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 + 0004 uint64 : Timestamp = Fri Sep 14 13:43:12 2007 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 +-------------- +Object: index=8 type=<Network Entity> state=mature + 0001 string : Entity identifier = "client1.isns-test.eu" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 14 13:43:12 2007 + 0007 uint32 : Entity index = 8 +-------------- +Object: index=9 type=<iSCSI Storage Node> state=mature parent=8 + 0020 string : iSCSI name = "isns.client1" + 0024 uint32 : iSCSI node index = 9 + 0021 uint32 : iSCSI node type = Initiator +-------------- +Object: index=10 type=<Portal> state=mature parent=8 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 860/tcp + 0016 uint32 : Portal index = 10 + 0014 uint32 : ESI port = 65535/tcp + 0013 uint32 : ESI interval = 5 +-------------- +Object: index=11 type=<Portal> state=mature parent=8 + 0010 ipaddr : Portal IP address = 127.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 1/tcp + 0016 uint32 : Portal index = 11 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=8 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 860/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 12 +-------------- +Object: index=13 type=<iSCSI Portal Group> state=mature parent=8 + 0030 string : Portal group name = "isns.client1" + 0031 ipaddr : Portal group address = 127.0.0.1 + 0032 uint32 : Portal group port = 1/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 13 diff --git a/utils/open-isns/tests/data/test07/05-expired b/utils/open-isns/tests/data/test07/05-expired new file mode 100644 index 0000000..fd51f78 --- /dev/null +++ b/utils/open-isns/tests/data/test07/05-expired @@ -0,0 +1,19 @@ +Dumping database contents +Backend: /tmp/isns-test/test07/server0/database +Last EID: 1 +Last Index: 14 +-------------- +Object: index=1 type=<Network Entity> state=mature PRIVATE + 0001 string : Entity identifier = "CONTROL" + 0007 uint32 : Entity index = 1 + 0004 uint64 : Timestamp = Fri Sep 14 13:43:12 2007 +-------------- +Object: index=2 type=<Policy> state=mature parent=1 PRIVATE + 0601v string : Security Policy Index = "client1.isns-test.eu" + 0607v string : Policy allowed node name = "isns.client1" + 0603v opaque : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01... +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE + 0020 string : iSCSI name = "isns.control" + 0021 uint32 : iSCSI node type = <NIL> + 0024 uint32 : iSCSI node index = 3 diff --git a/utils/open-isns/tests/data/test08/01-pauw1 b/utils/open-isns/tests/data/test08/01-pauw1 new file mode 100644 index 0000000..3de54da --- /dev/null +++ b/utils/open-isns/tests/data/test08/01-pauw1 @@ -0,0 +1,100 @@ +Dumping database contents +Backend: /tmp/isns-test/test08/server0/database +Last EID: 1 +Last Index: 15 +-------------- +Object: index=1 type=<Network Entity> state=mature + 0001 string : Entity identifier = "cyan.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Mon Sep 17 15:15:41 2007 + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0" + 0024 uint32 : iSCSI node index = 2 + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "Test (10 GB)" + 002a string : iSCSI auth method = "None" +-------------- +Object: index=3 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1" + 0024 uint32 : iSCSI node index = 3 + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "160 GB disk (ntfs)" + 002a string : iSCSI auth method = "None" +-------------- +Object: index=4 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2" + 0024 uint32 : iSCSI node index = 4 + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "160 GB disk (ext3)" + 002a string : iSCSI auth method = "CHAP" +-------------- +Object: index=5 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3" + 0024 uint32 : iSCSI node index = 5 + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "Test (1 GB)" + 002a string : iSCSI auth method = "None" +-------------- +Object: index=6 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4" + 0024 uint32 : iSCSI node index = 6 + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "Test (40 GB)" + 002a string : iSCSI auth method = "CHAP" +-------------- +Object: index=7 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5" + 0024 uint32 : iSCSI node index = 7 + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "test" + 002a string : iSCSI auth method = "None" +-------------- +Object: index=8 type=<Portal> state=mature parent=1 + 0010 ipaddr : Portal IP address = 10.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0016 uint32 : Portal index = 8 +-------------- +Object: index=9 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0" + 0031 ipaddr : Portal group address = 10.0.0.1 + 0032 uint32 : Portal group port = 3260/tcp + 0034 uint32 : Portal group index = 9 + 0033 uint32 : Portal group tag = 1 +-------------- +Object: index=10 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1" + 0031 ipaddr : Portal group address = 10.0.0.1 + 0032 uint32 : Portal group port = 3260/tcp + 0034 uint32 : Portal group index = 10 + 0033 uint32 : Portal group tag = 1 +-------------- +Object: index=11 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2" + 0031 ipaddr : Portal group address = 10.0.0.1 + 0032 uint32 : Portal group port = 3260/tcp + 0034 uint32 : Portal group index = 11 + 0033 uint32 : Portal group tag = 1 +-------------- +Object: index=12 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3" + 0031 ipaddr : Portal group address = 10.0.0.1 + 0032 uint32 : Portal group port = 3260/tcp + 0034 uint32 : Portal group index = 12 + 0033 uint32 : Portal group tag = 1 +-------------- +Object: index=13 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4" + 0031 ipaddr : Portal group address = 10.0.0.1 + 0032 uint32 : Portal group port = 3260/tcp + 0034 uint32 : Portal group index = 13 + 0033 uint32 : Portal group tag = 1 +-------------- +Object: index=14 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5" + 0031 ipaddr : Portal group address = 10.0.0.1 + 0032 uint32 : Portal group port = 3260/tcp + 0034 uint32 : Portal group index = 14 + 0033 uint32 : Portal group tag = 1 diff --git a/utils/open-isns/tests/data/test09/01-pauw2 b/utils/open-isns/tests/data/test09/01-pauw2 new file mode 100644 index 0000000..9b0a814 --- /dev/null +++ b/utils/open-isns/tests/data/test09/01-pauw2 @@ -0,0 +1,31 @@ +Dumping database contents +Backend: /tmp/isns-test/test09/server0/database +Last EID: 1 +Last Index: 9 +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Mon Sep 17 15:18:04 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0016 uint32 : Portal index = 6 + 0014 uint32 : ESI port = 56288/tcp + 0013 uint32 : ESI interval = 300 +-------------- +Object: index=7 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0024 uint32 : iSCSI node index = 7 + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 33849/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 diff --git a/utils/open-isns/tests/data/test10/01-pauw3 b/utils/open-isns/tests/data/test10/01-pauw3 new file mode 100644 index 0000000..b7f3b10 --- /dev/null +++ b/utils/open-isns/tests/data/test10/01-pauw3 @@ -0,0 +1,31 @@ +Dumping database contents +Backend: /tmp/isns-test/test10/server0/database +Last EID: 1 +Last Index: 9 +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Mon Sep 17 16:34:30 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0016 uint32 : Portal index = 6 + 0014 uint32 : ESI port = 56288/tcp + 0013 uint32 : ESI interval = 10 +-------------- +Object: index=7 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0024 uint32 : iSCSI node index = 7 + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 33849/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 diff --git a/utils/open-isns/tests/data/test10/02-expired b/utils/open-isns/tests/data/test10/02-expired new file mode 100644 index 0000000..b7f3b10 --- /dev/null +++ b/utils/open-isns/tests/data/test10/02-expired @@ -0,0 +1,31 @@ +Dumping database contents +Backend: /tmp/isns-test/test10/server0/database +Last EID: 1 +Last Index: 9 +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Mon Sep 17 16:34:30 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0016 uint32 : Portal index = 6 + 0014 uint32 : ESI port = 56288/tcp + 0013 uint32 : ESI interval = 10 +-------------- +Object: index=7 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0024 uint32 : iSCSI node index = 7 + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 33849/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 diff --git a/utils/open-isns/tests/data/test10/03-pauw3 b/utils/open-isns/tests/data/test10/03-pauw3 new file mode 100644 index 0000000..412c5b5 --- /dev/null +++ b/utils/open-isns/tests/data/test10/03-pauw3 @@ -0,0 +1,31 @@ +Dumping database contents +Backend: /tmp/isns-test/test10/server0/database +Last EID: 1 +Last Index: 9 +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Mon Sep 17 16:34:51 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0016 uint32 : Portal index = 6 + 0014 uint32 : ESI port = 56288/tcp + 0013 uint32 : ESI interval = 10 +-------------- +Object: index=7 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0024 uint32 : iSCSI node index = 7 + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 33849/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 diff --git a/utils/open-isns/tests/data/test10/04-expired b/utils/open-isns/tests/data/test10/04-expired new file mode 100644 index 0000000..412c5b5 --- /dev/null +++ b/utils/open-isns/tests/data/test10/04-expired @@ -0,0 +1,31 @@ +Dumping database contents +Backend: /tmp/isns-test/test10/server0/database +Last EID: 1 +Last Index: 9 +-------------- +Object: index=5 type=<Network Entity> state=mature + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Mon Sep 17 16:34:51 2007 + 0007 uint32 : Entity index = 5 +-------------- +Object: index=6 type=<Portal> state=mature parent=5 + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0016 uint32 : Portal index = 6 + 0014 uint32 : ESI port = 56288/tcp + 0013 uint32 : ESI interval = 10 +-------------- +Object: index=7 type=<iSCSI Storage Node> state=mature parent=5 + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0024 uint32 : iSCSI node index = 7 + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" +-------------- +Object: index=8 type=<iSCSI Portal Group> state=mature parent=5 + 0030 string : Portal group name = "iqn.2005-03.org.open-iscsi:blue" + 0031 ipaddr : Portal group address = 192.168.1.2 + 0032 uint32 : Portal group port = 33849/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 8 diff --git a/utils/open-isns/tests/data/test11/01-pauw4 b/utils/open-isns/tests/data/test11/01-pauw4 new file mode 100644 index 0000000..50c6b92 --- /dev/null +++ b/utils/open-isns/tests/data/test11/01-pauw4 @@ -0,0 +1,32 @@ +Dumping database contents +Backend: /tmp/isns-test/test11/server0/database +Last EID: 1 +Last Index: 5 +-------------- +Object: index=1 type=<Network Entity> state=mature + 0001 string : Entity identifier = "troopa.nki.nl" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 7200 + 0004 uint64 : Timestamp = Fri Sep 21 09:37:10 2007 + 0007 uint32 : Entity index = 1 +-------------- +Object: index=2 type=<Portal> state=mature parent=1 + 0010 ipaddr : Portal IP address = 192.168.1.40 + 0011 uint32 : Portal TCP/UDP port = 3229/tcp + 0017 uint32 : SCN port = 3230/tcp + 0014 uint32 : ESI port = 3230/tcp + 0013 uint32 : ESI interval = 300 + 0016 uint32 : Portal index = 2 +-------------- +Object: index=3 type=<iSCSI Portal Group> state=mature parent=1 + 0030 string : Portal group name = "iqn.1991-05.com.microsoft:orange" + 0031 ipaddr : Portal group address = 192.168.1.40 + 0032 uint32 : Portal group port = 3229/tcp + 0033 uint32 : Portal group tag = 1 + 0034 uint32 : Portal group index = 3 +-------------- +Object: index=4 type=<iSCSI Storage Node> state=mature parent=1 + 0020 string : iSCSI name = "iqn.1991-05.com.microsoft:orange" + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "<MS SW iSCSI Initiator>" + 0024 uint32 : iSCSI node index = 4 diff --git a/utils/open-isns/tests/genkey b/utils/open-isns/tests/genkey new file mode 100644 index 0000000..36c5eee --- /dev/null +++ b/utils/open-isns/tests/genkey @@ -0,0 +1,175 @@ +#!/bin/bash +# +# This is a very simple script to generate a DSA +# key pair for authenticated iSNS. +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This script is supposed to be run on the iSNS server. +# For the first time, run as +# isnsgenkey -s 1024 +# This will generate a DSA params file, and a DSA private +# and public key for the server. +# +# For each client, generate a key using +# isnsgenkey <clientname> +# where <clientname> is the fully qualified domain name. +# This script will convert the FQDN to a valid iSNS +# source name (isns.com.foobar.host) + +myname=`basename $0` +etcdir=/etc/isns +keystore=$etcdir/keystore +dsa_parms=$etcdir/dsa.params +dsa_bits=1024 +opt_force=0 +opt_server=0 + +function usage { + cat <<-EOF >&2 + $* + Usage: + $myname -s [-f] bits + $myname clientname + EOF + exit 1 +} + +function make_isns_name { + OFS="$IFS" + IFS=. + set -- $* + + __result=$1; shift + for part; do + __result=$part.$__result + done + echo "isns.$__result" + IFS="$OFS" +} + +set -- `getopt b:fk:s $*` +while [ $# -gt 0 ]; do + opt=$1; shift + case $opt in + --) break;; + -b) dsa_bits=$1; shift;; + -f) opt_force=1;; + -k) dsa_priv=$1; shift;; + -s) opt_server=1;; + *) usage "Unknown option $opt";; + esac +done + +if [ `id -un` != "root" -a $opt_force -eq 0 ]; then + echo "$myname: should be run by super user only" >&2 + exit 1 +fi + +# All newly generated files should have restricted +# access by default. +umask 077 + +tmpdir=`mktemp -d /tmp/isnsgenkey.XXXXXX` +trap "rm -rf $tmpdir" 0 1 2 15 + +if [ $opt_server -ne 0 ]; then + [ $# -eq 1 ] || usage "Expected DSA key length" + dsa_bits=$1 + + install -m 755 -d $etcdir + if [ -z $dsa_priv ]; then + dsa_priv=$etcdir/auth_key + fi + dsa_pub=$dsa_priv.pub + dsa_copy= +else + [ $# -eq 1 ] || usage "Expected client name" + client=`make_isns_name $1` + + mkdir -p $tmpdir$etcdir + # build_client_conf $client > $tmpdir$etcdir/client.conf + + if [ -z $dsa_priv ]; then + dsa_priv=$tmpdir$etcdir/auth_key + fi + dsa_pub=$dsa_priv.pub + dsa_copy=$keystore/$client +fi + +if [ -f $dsa_priv -a $opt_force -eq 0 ]; then + cat <<-EOF + + ------------------------------------------------------------------ + | There is already a DSA key installed in $dsa_priv. In order to + | generate a new key, please specify the -f [force] option. + ------------------------------------------------------------------ + EOF + exit 1 +fi + +if [ ! -r $dsa_parms ]; then + if [ $opt_server -eq 0 ]; then + echo "Please run $myname in server-initialization mode first" >&2 + exit 1 + fi + + cat <<-EOF + + ------------------------------------------------------------------ + | I will now try to generate a set of DSA parameters. This can be + | a slow process, so please be patient. + ------------------------------------------------------------------ + EOF + + mkdir -p `dirname $dsa_parms` + openssl dsaparam $dsa_bits -out $dsa_parms || + exit 1 + + # DSA parameters are public + chmod 644 $dsa_parms +fi + +cat <<EOF +------------------------------------------------------------------ +| I will now try to generate a DSA private key and store it in +| $dsa_priv. +| +| The key will not be protected by a passphrase. +------------------------------------------------------------------ +EOF +openssl gendsa -out $dsa_priv $dsa_parms +openssl dsa -pubout -in $dsa_priv -out $dsa_pub +chmod 644 $dsa_pub + +cat <<EOF +------------------------------------------------------------------ +| Testing new DSA key +------------------------------------------------------------------ +EOF +if ! openssl dgst -dss1 -sign $dsa_priv -out $tmpdir/test-sig /etc/hosts; then + echo "DSA signature failed - aborting!" >&2 + exit 1 +fi +if ! openssl dgst -dss1 -verify $dsa_pub -signature $tmpdir/test-sig /etc/hosts; then + echo "DSA verification failed - aborting!" >&2 + exit 1 +fi +od -tx1 $tmpdir/test-sig + +if [ $opt_server -eq 0 ]; then + echo "Installing DSA public key as $dsa_copy" + install -d -m 755 $keystore + install -m 644 $dsa_pub $dsa_copy + install -m 644 $etcdir/auth_key.pub $tmpdir$etcdir/server.pub + + tarball=auth-$client.tar.gz + tar -C $tmpdir -czf $tarball .$etcdir + + cat <<-EOF + ------------------------------------------------------------------ + | Successfully packaged $tarball + | Please copy this file to client $client and install + ------------------------------------------------------------------ + EOF +fi diff --git a/utils/open-isns/tests/harness.pl b/utils/open-isns/tests/harness.pl new file mode 100644 index 0000000..d7ce025 --- /dev/null +++ b/utils/open-isns/tests/harness.pl @@ -0,0 +1,929 @@ +#!/usr/bin/perl + +use Getopt::Long; + +$__isns_verbose = 1; +$__isns_security = 1; + +$__isns_bin = "../"; +$__isns_seq = 0; +$__isns_test_base = '/tmp/isns-test'; +$__isns_test_dir = '/tmp/isns-test/test'; +$__isns_stage = 1; +$__isns_test_data = ''; +$__isns_test_dump = ''; +$__isns_passed = 0; +$__isns_failed = 0; +$__isns_warned = 0; +@__isns_servers = (); + +%__isns_ignore_tag = ( + "0004" => 1, # Timestamp + "0603v" => 1, # DSA public key +); + +sub isns_fail { + + print "*** FAILURE ***\n"; + $__isns_failed++; + + my $line; + foreach $line (@_) { + print "*** $line ***\n"; + } +} + +sub isns_pass { + + print "*** SUCCESS ***\n" if ($__isns_verbose > 1); + $__isns_passed++; +} + +sub isns_warn { + + printf "*** WARNING: %s ***\n", join(' ', @_); + $__isns_warned++; +} + +sub isns_die { + + printf "*** TERMINAL FAILURE: %s ***\n", join(' ', @_); + $__isns_failed++; + + &isns_finish; + die "Test aborted\n"; +} + +sub isns_finish { + + my $pid; + foreach $pid (@__isns_servers) { + kill 15, $pid or &isns_warn("Cannot kill server process (pid=$pid): $!\n"); + } + + &isns_report; +} + +sub isns_report { + + print "*** Test $__isns_test_name complete."; + print " PASSED: $__isns_passed" if ($__isns_passed); + print " FAILED: $__isns_failed" if ($__isns_failed); + print " WARNINGS: $__isns_warned" if ($__isns_warned); + print " ***\n"; +} + +sub isns_info { + + print @_ if ($__isns_verbose > 1); +} + +sub isns_notice { + + print @_ if ($__isns_verbose > 0); +} + +sub isns_stage { + + local($name, @msg) = @_; + + if ($name =~ m/^[0-9]/o) { + $__isns_stage_name = $name; + } else { + $__isns_stage_name = sprintf "%02d-%s", + $__isns_stage++, $name; + } + &isns_notice("*** $__isns_stage_name: ", @msg, " ***\n"); +} + +sub build_config { + + local($src_file, $dst_file, *__subst) = @_; + my $key; + my $okey; + my $value; + my $sepa; + my %subst; + + &isns_info("*** Building $src_file -> $dst_file\n"); + + # Translate all keys to lower case. + foreach $key (keys(%__subst)) { + $value = $__subst{$key}; + $key =~ tr/A-Z/a-z/; + $subst{$key} = $value; + } +# foreach $key (keys(%subst)) { +# printf " %s -> %s\n", $key, $subst{$key}; +# } + + open IN, "<$src_file" or die "$src_file: $!\n"; + open OUT, ">$dst_file" or die "$dst_file: $!\n"; + + while (<IN>) { + $line = $_; + if (m:(\S+)(\s*=\s*)(.*):o) { + ($okey, $sepa, $value) = ($1, $2, $3); + + $key = $okey; + $key =~ tr/A-Z/a-z/; + + if ($subst{$key}) { + $line = "$okey$sepa$subst{$key}\n"; + } + } + + # Ignore unconfigured lines. + next if ($line =~ m/\@[A-Z_]*\@/o); + print OUT $line; + } + close OUT; + close IN; +} + +sub get_config_value { + local($cfg_file, $item_name) = @_; + my $result; + my $name; + my $value; + + $item_name =~ tr/A-Z/a-z/; + + open IN, "<$cfg_file" or die "$cfg_file: $!\n"; + while (<IN>) { + chop; + ($name, $value) = split(/\s+=\s+/, $_); + + $name =~ tr/A-Z/a-z/; + if ($name eq $item_name) { + $result = $value; + last; + } + } + close IN; + + return $result; +} + +sub create_key { + + local($keyfile) = @_; + + if ($__isns_security) { + &isns_info("*** Creating key at $keyfile\n"); + system "./genkey -fsk $keyfile 2048 >${keyfile}.log 2>&1"; + } + return $keyfile; +} + +sub create_server { + + local(*override) = @_; + my %local_config; + my $my_dir; + my $handle; + my $config; + + $handle = sprintf "server%d", $__isns_seq++; + $my_dir = "$__isns_test_dir/${handle}"; + + mkdir $my_dir, 0700 or die "Cannot create $my_dir: $!\n"; + + $server_addr = "127.0.0.1:7770" unless ($server_addr); + + $config = "$my_dir/config"; + + $local_config{"SourceName"} = "isns.$handle"; + $local_config{"Database"} = "$my_dir/database"; + $local_config{"BindAddress"} = "$server_addr"; + $local_config{"PIDFile"} = "$my_dir/pid"; + $local_config{"ControlSocket"} = "$my_dir/control"; + $local_config{"Security"} = $__isns_security; + $local_config{"AuthKeyFile"} = &create_key("$my_dir/auth_key"); + + foreach $key (keys(%override)) { + $local_config{$key} = $override{$key}; + } + + &build_config('server.conf', $config, \%local_config); + return $config; +} + +sub create_client { + + local($server_config, $client_address) = @_; + my %local_config; + my $server_key; + my $control_socket; + my $server_addr; + my $my_dir; + my $handle; + my $config; + + $handle = sprintf "client%d", $__isns_seq++; + $my_dir = "$__isns_test_dir/${handle}"; + + mkdir $my_dir, 0700 or die "Cannot create $my_dir: $!\n"; + + $control_socket = &get_config_value($server_config, "ControlSocket"); + $server_addr = &get_config_value($server_config, "BindAddress"); + $server_addr = "127.0.0.1" unless ($server_addr); + + $config = "$my_dir/config"; + + $local_config{"SourceName"} = "isns.$handle"; + $local_config{"AuthName"} = "$handle.isns-test.eu"; + $local_config{"ServerAddress"} = $server_addr; + $local_config{"ControlSocket"} = $control_socket; + $local_config{"BindAddress"} = $client_address if ($client_address); + $local_config{"server_config"} = $server_config; + $local_config{"Security"} = $__isns_security; + $local_config{"AuthKeyFile"} = &create_key("$my_dir/auth_key"); + $local_config{"ServerKeyFile"} = + &get_config_value($server_config, "AuthKeyFile") . ".pub"; + + &build_config('client.conf', $config, \%local_config); + + $__isns_data{$config,"server_config"} = $server_config; + $__isns_data{$config} = %local_config; + return $config; +} + +sub get_logfile { + + local($config) = @_; + my $dir; + + $dir = $config; + $dir =~ s|/+[^/]+$||o; + + return "$dir/logfile"; +} + +sub run_command { + + local(@cmd) = @_; + my $status; + my $cmd; + + $cmd = join(' ', @cmd); + &isns_info("$cmd\n"); + + system "$cmd"; + + $status = $?; + if ($status) { + &isns_warn("Command failed, exit status $status"); + print "*** Command was: $cmd ***\n"; + return undef; + } + + return 1; +} + +sub isns_start_server { + + local($server_config) = @_; + my $logfile; + my $pidfile; + my $pid; + + die "restart_server: missing server config argument!\n" + unless(-f $server_config); + $logfile = &get_logfile($server_config); + $pidfile = &get_config_value($server_config, "PIDFile"); + + &isns_info("*** Starting server (logging to $logfile)\n"); + + $pid = fork(); + if ($pid) { + my $retry; + + if ($pidfile) { + for ($retry = 0; $retry < 5; $retry++) { + last if (-f $pidfile); + sleep 1; + } + $pid = `cat $pidfile` if ($pidfile); + chop($pid); + } + &isns_info("*** Started server (pid=$pid) ***\n"); + push(@__isns_servers, $pid); + return $pid; + } + + &isns_info("${__isns_bin}isnsd -c $server_config -f -d all\n"); + exec "${__isns_bin}isnsd -c $server_config -f -d all >$logfile 2>&1 &" + or die "Unable to run isnsd: $!\n"; +} + +sub isns_stop_server { + + local($pid) = @_; + my @list; + my $p; + + kill 15, $pid or &isns_warn("Cannot kill server process (pid=$pid): $!\n"); + foreach $p (@__isns_servers) { + append(@list, $p) unless ($p == $pid); + } + @__isns_servers = @list; +} + +sub isns_restart_server { + + local($pid, $server_config); + + if ($_[0] =~ m:^\d+$:o) { + $pid = shift(@_); + } else { + if ($#__isns_servers < 0) { + &isns_warn("isns_restart_server: no server running\n"); + return 0; + } + $pid = $__isns_servers[0]; + } + $server_config = shift(@_); + + &isns_stop_server($pid); + return &isns_start_server($server_config); +} + +sub isns_verify_db { + + local($stage, $server_config); + my $dump_file; + my $data_file; + + if ($_[0] =~ m/^\d/o) { + $stage = shift(@_); + } else { + $stage = $__isns_stage_name; + } + $server_config = shift(@_); + + die "Test case forgot to call test_prep" unless($__isns_test_data); + + $dump_file = "$__isns_test_dump/$stage"; + unless (&run_command("${__isns_bin}/isnsd -c $server_config --dump-db > $dump_file")) { + &isns_fail; + return 0; + } + + # See if the reference data file exists. If it + # doesn't, this means we're priming the test case. + # Just copy the dump file. + $data_file = "$__isns_test_data/$stage"; + unless (-f $data_file) { + print "*** Saving database dump for stage $stage ***\n"; + mkdir $__isns_test_data, 0755; + system "cp $dump_file $data_file"; + return 1; + } + + &isns_info("*** Verifying database dump for stage $stage ***\n"); + if (&verify_dump($stage, $data_file, $dump_file)) { + &isns_pass; + } else { + if ($__isns_verbose > 1) { + system("diff -u -ITimestamp -I'DSA security key' $data_file $dump_file"); + } + &isns_fail; + } + + return 1; +} + +sub verify_db { + + &isns_verify_db(@_); +} + +sub verify_response { + + local($stage, $client_config) = @_; + my $dump_file; + my $data_file; + + die "Test case forgot to call test_prep" unless($__isns_test_data); + + $dump_file = &get_logfile($client_config); + + # See if the reference data file exists. If it + # doesn't, this means we're priming the test case. + # Just copy the dump file. + $data_file = "$__isns_test_data/$stage"; + unless (-f $data_file) { + print "*** Saving data for stage $stage ***\n"; + mkdir $__isns_test_data, 0755; + system "cp $dump_file $data_file"; + return 1; + } + + &isns_info("*** Verifying data for stage $stage ***\n"); + if (&verify_query($stage, $data_file, $dump_file)) { + &isns_pass; + } else { + &isns_fail("Query response returns unexpected data"); + system "cp $dump_file $__isns_test_dump/$stage"; + print "*** Saved dump as $__isns_test_dump/$stage\n"; + print "*** Reference data in $data_file\n"; + if ($__isns_verbose > 1) { + system("diff -u -ITimestamp -I'DSA security key' $data_file $dump_file"); + } + } + + return 1; +} + +sub verify_dump { + + local($stage, $data_file, $dump_file) = @_; + my $line; + my @dump; + my @data; + my @obj1; + my @obj2; + + @dump = &load_dump($dump_file); + @data = &load_dump($data_file); + + &skip_header(\@dump); + &skip_header(\@data); + + while (1) { + $line++; + + @obj1 = &get_next_object(\@dump); + @obj2 = &get_next_object(\@data); + + last unless(@obj1 || @obj2); + + unless (@obj1 && @obj2) { + print STDERR "*** $stage: Excess data at end of dump\n"; + return 0; + } + + unless (&compare_objects(\@obj1, \@obj2)) { + print STDERR "*** Object mismatch (object $line):\n"; + print STDERR "Expected:\n "; + print STDERR join("\n ", @obj2), "\n"; + print STDERR "Got:\n "; + print STDERR join("\n ", @obj1), "\n"; + return 0; + } + } + + if (@data) { + print STDERR "*** $stage: Unexpected end of dump at line $line\n"; + return 0; + } + + return 1; +} + +sub skip_header { + + local(*list) = @_; + local($_); + + while ($_ = shift(@list)) { + last if (/^-/o); + } +} + +sub get_next_object { + + local(*list) = @_; + local($_, $header, @result); + my @tags; + + while ($_ = shift(@list)) { + next if (/^-/o); + if (/^\s+([0-9a-fv]+)\s+/o) { + next if ($__isns_ignore_tag{$1}); + push(@tags, $_); + } else { + if (@result) { + unshift(@list, $_); + last; + } + push(@result, $_); + } + #print "### $_\n"; + } + + if (@tags) { + push(@result, sort(@tags)); + } + return @result; +} + +sub compare_objects { + + local(*a, *b) = @_; + local($i); + + return 0 unless ($#a == $#b); + for ($i = 0; $i <= $#a; $i++) { + return 0 unless ($a[$i] eq $b[$i]); + } + + return 1; +} + + +sub verify_query { + + local($stage, $data_file, $dump_file) = @_; + my $line; + my @dump; + my @data; + + @dump = &load_dump($dump_file); + @data = &load_dump($data_file); + + while (@dump) { + $line++; + unless (@data) { + print STDERR "*** $stage: Excess data in dump at line $line\n"; + return 0; + } + + $a = shift(@dump); + $b = shift(@data); + if ($a =~ /^\S/o) { + next if ($a eq $b); + print STDERR "*** $stage: Mismatch at line $line ***\n"; + print STDERR "*** Found: $a\n"; + print STDERR "*** Expected: $b\n"; + return 0; + } + + ($nix, $a_tag, $a_value) = split(/\s+/, $a, 3); + ($nix, $b_tag, $b_value) = split(/\s+/, $b, 3); + if ($a_tag ne $b_tag) { + print STDERR "*** $stage: Tag mismatch at line $line\n"; + print STDERR "*** Found: $a\n"; + print STDERR "*** Expected: $b\n"; + return 0; + } + + next if ($__isns_ignore_tag{$a_tag}); + if ($a_value ne $b_value) { + print STDERR "*** $stage: Value mismatch at line $line (tag $a_tag)\n"; + print STDERR "*** Found: $a\n"; + print STDERR "*** Expected: $b\n"; + return 0; + } + } + + if (@data) { + print STDERR "*** $stage: Unexpected end of dump at line $line\n"; + return 0; + } + + return 1; +} + +sub load_dump { + + local($filename) = @_; + my @result; + + open IN, $filename or die "Unable to open $filename: $!\n"; + while (<IN>) { + chop; + push(@result, $_); + } + close IN; + return @result; +} + + +sub run_client { + + local($config, @args) = @_; + my $logfile; + my $cmd; + + $logfile = &get_logfile($config); + + $cmd = "${__isns_bin}/isnsadm -c $client_config " . join(' ', @args); + if (&run_command("$cmd >$logfile")) { + return $logfile; + } + return undef; +} + +sub __isns_enroll_client { + + local($client_config, @extra_args) = @_; + my $source_name; + my $auth_name; + my $auth_key; + my @args; + + $source_name = &get_config_value($client_config, "SourceName"); + $auth_name = &get_config_value($client_config, "AuthName"); + $auth_key = &get_config_value($client_config, "AuthKeyFile"); + + push(@args, "--local --enroll $auth_name node-name=$source_name"); + push(@args, " key=${auth_key}.pub") if ($auth_key); + push(@args, @extra_args) if (@extra_args); + + &run_client($client_config, @args); +} + +sub isns_enroll_client { + + local($client, @args) = @_; + my $server; + + $server = $__isns_data{$client,"server_config"}; + &isns_stage("enroll", "Enrolling client"); + &__isns_enroll_client($client, @args); + &verify_db($__isns_stage_name, $server); +} + +sub enroll_client { + + print "*** Enrolling client ***\n"; + &__isns_enroll_client(@_); +} + +sub __isns_register_client { + + local($client_config, @extra_args) = @_; + my @args; + + push(@args, "--register"); + push(@args, @extra_args) if (@extra_args); + + &run_client($client_config, @args); +} + +sub isns_register_client { + + local($client, @args) = @_; + my $server; + + $server = $__isns_data{$client,"server_config"}; + &isns_stage("registration", "Registering client " . join(' ', @args)); + &__isns_register_client($client, @args); + &verify_db($__isns_stage_name, $server); +} + +sub register_client { + + print "*** Registering client ***\n"; + &__isns_register_client(@_); +} + +sub __isns_query_objects { + + local($client_config, @extra_args) = @_; + my @args; + + push(@args, "--query"); + push(@args, @extra_args) if (@extra_args); + + return &run_client($client_config, @args); +} + +sub isns_query_objects { + + local($client, @args) = @_; + + &isns_stage("query", "Querying " . join(' ', @args)); + &__isns_query_objects($client, @args); + &verify_response($__isns_stage_name, $client); +} + +sub query_objects { + + print "*** Querying objects ***\n"; + __isns_query_objects(@_); +} + +sub isns_query_eid { + + local($client_config, @extra_args) = @_; + my $logfile; + my @args; + local($eid); + + push(@args, "--query-eid"); + push(@args, @extra_args) if (@extra_args); + + &isns_info("*** Querying for EID ***\n"); + $logfile = &run_client($client_config, @args); + + if ($logfile) { + $eid = `cat $logfile`; + unless ($eid) { + &isns_fail("Server reports empty EID"); + } + chop($eid); + } + + return $eid; +} + +sub __isns_unregister_client { + + local($client_config, @extra_args) = @_; + my @args; + + push(@args, "--deregister"); + push(@args, @extra_args) if (@extra_args); + + &run_client($client_config, @args); +} + +sub isns_unregister_client { + + my $stage = 0; + my $client; + my $server; + my $eid; + + if ($_[0] =~ m/^\d/o) { + &isns_stage(shift(@_), "Unregister client"); + } else { + &isns_stage("unregistration", "Unregister client"); + } + + $client = shift(@_); + + unless (@_) { + $eid = &isns_query_eid($client); + push(@_, "eid=$eid"); + } + + &__isns_unregister_client($client, @_); + + $server = $__isns_data{$client,"server_config"}; + &verify_db($__isns_stage_name, $server); +} + +sub unregister_client { + + &isns_info("*** Unregistering client ***\n"); + &__isns_unregister_client(@_); +} + +sub __isns_register_domain { + + local($client_config, @extra_args) = @_; + my @args; + + push(@args, "--local --dd-register"); + push(@args, @extra_args) if (@extra_args); + + &run_client($client_config, @args); +} + +sub isns_register_domain { + + local($client, @args) = @_; + my $server; + + &isns_stage("dd-registration", "Registering DD " . join(' ', @args)); + &__isns_register_domain($client, @args); + + $server = $__isns_data{$client,"server_config"}; + &isns_verify_db($server); +} + +sub register_domain { + + &isns_info("*** Registering DD ***\n"); + &__isns_register_domain(@_); +} + +sub __isns_deregister_domain { + + local($client_config, @extra_args) = @_; + my @args; + + push(@args, "--local --dd-deregister"); + push(@args, @extra_args) if (@extra_args); + + &run_client($client_config, @args); +} + +sub isns_deregister_domain { + + local($client, @args) = @_; + my $server; + + &isns_stage("dd-deregistration", "Deregistering DD (members)" . join(' ', @args)); + &__isns_deregister_domain($client, @args); + + $server = $__isns_data{$client,"server_config"}; + &isns_verify_db($server); +} + +sub isns_external_test { + + local($client, @args) = @_; + my $logfile; + my $stage; + my $cmd; + + $logfile = &get_logfile($client); + + $cmd = shift(@args); + $stage = $cmd; + $stage =~ s:.*/::o; + + $cmd = "${__isns_bin}/$cmd -c $client " . join(' ', @args); + + &isns_stage($stage, "Running external $cmd " . join(' ', @args)); + unless (&run_command("$cmd >$logfile")) { + return undef; + } + + $server = $__isns_data{$client,"server_config"}; + &isns_verify_db($server); +} + +sub __isns_prep_test { + + local($name, $duration, @ARGV) = @_; + + GetOptions('verbose+' => \$__isns_verbose, + "quiet" => \$__isns_quiet, + "fast" => \$__isns_quick, + "insecure" => \$__isns_insecure); + $__isns_verbose = 0 if ($__isns_quiet); + $__isns_security = 0 if ($__isns_insecure); + + if ($__isns_quick && $duration > 15) { + print "*** Skipping $name (duration ~ $duration seconds) ***\n"; + exit(0); + } + + print "*** Starting $name ***\n"; + printf "*** This test case will take about %u sec ***\n", $duration + if ($duration); + $__isns_test_name = $name; + $__isns_test_dir = "$__isns_test_base/$name"; + $__isns_test_dump = "$__isns_test_dir/dump"; + $__isns_test_data = "data/$name"; + + # Be careful when removing test dir + system "rm -rf $__isns_test_dir" if ($__isns_test_dir =~ m:/tmp/:o); + + mkdir $__isns_test_base, 0700; + mkdir $__isns_test_dir, 0700; + mkdir $__isns_test_dump, 0700; +} + +sub test_prep { + + local($name, @args) = @_; + + __isns_prep_test($name, 0, @args); +} + +sub isns_prep_slow_test { + + __isns_prep_test(@_); +} + +# Sleep for a few seconds, giving the user some dots to keep +# him occupied. +sub isns_idle { + + local($time) = @_; + + if ($__isns_verbose == 0) { + sleep $time; + return; + } + + $| = 1; + print "Snooze"; + while ($time--) { + print "."; + sleep 1; + } + print "\n"; + $| = 0; +} + +sub main { + + my $server_config; + my $client_config; + + &test_prep; + + $server_config = &create_server; + $client_config = &create_client($server_config); +} + +#&main; +1; diff --git a/utils/open-isns/tests/pauw1.c b/utils/open-isns/tests/pauw1.c new file mode 100644 index 0000000..c3e66f7 --- /dev/null +++ b/utils/open-isns/tests/pauw1.c @@ -0,0 +1,179 @@ +/* + * Test case, captured from a Wasabi Storage Builder + * registering itself. + */ +#include <getopt.h> +#include <isns.h> +#include <paths.h> +#include <util.h> +#include <message.h> + +int +main(int argc, char **argv) +{ + const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG; + isns_client_t *clnt; + isns_attr_list_t *attrs; + isns_simple_t *reg; + isns_portal_info_t portal_info; + uint32_t status; + int c; + + while ((c = getopt(argc, argv, "c:d:")) != -1) { + switch (c) { + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + default: + isns_fatal("Unknown option\n"); + } + } + + isns_read_config(opt_configfile); + isns_assign_string(&isns_config.ic_source_name, + "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"); + + clnt = isns_create_default_client(NULL); + + reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, + clnt->ic_source, NULL); + + attrs = ®->is_operating_attrs; + +#define ADD(type, tag, value) \ + isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value) +#define STR(tag, value) ADD(string, tag, value) +#define U32(tag, value) ADD(uint32, tag, value) +#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag) +#define TARGET(name, alias, auth) \ + STR(ISCSI_NAME, name); \ + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \ + STR(ISCSI_ALIAS, alias); \ + STR(ISCSI_AUTHMETHOD, auth) + + STR(ENTITY_IDENTIFIER, "cyan.pauw.homeunix.net"); + U32(ENTITY_PROTOCOL, 2); + U32(REGISTRATION_PERIOD, 31536000); + + TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0", + "Test (10 GB)", + "None"); + TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1", + "160 GB disk (ntfs)", + "None"); + TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2", + "160 GB disk (ext3)", + "CHAP"); + TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3", + "Test (1 GB)", + "None"); + TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4", + "Test (40 GB)", + "CHAP"); + TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5", + "test", + "None"); + + isns_portal_parse(&portal_info, "10.0.0.1:3260/tcp", NULL); + isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + attrs); + + /* Mumbo jumbo encoding of portal groups */ + U32(PG_TAG, 1); + STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"); + STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"); + STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"); + STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"); + STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"); + STR(PG_ISCSI_NAME, "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"); + + /* Strictly speaking, a PGT not followed by any data is invalid. + * + * 5.6.5.1. + * When a Portal is registered, the Portal attributes MAY + * immediately be followed by a PGT attribute. The PGT attribute + * SHALL be followed by the set of PG iSCSI Names representing + * nodes that will be associated to the Portal using the indicated + * PGT value. + */ + NIL(PG_TAG); + + isns_simple_print(reg, isns_print_stdout); + + status = isns_client_call(clnt, ®); + + if (status != ISNS_SUCCESS) + isns_fatal("Unable to register object: %s\n", + isns_strerror(status)); + + printf("Successfully registered object(s)\n"); + isns_simple_print(reg, isns_print_stdout); + + return 0; +} + +/* + Creating file DB backend (/var/lib/isns) + DB: loading all objects from /var/lib/isns + Next ESI message in 3600 seconds + Incoming PDU xid=0001 seq=0 len=1208 func=DevAttrReg client first last + Next message xid=0001 + Received message + ---DevAttrReg--- + Source: + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0" + Message attributes: <empty list> + Operating attributes: + 0001 string : Entity identifier = "cyan.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0006 uint32 : Registration Period = 31536000 + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0" + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "Test (10 GB)" + 002a string : iSCSI auth method = "None" + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1" + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "160 GB disk (ntfs)" + 002a string : iSCSI auth method = "None" + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2" + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "160 GB disk (ext3)" + 002a string : iSCSI auth method = "CHAP" + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3" + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "Test (1 GB)" + 002a string : iSCSI auth method = "None" + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4" + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "Test (40 GB)" + 002a string : iSCSI auth method = "CHAP" + 0020 string : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5" + 0021 uint32 : iSCSI node type = Target + 0022 string : iSCSI alias = "test" + 002a string : iSCSI auth method = "None" + 0010 ipaddr : Portal IP address = 10.0.0.1 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0033 uint32 : Portal group tag = 1 + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0" + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1" + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2" + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3" + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4" + 0030 string : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5" + 0033 nil : Portal group tag = <empty> + :: policy insecure function DevAttrReg (0001) permitted + :: policy insecure source +iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0 permitted + :: policy insecure operation DevAttrReg on Network Entity object +permitted + DB: Storing object 00000001 -> /var/lib/isns/00000001 + DB: added object 1 (Network Entity) state 1 +Segmentation fault + */ diff --git a/utils/open-isns/tests/pauw2.c b/utils/open-isns/tests/pauw2.c new file mode 100644 index 0000000..29084b3 --- /dev/null +++ b/utils/open-isns/tests/pauw2.c @@ -0,0 +1,212 @@ +/* + * Test case, captured from iscsi-target + * registering itself. + */ +#include <getopt.h> +#include <isns.h> +#include <paths.h> +#include <util.h> +#include <message.h> + +#define ADD(type, tag, value) \ + isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value) +#define STR(tag, value) ADD(string, tag, value) +#define U32(tag, value) ADD(uint32, tag, value) +#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag) +#define TARGET(name, alias, auth) \ + STR(ISCSI_NAME, name); \ + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \ + STR(ISCSI_ALIAS, alias); \ + STR(ISCSI_AUTHMETHOD, auth) + +int +main(int argc, char **argv) +{ + const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG; + isns_client_t *clnt; + isns_attr_list_t *attrs; + isns_simple_t *reg; + isns_portal_info_t portal_info; + uint32_t status; + int c; + + while ((c = getopt(argc, argv, "c:d:")) != -1) { + switch (c) { + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + default: + isns_fatal("Unknown option\n"); + } + } + + isns_read_config(opt_configfile); + + /* + ---DevAttrReg[REPLACE]--- + Source: + 0020 string : iSCSI name = "iqn.2007-03.com.example:stgt.disk" + Message attributes: + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + Operating attributes: + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 3260/tcp + 0017 uint32 : SCN port = 42138/tcp + 0020 string : iSCSI name = "iqn.2007-03.com.example:stgt.disk" + 0021 uint32 : iSCSI node type = Target + */ + isns_assign_string(&isns_config.ic_source_name, + "iqn.2007-03.com.example:stgt.disk"); + + clnt = isns_create_default_client(NULL); + reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, + clnt->ic_source, NULL); + reg->is_replace = 1; + + /* Message attributes */ + attrs = ®->is_message_attrs; + STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); + + /* Operating attributes */ + attrs = ®->is_operating_attrs; + + STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); + U32(ENTITY_PROTOCOL, 2); + + isns_portal_parse(&portal_info, "192.168.1.2:3260/tcp", NULL); + isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + attrs); + + U32(SCN_PORT, 42138); + STR(ISCSI_NAME, "iqn.2007-03.com.example:stgt.disk"); + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); + isns_simple_print(reg, isns_print_stdout); + + status = isns_client_call(clnt, ®); + + if (status != ISNS_SUCCESS) + isns_fatal("Unable to register object: %s\n", + isns_strerror(status)); + + printf("Successfully registered object #1\n"); + // isns_simple_print(reg, isns_print_stdout); + isns_simple_free(reg); + isns_client_destroy(clnt); + + /* + ---DevAttrReg[REPLACE]--- + Source: + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + Message attributes: + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + Operating attributes: + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0014 uint32 : ESI port = 56288/tcp + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" + + [...] + response status 0x0003 (Invalid registration) + + This would fail because we got confused about EID in + the replace case. + */ + isns_assign_string(&isns_config.ic_source_name, + "iqn.2005-03.org.open-iscsi:blue"); + + clnt = isns_create_default_client(NULL); + reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, + clnt->ic_source, NULL); + reg->is_replace = 1; + + /* Message attributes */ + attrs = ®->is_message_attrs; + STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); + + /* Operating attributes */ + attrs = ®->is_operating_attrs; + + STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); + U32(ENTITY_PROTOCOL, 2); + + isns_portal_parse(&portal_info, "192.168.1.2:33849/tcp", NULL); + isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + attrs); + + U32(ESI_PORT, 56288); + STR(ISCSI_NAME, "iqn.2005-03.org.open-iscsi:blue"); + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_INITIATOR_MASK); + STR(ISCSI_ALIAS, "blue.pauw.homeunix.net"); + isns_simple_print(reg, isns_print_stdout); + + status = isns_client_call(clnt, ®); + + if (status != ISNS_SUCCESS) + isns_fatal("Unable to register object: %s\n", + isns_strerror(status)); + + printf("Successfully registered object #2\n"); + // isns_simple_print(reg, isns_print_stdout); + isns_simple_free(reg); + isns_client_destroy(clnt); + + return 0; +} + +/* + Creating file DB backend (/var/lib/isns) + DB: loading all objects from /var/lib/isns + Next ESI message in 3600 seconds + Incoming PDU xid=0001 seq=0 len=232 func=DevAttrReg client first last + Next message xid=0001 + Received message + + :: policy insecure function DevAttrReg (0001) permitted + :: policy insecure source iqn.2005-03.org.open-iscsi:blue permitted + :: policy insecure operation DevAttrReg on object 00000001 (Network +Entity) permitted + Replacing Network Entity (id 1) + DB: removed object 2 (Portal) + DB: removed object 4 (iSCSI Portal Group) + DB: removed object 3 (iSCSI Storage Node) + DB: removed object 1 (Network Entity) + DB: destroying object 2 (Portal) + DB: Purging object 2 (/var/lib/isns/00000002) + DB: destroying object 1 (Network Entity) + DB: Purging object 1 (/var/lib/isns/00000001) + DB: destroying object 3 (iSCSI Storage Node) + DB: Purging object 3 (/var/lib/isns/00000003) + DB: destroying object 4 (iSCSI Portal Group) + DB: Purging object 4 (/var/lib/isns/00000004) + :: policy insecure entity ID blue.pauw.homeunix.net permitted + :: policy insecure operation DevAttrReg on Network Entity object +permitted + DB: Storing object 5 -> /var/lib/isns/00000005 + DB: added object 5 (Network Entity) state 1 + DB: Storing object 5 -> /var/lib/isns/00000005 + isns_esi_callback(0x9dee788, 0x10) + Deleting SCN registration for iqn.2007-03.com.example:stgt.disk + isns_esi_callback(0x9deeae0, 0x10) + isns_esi_callback(0x9deea30, 0x10) + isns_esi_callback(0x9deec80, 0x10) + SCN multicast <iSCSI Storage Node 3, removed> + isns_scn_callback(0x9deec80, 0x10) + isns_esi_callback(0x9def4b0, 0xc) + Enable ESI monitoring for entity 5 + + */ diff --git a/utils/open-isns/tests/pauw3.c b/utils/open-isns/tests/pauw3.c new file mode 100644 index 0000000..3be0baa --- /dev/null +++ b/utils/open-isns/tests/pauw3.c @@ -0,0 +1,139 @@ +/* + * This tests another problem reported by Albert, where a + * re-registration shortly before ESI expiry would fail + * to resurrect the registration properly. + * + * Usage: + * pauw3 [options] timeout + * + * Where timeout is the delay until we try to re-register + */ + +#include <getopt.h> +#include <unistd.h> + +#include <isns.h> +#include <paths.h> +#include <util.h> +#include <message.h> + +#define ADD(type, tag, value) \ + isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value) +#define STR(tag, value) ADD(string, tag, value) +#define U32(tag, value) ADD(uint32, tag, value) +#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag) +#define TARGET(name, alias, auth) \ + STR(ISCSI_NAME, name); \ + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \ + STR(ISCSI_ALIAS, alias); \ + STR(ISCSI_AUTHMETHOD, auth) + +int +main(int argc, char **argv) +{ + const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG; + isns_client_t *clnt; + isns_attr_list_t *attrs; + isns_simple_t *reg; + isns_portal_info_t portal_info; + uint32_t status; + int opt_replace = 1; + int c, n, timeout; + + while ((c = getopt(argc, argv, "c:d:n")) != -1) { + switch (c) { + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + case 'n': + opt_replace = 0; + break; + + default: + isns_fatal("Unknown option\n"); + } + } + + if (optind != argc - 1) + isns_fatal("Need timeout argument\n"); + timeout = parse_timeout(argv[optind]); + + isns_read_config(opt_configfile); + + /* + ---DevAttrReg[REPLACE]--- + Source: + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + Message attributes: + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + Operating attributes: + 0001 string : Entity identifier = "blue.pauw.homeunix.net" + 0002 uint32 : Entity protocol = iSCSI (2) + 0010 ipaddr : Portal IP address = 192.168.1.2 + 0011 uint32 : Portal TCP/UDP port = 33849/tcp + 0014 uint32 : ESI port = 56288/tcp + 0020 string : iSCSI name = "iqn.2005-03.org.open-iscsi:blue" + 0021 uint32 : iSCSI node type = Initiator + 0022 string : iSCSI alias = "blue.pauw.homeunix.net" + + [...] + response status 0x0003 (Invalid registration) + + This would fail because we got confused about EID in + the replace case. + */ + isns_assign_string(&isns_config.ic_source_name, + "iqn.2005-03.org.open-iscsi:blue"); + + for (n = 0; n < 2; ++n) { + clnt = isns_create_default_client(NULL); + reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, + clnt->ic_source, NULL); + reg->is_replace = opt_replace; + + /* Message attributes */ + attrs = ®->is_message_attrs; + STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); + + /* Operating attributes */ + attrs = ®->is_operating_attrs; + + STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); + U32(ENTITY_PROTOCOL, 2); + + isns_portal_parse(&portal_info, "192.168.1.2:33849/tcp", NULL); + isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + attrs); + + U32(ESI_PORT, 56288); + STR(ISCSI_NAME, "iqn.2005-03.org.open-iscsi:blue"); + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_INITIATOR_MASK); + STR(ISCSI_ALIAS, "blue.pauw.homeunix.net"); + isns_simple_print(reg, isns_print_stdout); + + status = isns_client_call(clnt, ®); + + if (status != ISNS_SUCCESS) + isns_fatal("Unable to register object: %s\n", + isns_strerror(status)); + + printf("Successfully registered object\n"); + // isns_simple_print(reg, isns_print_stdout); + isns_simple_free(reg); + isns_client_destroy(clnt); + + if (n == 0) { + printf("Sleeping for %d seconds\n", timeout); + sleep(timeout); + } + } + + return 0; +} diff --git a/utils/open-isns/tests/pauw4.c b/utils/open-isns/tests/pauw4.c new file mode 100644 index 0000000..9510ddd --- /dev/null +++ b/utils/open-isns/tests/pauw4.c @@ -0,0 +1,137 @@ +/* + * Test MS initiator registration. + * The oddity about this is that the PG object precedes the + * initiator object in the message. + */ + +#include <getopt.h> +#include <unistd.h> + +#include <isns.h> +#include <paths.h> +#include <util.h> +#include <message.h> + +#define ADD(type, tag, value) \ + isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value) +#define STR(tag, value) ADD(string, tag, value) +#define U32(tag, value) ADD(uint32, tag, value) +#define NIL(tag) isns_attr_list_append_nil(attrs, ISNS_TAG_##tag) +#define TARGET(name, alias, auth) \ + STR(ISCSI_NAME, name); \ + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); \ + STR(ISCSI_ALIAS, alias); \ + STR(ISCSI_AUTHMETHOD, auth) + +int +main(int argc, char **argv) +{ + const char *opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG; + isns_client_t *clnt; + isns_attr_list_t *attrs; + isns_simple_t *reg; + isns_portal_info_t portal_info; + uint32_t status; + int opt_replace = 1; + int c; + + while ((c = getopt(argc, argv, "c:d:n")) != -1) { + switch (c) { + case 'c': + opt_configfile = optarg; + break; + + case 'd': + isns_enable_debugging(optarg); + break; + + case 'n': + opt_replace = 0; + break; + + default: + isns_fatal("Unknown option\n"); + } + } + + isns_read_config(opt_configfile); + + isns_assign_string(&isns_config.ic_source_name, + "iqn.1991-05.com.microsoft:orange"); + + clnt = isns_create_default_client(NULL); + + reg = isns_simple_create(ISNS_SCN_DEREGISTER, clnt->ic_source, NULL); + + /* Message attributes */ + attrs = ®->is_message_attrs; + STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange"); + + status = isns_client_call(clnt, ®); + if (status != ISNS_SUCCESS) + isns_error("SCNDereg failed: %s\n", isns_strerror(status)); + isns_simple_free(reg); + + + reg = isns_simple_create(ISNS_DEVICE_DEREGISTER, clnt->ic_source, NULL); + + attrs = ®->is_operating_attrs; + STR(ENTITY_IDENTIFIER, "troopa.nki.nl"); + U32(ENTITY_PROTOCOL, 2); + + isns_portal_parse(&portal_info, "192.168.1.40:3229/tcp", NULL); + isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + attrs); + + STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange"); + + status = isns_client_call(clnt, ®); + if (status != ISNS_SUCCESS) + isns_fatal("DevDereg failed: %s\n", isns_strerror(status)); + isns_simple_free(reg); + + reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, clnt->ic_source, NULL); + reg->is_replace = opt_replace; + + attrs = ®->is_operating_attrs; + STR(ENTITY_IDENTIFIER, "troopa.nki.nl"); + U32(ENTITY_PROTOCOL, 2); + + isns_portal_parse(&portal_info, "192.168.1.40:3229/tcp", NULL); + isns_portal_to_attr_list(&portal_info, + ISNS_TAG_PORTAL_IP_ADDRESS, + ISNS_TAG_PORTAL_TCP_UDP_PORT, + attrs); + + U32(SCN_PORT, 3230); + U32(ESI_PORT, 3230); + + U32(PG_TAG, 1); + STR(PG_ISCSI_NAME, "iqn.1991-05.com.microsoft:orange"); + + STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange"); + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_INITIATOR_MASK); + STR(ISCSI_ALIAS, "<MS SW iSCSI Initiator>"); + + status = isns_client_call(clnt, ®); + if (status != ISNS_SUCCESS) + isns_fatal("DevAttrReg failed: %s\n", isns_strerror(status)); + isns_simple_free(reg); + + reg = isns_simple_create(ISNS_DEVICE_GET_NEXT, clnt->ic_source, NULL); + attrs = ®->is_message_attrs; + NIL(ISCSI_NAME); + + attrs = ®->is_operating_attrs; + U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); + NIL(ISCSI_NODE_TYPE); + + status = isns_client_call(clnt, ®); + if (status != ISNS_SUCCESS) + isns_fatal("DevGetNext failed: %s\n", isns_strerror(status)); + isns_simple_free(reg); + + return 0; +} diff --git a/utils/open-isns/tests/server.conf b/utils/open-isns/tests/server.conf new file mode 100644 index 0000000..fc0bb5a --- /dev/null +++ b/utils/open-isns/tests/server.conf @@ -0,0 +1,11 @@ +BindAddress = @SERVER_ADDRESS@ +SourceName = @SOURCE_NAME@ +Database = @DB_PATH@ +RegistrationPeriod = 2h +ESIMinInterval = 1m +ESIMinInterval = 5m +Security = @NOT_SET@ +AuthKeyFile = @AUTH_KEY@ +ClientKeyStore = DB: +PIDFile = @MYDIR@/pid +ControlSocket = @MYDIR@/control diff --git a/utils/open-isns/tests/test01.pl b/utils/open-isns/tests/test01.pl new file mode 100644 index 0000000..258acff --- /dev/null +++ b/utils/open-isns/tests/test01.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and simple query of +# single client. + +push(@INC, "."); +require "harness.pl"; + +&test_prep("test01", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +# 1: Enroll the test client +&isns_enroll_client($client); + +# 2: Register an initiator with default portal +&isns_register_client($client, "initiator portal"); + +# 3: Run a simple query +&isns_query_objects($client, "eid"); + +# 99: Unregister client +&isns_unregister_client("99-unregistration", $client); + +&isns_finish; diff --git a/utils/open-isns/tests/test02.pl b/utils/open-isns/tests/test02.pl new file mode 100644 index 0000000..208bed5 --- /dev/null +++ b/utils/open-isns/tests/test02.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and simple query of +# two clients, and simple DD functionality. + +push(@INC, "."); +require "harness.pl"; + +&test_prep("test02", @ARGV); + +$server = &create_server; +$client1 = &create_client($server, "127.1.0.1"); +$client2 = &create_client($server, "127.1.0.2"); + +&isns_start_server($server); + +# 1: Enroll the client1 +&isns_enroll_client($client1); + +# 2: Enroll the client1 +&isns_enroll_client($client2, "node-type=target"); + +&isns_stage("registration", "Registering both clients"); +&__isns_register_client($client1, "initiator portal"); +&__isns_register_client($client2, "target portal"); +&isns_verify_db($server); + +# Now each of the two clients should just see +# itself +&isns_query_objects($client1, "eid"); +&isns_query_objects($client2, "eid"); + +# Register a DD linking the two nodes +&isns_register_domain($client1, "member-name=isns.client1", "member-name=isns.client2"); + +# Now the clients should see each other +&isns_query_objects($client1, "eid"); +&isns_query_objects($client2, "eid"); + +# Initiator querying for target: +&isns_query_objects($client1, "iscsi-node-type=Target"); + +# Add another member to this DD, and re-add client2 (making +# sure the server doesn't generate dupes) +&isns_register_domain($client1, "dd-id=1", "member-name=isns.client2", "member-name=iqn.com.foobar:disk1"); + +# Query the list of DDs we're a member of +&isns_query_objects($client1, "dd-id"); + +# Remove some entries from the DD +&isns_deregister_domain($client1, "1", "member-iscsi-idx=10"); +&isns_deregister_domain($client1, "1", "member-name=iqn.com.foobar:disk1"); +&isns_register_domain($client1, "dd-id=1", "member-name=isns.client2"); +&isns_deregister_domain($client1, "1"); + +&isns_finish; diff --git a/utils/open-isns/tests/test03.pl b/utils/open-isns/tests/test03.pl new file mode 100644 index 0000000..3cc0d71 --- /dev/null +++ b/utils/open-isns/tests/test03.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and unregistration. + +push(@INC, "."); +require "harness.pl"; + +&test_prep("test03", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +&isns_enroll_client($client); +&isns_register_client($client, "initiator portal"); + +# Unregistering the portal should leave the iscsi node and +# portal group active, and move the portal to state limbo. +&isns_unregister_client($client, "portal=127.0.0.1:860"); + +# As the iscsi node goes away, so should the whole entity +&isns_unregister_client($client, "iscsi-name=isns.client1"); + +&isns_finish; diff --git a/utils/open-isns/tests/test04.pl b/utils/open-isns/tests/test04.pl new file mode 100644 index 0000000..8181a4e --- /dev/null +++ b/utils/open-isns/tests/test04.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case verifies that the database remains intact +# across server restarts. + +push(@INC, "."); +require "harness.pl"; + +&test_prep("test04", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +&isns_enroll_client($client); +&isns_register_client($client, "initiator portal"); + +# Restart the server, and make sure it still displays +# the database properly +&isns_stage("restart", "Restarting server process"); +&isns_restart_server($server); +&isns_verify_db($server); + +# Run a simple query +&isns_query_objects($client, "iscsi-name"); + +&isns_finish; diff --git a/utils/open-isns/tests/test05.pl b/utils/open-isns/tests/test05.pl new file mode 100644 index 0000000..694d7c3 --- /dev/null +++ b/utils/open-isns/tests/test05.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case verifies entity expiry + +push(@INC, "."); +require "harness.pl"; + +&isns_prep_slow_test("test05", 30, @ARGV); + +$server = &create_server({ "RegistrationPeriod" => "20s" }); +$client = &create_client($server); + +&isns_start_server($server); + +&isns_enroll_client($client); +&isns_register_client($client, "initiator portal"); + +&isns_stage("expired", "Waiting for registration period to expire (25s)"); +&isns_idle(25); +&isns_verify_db($server); + +&isns_finish; + diff --git a/utils/open-isns/tests/test06.pl b/utils/open-isns/tests/test06.pl new file mode 100644 index 0000000..6b6aa05 --- /dev/null +++ b/utils/open-isns/tests/test06.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates DevAttrReg replace mode. + +push(@INC, "."); +require "harness.pl"; + +&test_prep("test06", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +# 1: Enroll the client +&isns_enroll_client($client); + +# 2: Register a simple initiator with one portal +&isns_register_client($client, "initiator portal"); + +$eid = &isns_query_eid($client); +unless ($eid) { + &isns_die("Cannot obtain entity ID"); +} + +# Now replace the portal with different values +&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.1:iscsi"); +&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.2:iscsi"); + +&isns_register_domain($client, "member-name=isns.client1"); + +# Replace our registration once more. Now the object index of the +# initiator should not change, since it's a domain member now. +&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.1:iscsi"); + +# Make the portal a domain member too. Now even the portal index should stay +# the same. Note that we do not replace the whole entity now, but just the +# portal +&isns_register_domain($client, "dd-id=1 member-addr=192.168.1.1 member-port=860"); +&isns_register_client($client, "--replace --key portal=192.168.1.1:iscsi portal=192.168.1.2:iscsi"); +&isns_register_client($client, "--replace --key portal=192.168.1.2:iscsi portal=192.168.1.1:iscsi"); + +# Now unregister the whole client, and re-register. +# Portal and client index should remain the same +&isns_unregister_client($client, "eid=$eid"); +&isns_register_client($client, "initiator portal=192.168.1.1:iscsi"); + +&isns_finish; diff --git a/utils/open-isns/tests/test07.pl b/utils/open-isns/tests/test07.pl new file mode 100644 index 0000000..c13df11 --- /dev/null +++ b/utils/open-isns/tests/test07.pl @@ -0,0 +1,37 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates that the server discards portals +# that do not respond to ESI messages + +push(@INC, "."); +require "harness.pl"; + +&isns_prep_slow_test("test07", 30, @ARGV); + +$server = &create_server({ "ESIMinInterval" => "5s" }); +$client = &create_client($server); + +&isns_start_server($server); + +# 1: Enroll the client +&isns_enroll_client($client); + +# 2: Register a simple initiator with one portal +&isns_register_client($client, "initiator portal,esi-port=65535,esi-interval=5"); + +&isns_stage("expired", "Waiting for ESI to expire (~15 sec)"); +&isns_idle(15); +&isns_verify_db($server); + +# 3: Register a simple initiator with two portals, one with ESI and one without. +# When the ESI monitored portal expires, this should still take down +# the whole network entity. +&isns_register_client($client, "initiator portal,esi-port=65535,esi-interval=5 portal=127.0.0.1:1"); + +&isns_stage("expired", "Waiting for ESI to expire (~15 sec)"); +&isns_idle(15); +&isns_verify_db($server); + +&isns_finish; diff --git a/utils/open-isns/tests/test08.pl b/utils/open-isns/tests/test08.pl new file mode 100644 index 0000000..1487532 --- /dev/null +++ b/utils/open-isns/tests/test08.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and simple query of +# single client. + +push(@INC, "."); +require "harness.pl"; + +# For now, this one will run w/o security only +push(@ARGV, '-i'); + +&test_prep("test08", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +&isns_external_test($client, "tests/pauw1"); + +&isns_finish; diff --git a/utils/open-isns/tests/test09.pl b/utils/open-isns/tests/test09.pl new file mode 100644 index 0000000..bd2bd7f --- /dev/null +++ b/utils/open-isns/tests/test09.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and simple query of +# single client. + +push(@INC, "."); +require "harness.pl"; + +# For now, this one will run w/o security only +push(@ARGV, '-i'); + +&test_prep("test09", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +&isns_external_test($client, "tests/pauw2"); + +&isns_finish; diff --git a/utils/open-isns/tests/test10.pl b/utils/open-isns/tests/test10.pl new file mode 100644 index 0000000..7286521 --- /dev/null +++ b/utils/open-isns/tests/test10.pl @@ -0,0 +1,33 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and simple query of +# single client. + +push(@INC, "."); +require "harness.pl"; + +# For now, this one will run w/o security only +push(@ARGV, '-i'); + +&isns_prep_slow_test("test10", 20, @ARGV); + +$server = &create_server({ "ESIMinInterval" => "10s" }); +$client = &create_client($server); + +&isns_start_server($server); + +&isns_external_test($client, "tests/pauw3", "16"); + +&isns_stage("expired", "Waiting for ESI to come around"); +&isns_idle(5); +&isns_verify_db($server); + +&isns_external_test($client, "tests/pauw3", "-n", "16"); + +&isns_stage("expired", "Waiting for ESI to come around"); +&isns_idle(5); +&isns_verify_db($server); + +&isns_finish; diff --git a/utils/open-isns/tests/test11.pl b/utils/open-isns/tests/test11.pl new file mode 100644 index 0000000..2745955 --- /dev/null +++ b/utils/open-isns/tests/test11.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +# +# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> +# +# This test case validates registration and simple query of +# single client. + +push(@INC, "."); +require "harness.pl"; + +# For now, this one will run w/o security only +push(@ARGV, '-i'); + +&test_prep("test11", @ARGV); + +$server = &create_server; +$client = &create_client($server); + +&isns_start_server($server); + +&isns_external_test($client, "tests/pauw4"); + +&isns_finish; diff --git a/utils/open-isns/timer.c b/utils/open-isns/timer.c new file mode 100644 index 0000000..ed8a23f --- /dev/null +++ b/utils/open-isns/timer.c @@ -0,0 +1,126 @@ +/* + * Timers (one-short and periodic) + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <time.h> +#include "isns.h" +#include "util.h" + +typedef struct isns_timer isns_timer_t; +struct isns_timer { + isns_list_t it_list; + time_t it_when; + unsigned int it_period; + isns_timer_callback_t * it_func; + void * it_data; +}; + + +static ISNS_LIST_DECLARE(timers); + +static void +__isns_arm_timer(isns_timer_t *tm) +{ + isns_list_t *pos, *next; + time_t when = tm->it_when; + + isns_list_foreach(&timers, pos, next) { + isns_timer_t *cur = isns_list_item(isns_timer_t, it_list, pos); + + if (when < cur->it_when) + break; + } + isns_item_insert_before(pos, &tm->it_list); +} + +static isns_timer_t * +__isns_create_timer(time_t when, + unsigned int period, + isns_timer_callback_t *fn, + void *data) +{ + isns_timer_t *tm; + + tm = isns_calloc(1, sizeof(*tm)); + tm->it_when = when; + tm->it_period = period; + tm->it_func = fn; + tm->it_data = data; + return tm; +} + +void +isns_add_timer(unsigned int period, + isns_timer_callback_t *fn, + void *data) +{ + isns_timer_t *tm; + + isns_assert(period); + tm = __isns_create_timer(time(NULL) + period, period, fn, data); + __isns_arm_timer(tm); +} + +void +isns_add_oneshot_timer(unsigned int expires, + isns_timer_callback_t *fn, + void *data) +{ + isns_timer_t *tm; + + tm = __isns_create_timer(time(NULL) + expires, 0, fn, data); + __isns_arm_timer(tm); +} + +void +isns_cancel_timer(isns_timer_callback_t *fn, void *data) +{ + isns_list_t *pos, *next; + + isns_list_foreach(&timers, pos, next) { + isns_timer_t *tm = isns_list_item(isns_timer_t, it_list, pos); + + if (tm->it_func == fn + && (data == NULL || tm->it_data == data)) { + isns_list_del(pos); + isns_free(tm); + } + } +} + +time_t +isns_run_timers(void) +{ + + while (!isns_list_empty(&timers)) { + isns_timer_t *tm = isns_list_item(isns_timer_t, it_list, timers.next); + isns_timer_callback_t *func; + time_t expire; + void *data; + + expire = tm->it_when; + if (time(NULL) < expire) + return expire; + + isns_list_del(&tm->it_list); + func = tm->it_func; + data = tm->it_data; + expire = 0; + + /* If it's a periodic timer, rearm it now. This allows + * the timer callback to cancel the timer. */ + if (tm->it_period) { + tm->it_when = time(NULL) + tm->it_period; + __isns_arm_timer(tm); + } else { + isns_free(tm); + } + + func(data); + } + + return 0; +} diff --git a/utils/open-isns/types.h b/utils/open-isns/types.h new file mode 100644 index 0000000..ddd153f --- /dev/null +++ b/utils/open-isns/types.h @@ -0,0 +1,57 @@ +/* + * Open-iSNS types + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_TYPES_H +#define ISNS_TYPES_H + +typedef struct isns_simple isns_simple_t; +typedef struct isns_source isns_source_t; +typedef struct isns_object isns_object_t; +typedef struct isns_relation isns_relation_t; +typedef struct isns_attr isns_attr_t; +typedef struct isns_attr_list isns_attr_list_t; +typedef struct isns_message isns_message_t; +typedef struct isns_socket isns_socket_t; +typedef struct isns_db isns_db_t; +typedef struct isns_tag_type isns_tag_type_t; +typedef const struct isns_object_template isns_object_template_t; +typedef struct isns_authdata isns_authdata_t; +typedef struct isns_security isns_security_t; +typedef struct isns_principal isns_principal_t; +typedef struct isns_policy isns_policy_t; +typedef struct isns_keystore isns_keystore_t; +typedef struct isns_scope isns_scope_t; +typedef struct isns_portal_info isns_portal_info_t; +typedef struct isns_server isns_server_t; +typedef struct isns_db_event isns_db_event_t; +typedef struct isns_bitvector isns_bitvector_t; + +typedef struct isns_object_list { + unsigned int iol_count; + isns_object_t ** iol_data; +} isns_object_list_t; + +#define ISNS_OBJECT_LIST_INIT { .iol_count = 0, .iol_data = NULL } + +/* + * An attribute list + */ +struct isns_attr_list { + unsigned int ial_count; + isns_attr_t ** ial_data; +}; +#define ISNS_ATTR_LIST_INIT { .ial_count = 0, .ial_data = NULL } + +/* + * Function types. + */ +typedef void isns_print_fn_t(const char *, ...); +typedef void isns_timer_callback_t(void *); + + +#endif /* ISNS_TYPES_H */ + + diff --git a/utils/open-isns/util.c b/utils/open-isns/util.c new file mode 100644 index 0000000..4c0a7b2 --- /dev/null +++ b/utils/open-isns/util.c @@ -0,0 +1,263 @@ +/* + * util.c + * + * Misc utility functions + * + * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <err.h> +#include <errno.h> +#include "util.h" + +unsigned long +parse_size(const char *arg) +{ + unsigned long mult = 1, ret; + char *s; + + ret = strtol(arg, &s, 0); + + switch (*s++) { + case 'g': + case 'G': + mult = 1024 * 1024 * 1024; + break; + case 'm': + case 'M': + mult = 1024 * 1024; + break; + case 'k': + case 'K': + mult = 1024; + break; + + case '\0': + return ret; + + default: + bad: + err(1, "parse_size: unknown unit in \"%s\"\n", arg); + } + + if (*s != '\0') + goto bad; + + return mult * ret; +} + +char * +print_size(unsigned long size) +{ + static char unit[] = "-kMG"; + static char buffer[64]; + unsigned int power = 0; + + while (size && !(size % 1024) && power < sizeof(unit)) { + size /= 1024; + power++; + } + + if (!power) { + snprintf(buffer, sizeof(buffer), "%lu", size); + } else { + snprintf(buffer, sizeof(buffer), "%lu%c", + size, unit[power]); + } + return buffer; +} + +unsigned int +parse_count(const char *arg) +{ + unsigned long ret; + char *s; + + ret = strtoul(arg, &s, 0); + if (*s != '\0') + err(1, "parse_count: unexpected character in \"%s\"\n", arg); + + return ret; +} + +int +parse_int(const char *arg) +{ + long ret; + char *s; + + ret = strtol(arg, &s, 0); + if (*s != '\0') + err(1, "parse_count: unexpected character in \"%s\"\n", arg); + + return ret; +} + +long long +parse_longlong(const char *arg) +{ + long long ret; + char *s; + + ret = strtoll(arg, &s, 0); + if (*s != '\0') + err(1, "parse_count: unexpected character in \"%s\"\n", arg); + + return ret; +} + +double +parse_double(const char *arg) +{ + double ret; + char *s; + + ret = strtod(arg, &s); + if (*s != '\0') + err(1, "parse_count: unexpected character in \"%s\"\n", arg); + + return ret; +} + +unsigned int +parse_timeout(const char *arg) +{ + unsigned int v, ret = 0; + char *s; + + do { + v = strtoul(arg, &s, 10); + switch (*s) { + case '\0': + ret += v; + break; + case 'd': + v *= 24; + case 'h': + v *= 60; + case 'm': + v *= 60; + case 's': + ret += v; + ++s; + break; + + default: + errx(1, "parse_timeout: unexpected character in \"%s\"\n", + arg); + } + + arg = s; + } while (*arg); + + return ret; +} + +void +isns_string_array_append(struct string_array *array, const char *val) +{ + if (!(array->count % 32)) { + array->list = isns_realloc(array->list, + (array->count + 32) * sizeof(val)); + } + array->list[array->count++] = val? isns_strdup(val) : NULL; +} + +void +isns_string_array_destroy(struct string_array *array) +{ + unsigned int i; + + for (i = 0; i < array->count; ++i) + isns_free(array->list[i]); + isns_free(array->list); + memset(array, 0, sizeof(*array)); +} + +void +isns_assign_string(char **var, const char *val) +{ + char *s = NULL; + + if (val && !(s = isns_strdup(val))) + errx(1, "out of memory"); + + if (*var) + isns_free(*var); + *var = s; +} + +/* + * Recursively create a directory + */ +int +isns_mkdir_recursive(const char *pathname) +{ + const char *orig_pathname = pathname; + char *squirrel[64]; + char *copy = NULL, *s; + int ns = 0; + + if (!pathname || !strcmp(pathname, ".")) + return 0; + while (1) { + if (mkdir(pathname, 0755) >= 0) { + if (ns == 0) + break; + *squirrel[--ns] = '/'; + continue; + } + + if (errno == EEXIST) + goto good; + if (errno != ENOENT) + goto bad; + + if (copy == NULL) { + copy = isns_strdup(pathname); + pathname = copy; + } + + s = strrchr(copy, '/'); + while (s > copy && s[-1] == '/') + --s; + *s = '\0'; + + isns_assert(ns < 64); + squirrel[ns++] = s; + + if (s == copy) + goto bad; + } + +good: if (copy) + isns_free(copy); + errno = 0; + return 0; + +bad: if (copy) + isns_free(copy); + perror(orig_pathname); + return -1; +} + +/* + * This one differs from POSIX dirname; it does not + * modify its argument + */ +const char * +isns_dirname(const char *pathname) +{ + static char buffer[4096]; + char *s; + + strcpy(buffer, pathname); + if ((s = strrchr(buffer, '/')) != NULL) { + *s = '\0'; + return buffer; + } + return "."; +} diff --git a/utils/open-isns/util.h b/utils/open-isns/util.h new file mode 100644 index 0000000..bd6b979 --- /dev/null +++ b/utils/open-isns/util.h @@ -0,0 +1,288 @@ +/* + * Utility functions + * + * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef UTIL_H +#define UTIL_H + +#include <sys/types.h> +#include <stdint.h> +#include <stdio.h> +#include <stddef.h> +#include <string.h> // for strdup +#include "types.h" + +#define array_num_elements(a) (sizeof(a) / sizeof((a)[0])) + +const char * isns_dirname(const char *); +int isns_mkdir_recursive(const char *); + +extern const char *parser_separators; +char * parser_get_next_line(FILE *); +char * parser_get_next_word(char **); +char * parser_get_rest_of_line(char **); +int parser_split_line(char *, unsigned int, char **); + +unsigned long parse_size(const char *); +unsigned int parse_count(const char *); +int parse_int(const char *); +long long parse_longlong(const char *); +double parse_double(const char *); +unsigned int parse_timeout(const char *); + +char * print_size(unsigned long); + +/* + * Very simple and stupid string array. + */ +struct string_array { + unsigned int count; + char ** list; +}; + +void isns_string_array_append(struct string_array *, const char *); +void isns_string_array_destroy(struct string_array *); + +void isns_assign_string(char **, const char *); + +void isns_write_pidfile(const char *); +void isns_update_pidfile(const char *); +void isns_remove_pidfile(const char *); + +extern void isns_log_background(void); +extern void isns_assert_failed(const char *, + const char *, unsigned int); +extern void isns_fatal(const char *, ...); +extern void isns_warning(const char *, ...); +extern void isns_error(const char *, ...); +extern void isns_notice(const char *, ...); +extern void isns_debug_general(const char *, ...); +extern void isns_debug_socket(const char *, ...); +extern void isns_debug_protocol(const char *, ...); +extern void isns_debug_message(const char *, ...); +extern void isns_debug_state(const char *, ...); +extern void isns_debug_auth(const char *, ...); +extern void isns_debug_scn(const char *, ...); +extern void isns_debug_esi(const char *, ...); +extern void isns_enable_debugging(const char *); +extern int isns_debug_enabled(int); + +enum { + DBG_GENERAL = 0, + DBG_SOCKET, + DBG_PROTOCOL, + DBG_MESSAGE, + DBG_STATE, + DBG_AUTH, + DBG_SCN, + DBG_ESI, +}; + +/* + * There's no htonll yet + */ +#ifndef htonll +# include <endian.h> +# include <byteswap.h> +# if __BYTE_ORDER == __BIG_ENDIAN +# define htonll(x) (x) +# define ntohll(x) (x) +# elif __BYTE_ORDER == __LITTLE_ENDIAN +# define htonll(x) __bswap_64(x) +# define ntohll(x) __bswap_64(x) +# endif +#endif + +/* + * One of the those eternal staples of C coding: + */ +#ifndef MIN +# define MIN(a, b) ((a) < (b)? (a) : (b)) +# define MAX(a, b) ((a) > (b)? (a) : (b)) +#endif + +#define DECLARE_BITMAP(name, NBITS) \ + uint32_t name[(NBITS+31) >> 5] = { 0 } + +#define __BIT_INDEX(nr) (nr >> 5) +#define __BIT_MASK(nr) (1 << (nr & 31)) + +static inline void +set_bit(uint32_t *map, unsigned int nr) +{ + map[__BIT_INDEX(nr)] |= __BIT_MASK(nr); +} + +static inline void +clear_bit(uint32_t *map, unsigned int nr) +{ + map[__BIT_INDEX(nr)] &= ~__BIT_MASK(nr); +} + +static inline int +test_bit(const uint32_t *map, unsigned int nr) +{ + return !!(map[__BIT_INDEX(nr)] & __BIT_MASK(nr)); +} + +/* + * Dynamically sized bit vector + */ +extern isns_bitvector_t *isns_bitvector_alloc(void); +extern void isns_bitvector_init(isns_bitvector_t *); +extern void isns_bitvector_destroy(isns_bitvector_t *); +extern void isns_bitvector_free(isns_bitvector_t *); +extern int isns_bitvector_test_bit(const isns_bitvector_t *, unsigned int); +extern int isns_bitvector_set_bit(isns_bitvector_t *, unsigned int); +extern int isns_bitvector_clear_bit(isns_bitvector_t *, unsigned int); +extern int isns_bitvector_is_empty(const isns_bitvector_t *); +extern int isns_bitvector_intersect(const isns_bitvector_t *a, + const isns_bitvector_t *b, + isns_bitvector_t *result); +extern void isns_bitvector_print(const isns_bitvector_t *, + isns_print_fn_t *); +extern void isns_bitvector_foreach(const isns_bitvector_t *bv, + int (*cb)(uint32_t, void *), + void *user_data); + +/* + * List manipulation primites + */ +typedef struct isns_list isns_list_t; +struct isns_list { + isns_list_t * next; + isns_list_t * prev; +}; + +#define ISNS_LIST_DECLARE(list) \ + isns_list_t list = { &list, &list } + +static inline void +isns_list_init(isns_list_t *head) +{ + head->next = head->prev = head; +} + +static inline void +__isns_list_insert(isns_list_t *prev, isns_list_t *item, isns_list_t *next) +{ + item->next = next; + item->prev = prev; + next->prev = item; + prev->next = item; +} + +static inline void +isns_list_append(isns_list_t *head, isns_list_t *item) +{ + __isns_list_insert(head->prev, item, head); +} + +static inline void +isns_list_insert(isns_list_t *head, isns_list_t *item) +{ + __isns_list_insert(head, item, head->next); +} + +static inline void +isns_item_insert_before(isns_list_t *where, isns_list_t *item) +{ + __isns_list_insert(where->prev, item, where); +} + +static inline void +isns_item_insert_after(isns_list_t *where, isns_list_t *item) +{ + __isns_list_insert(where, item, where->next); +} + +static inline void +isns_list_del(isns_list_t *item) +{ + isns_list_t *prev = item->prev; + isns_list_t *next = item->next; + + prev->next = next; + next->prev = prev; + item->next = item->prev = item; +} + +static inline int +isns_list_empty(const isns_list_t *head) +{ + return head == head->next; +} + +static inline void +isns_list_move(isns_list_t *dst, isns_list_t *src) +{ + isns_list_t *prev, *next; + isns_list_t *head, *tail; + + if (isns_list_empty(src)) + return; + + prev = dst->prev; + next = dst; + + head = src->next; + tail = src->prev; + + next->prev = tail; + prev->next = head; + head->prev = prev; + tail->next = next; + + src->next = src->prev = src; +} + +#define isns_list_item(type, member, ptr) \ + container_of(type, member, ptr) + +#define isns_list_foreach(list, __pos, __next) \ + for (__pos = (list)->next; \ + (__pos != list) && (__next = __pos->next, 1); \ + __pos = __next) + +#if 0 +/* This is defined in stddef */ +#define offsetof(type, member) ((unsigned long) &(((type *) 0)->member)) +#endif +#define container_of(type, member, ptr) \ + ((type *) (((unsigned char *) ptr) - offsetof(type, member))) + +/* + * Use isns_assert instead of libc's assert, so that the + * message can be captured and sent to syslog. + */ +#define isns_assert(condition) do { \ + if (!(condition)) \ + isns_assert_failed(#condition, \ + __FILE__, __LINE__); \ +} while (0) + +#ifndef MDEBUG +# define isns_malloc(size) malloc(size) +# define isns_calloc(n, size) calloc(n, size) +# define isns_realloc(p, size) realloc(p, size) +# define isns_strdup(s) strdup(s) +# define isns_free(p) free(p) +#else +# define isns_malloc(size) isns_malloc_fn(size, __FILE__, __LINE__) +# define isns_calloc(n, size) isns_calloc_fn(n, size, __FILE__, __LINE__) +# define isns_realloc(p, size) isns_realloc_fn(p, size, __FILE__, __LINE__) +# define isns_strdup(s) isns_strdup_fn(s, __FILE__, __LINE__) +# define isns_free(p) isns_free_fn(p, __FILE__, __LINE__) + +extern void * (*isns_malloc_fn)(size_t, const char *, unsigned int); +extern void * (*isns_calloc_fn)(unsigned int, size_t, + const char *, unsigned int); +extern void * (*isns_realloc_fn)(void *, size_t, + const char *, unsigned int); +extern char * (*isns_strdup_fn)(const char *, const char *, unsigned int); +extern void (*isns_free_fn)(void *, const char *, unsigned int); +#endif + +#endif /* UTIL_H */ diff --git a/utils/open-isns/vendor.c b/utils/open-isns/vendor.c new file mode 100644 index 0000000..e24164d --- /dev/null +++ b/utils/open-isns/vendor.c @@ -0,0 +1,41 @@ +/* + * iSNS vendor specific objects + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#include <stdlib.h> +#include <string.h> +#include "isns.h" +#include "objects.h" +#include "attrs.h" +#include "vendor.h" +#include "util.h" + +static uint32_t policy_attrs[] = { + OPENISNS_TAG_POLICY_SPI, + OPENISNS_TAG_POLICY_KEY, + OPENISNS_TAG_POLICY_ENTITY, + OPENISNS_TAG_POLICY_OBJECT_TYPE, + OPENISNS_TAG_POLICY_NODE_NAME, + OPENISNS_TAG_POLICY_NODE_TYPE, + OPENISNS_TAG_POLICY_FUNCTIONS, + OPENISNS_TAG_POLICY_VISIBLE_DD, + OPENISNS_TAG_POLICY_DEFAULT_DD, +}; + +static uint32_t policy_key_attrs[] = { + OPENISNS_TAG_POLICY_SPI, +}; + +isns_object_template_t isns_policy_template = { + .iot_name = "Policy", + .iot_handle = ISNS_OBJECT_TYPE_POLICY, + .iot_attrs = policy_attrs, + .iot_num_attrs = array_num_elements(policy_attrs), + .iot_keys = policy_key_attrs, + .iot_num_keys = array_num_elements(policy_key_attrs), + .iot_container = &isns_entity_template, + .iot_vendor_specific = 1, +}; + diff --git a/utils/open-isns/vendor.h b/utils/open-isns/vendor.h new file mode 100644 index 0000000..49c6132 --- /dev/null +++ b/utils/open-isns/vendor.h @@ -0,0 +1,56 @@ +/* + * iSNS "vendor-specific" protocol definitions + * + * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> + */ + +#ifndef ISNS_VENDOR_H +#define ISNS_VENDOR_H + +#include "isns-proto.h" + +/* + * We're poor, we don't own a OUI. Let's fake one. + */ +#define OPENISNS_VENDOR_OUI 0xFFFF00 +#define OPENISNS_VENDOR_PREFIX (OPENISNS_VENDOR_OUI << 8) +#define OPENISNS_IS_PRIVATE_ATTR(tag) (((tag) >> 16) == 0xFFFF) + +enum openisns_vendor_tag { + /* Security Policy Identifier */ + OPENISNS_TAG_POLICY_SPI = OPENISNS_VENDOR_PREFIX + ISNS_VENDOR_SPECIFIC_OTHER_BASE, + + __OPENISNS_TAG_POLICY_RESERVED, + + /* DSA signature key (public) */ + OPENISNS_TAG_POLICY_KEY, + + /* Entity name to use */ + OPENISNS_TAG_POLICY_ENTITY, + + /* Functions the client is permitted to invoke */ + OPENISNS_TAG_POLICY_FUNCTIONS, + + /* Object types the client is permitted to see. */ + OPENISNS_TAG_POLICY_OBJECT_TYPE, + + /* iSCSI node name the client is permitted to register. + * This attribute may occur multiple times. + * If absent, it defaults to POLICY_SOURCE_NAME + */ + OPENISNS_TAG_POLICY_NODE_NAME, + + /* Node type bitmap the client is permitted to register */ + OPENISNS_TAG_POLICY_NODE_TYPE, + + /* Default discovery domain the client will be + * placed in. + * Not used yet. + */ + OPENISNS_TAG_POLICY_DEFAULT_DD, + OPENISNS_TAG_POLICY_VISIBLE_DD, +}; + +extern const struct isns_object_template isns_policy_template; + +#endif /* ISNS_VENDOR_H */ |