From a132db90debbd168ab4b574b00bf75f257b76466 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 25 2020 09:52:45 +0000 Subject: fcoe-utils-1.0.32 base --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c3d07c --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# standard autotools stuff +.deps/ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +compile +config.log +config.status +configure +depcomp +install-sh +missing + +# other autoconf generated files +fcoe-utils.spec +fcoeplumb +fcoe_utils_version.h + +# compile generated files +*.o +fcoeadm +fcoemon +fipvlan +fcping +fcnsq +fcrls +etc/initd/fcoe + +# build.sh generated files +fcoe-utils-*.tar.gz +fcoe-utils-*.rpm diff --git a/CONFIGURE b/CONFIGURE new file mode 100644 index 0000000..d3c5b40 --- /dev/null +++ b/CONFIGURE @@ -0,0 +1,237 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..ea8c95e --- /dev/null +++ b/INSTALL @@ -0,0 +1,27 @@ +This document explains how to build the Open-FCoE +user space utilities. This includes fcoeadm, fcoemon and +their corresponding manual pages. + +## +# fcoe-utils +############ + +DEPENDENCIES + +* libhbalinux +* lldpad v0.9.43 or greater (optional configure flag to build without) + +* autoconf +* autotools +* sysconftool +* automake +* libtool + +1) Bootstrap, configure, make and make install + # ./bootstrap.sh + Either execute the next line for System V init script install + # rpm --eval "%configure" | sh + or the following line to install systemd unit scripts + # rpm --eval "%configure --with-systemdsystemunitdir=/" | sh + # make + # make install diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..ba302e0 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,87 @@ +## target programs, to be built and installed in $(prefix)/sbin +sbin_PROGRAMS = fcoeadm fcoemon fcping fipvlan fcnsq fcrls + +## all targets should look for headers in the include directory +AM_CPPFLAGS = -I${srcdir}/include -I${builddir}/include +## pass the sysconfdir into the C proprocessor +AM_CPPFLAGS += -DSYSCONFDIR="\"${sysconfdir}\"" -D_FORTIFY_SOURCE=2 +AM_CFLAGS = -Wall -Wformat=2 -Werror -Wmissing-prototypes -Wstrict-prototypes + +## rules for building fcoeadm +fcoeadm_SOURCES = fcoeadm.c fcoeadm_display.c +fcoeadm_LDADD = lib/libutil.a libopenfcoe.a $(PCIACCESS_LIBS) +fcoeadm_CFLAGS = $(AM_CFLAGS) + +## rules for building fcoemon +fcoemon_SOURCES = fcoemon.c +fcoemon_LDADD = lib/libutil.a -lrt +fcoemon_CFLAGS = $(AM_CFLAGS) $(DCBD_CFLAGS) + +## rules for building fcping +fcping_SOURCES = fcping.c +fcping_LDADD = lib/libutil.a libopenfcoe.a $(PCIACCESS_LIBS) -lrt +fcping_CFLAGS = $(AM_CFLAGS) + +## rules for building fipvlan +fipvlan_SOURCES = fipvlan.c +fipvlan_LDADD = lib/libutil.a + +AUTOMAKE_OPTIONS=subdir-objects +noinst_LIBRARIES = lib/libutil.a libopenfcoe.a +lib_libutil_a_SOURCES = lib/fcoe_utils.c lib/sa_log.c lib/sa_select.c \ + lib/sa_timer.c lib/sa_other.c lib/fip.c lib/rtnetlink.c lib/sa_sys.c \ + lib/sysfs_hba.c +libopenfcoe_a_SOURCES = libopenfcoe.c + +## header files that need to be distributed +noinst_HEADERS = fcoeadm_display.h fcoe_clif.h fcoemon.h \ + include/fcoemon_utils.h include/fcoe_utils.h include/fc_scsi.h \ + include/fc_types.h include/fip.h include/net_types.h include/rtnetlink.h \ + include/libopenfcoe.h include/scsi_netlink_fc.h include/scsi_netlink.h \ + include/strarr.h include/fc_ns.h include/fc_gs.h include/fc_els.h include/scsi_bsg_fc.h \ + include/sysfs_hba.h include/linux/rtnetlink.h include/linux/types.h include/linux/dcbnl.h + +## install configuration file in $(prefix)/etc/fcoe +fcoe_configdir = ${sysconfdir}/fcoe +dist_fcoe_config_DATA = etc/cfg-ethx + +## install systemd service files +if HAVE_SYSTEMD +dist_systemdsystemunit_DATA = etc/systemd/fcoe.service etc/systemd/fcoemon.socket +endif + +## man pages for fcoeadm and fcoemon +dist_man_MANS = doc/fcoeadm.8 doc/fcoemon.8 doc/fipvlan.8 doc/fcrls.8 \ + doc/fcnsq.8 doc/fcping.8 + +## init script for fcoemon +dist_noinst_SCRIPTS = etc/initd/initd.suse etc/initd/initd.fedora \ + contrib/fcc.sh contrib/fcoe-setup.sh contrib/fcoe_edd.sh \ + debug/dcbcheck.sh debug/fcoedump.sh + +CLEANFILES = etc/initd/fcoe +etc/initd/fcoe: + mkdir -p etc/initd + if [ -f /etc/fedora-release ] || [ -f /etc/redhat-release ]; then \ + ln -s ${abs_srcdir}/etc/initd/initd.fedora $@; \ + else \ + ln -s ${abs_srcdir}/etc/initd/initd.suse $@; \ + fi + +init_ddir = ${sysconfdir}/init.d +init_d_SCRIPTS = etc/initd/fcoe + +dist_noinst_DATA = README COPYING INSTALL CONFIGURE QUICKSTART fcoe-utils.spec etc/config + +bashcompletiondir = $(sysconfdir)/bash_completion.d +dist_bashcompletion_DATA = contrib/bash_completion/fcoeadm contrib/bash_completion/fcoemon + +install-data-hook: + if [ ! -f ${DESTDIR}${fcoe_configdir}/config ] ; then \ + cp ${srcdir}/etc/config ${DESTDIR}${fcoe_configdir}/config; \ + fi; + +uninstall-hook: + rm ${DESTDIR}${fcoe_configdir}/config + +AM_DISTCHECK_CONFIGURE_FLAGS = --with-systemdsystemunitdir='$${prefix}/lib/systemd/system' diff --git a/QUICKSTART b/QUICKSTART new file mode 100644 index 0000000..c001bc1 --- /dev/null +++ b/QUICKSTART @@ -0,0 +1,281 @@ +This document is a quickstart guide for a user connected to a Fibre Channel +Forwarder (FCF) and not a SW target. It covers the configuration of the kernel +and installation and configuration of Data Center Bridging (DCB), the HBA API +wrapper library, libhbalinux and fcoe-utils. + +This document was written using Fedora 11 as the installed operating system. +The instructions may need adjustments for them to work on other +distributions. + +## +# Dependencies +############### + +git://open-fcoe.org/libHBAAPI.git +git://open-fcoe.org/libhbalinux.git +git://open-fcoe.org/fcoe-utils.git + +## +# Kernel Configuration and Compilation +###################################### + +1) Download kernel source + There are a number of good choices regarding the most appropriate + kernel source for your needs. fcoe-next.git is a repository that may be + unstable, but will have the latest code. Downloading a released kernel + from kernel.org will give you the most stable kernel, but you'll need to + get user space code that matches the kernel version you've chosen. You can + get user space code that matches stable kernels on the Open-FCoE.org + "Downloads" page or you can pull directly from the git repositories using + tags. + +2) Configure the kernel + # make menuconfig + + Select the following: + + Networking Support -> Networking Options + Data Center Bridging + 802.1Q VLAN Support + GVRP (GARP VLAN Registration Protocol) support + + Networking Support -> Networking Options -> QoS and/or fair queuing + Multi Band Priority Queueing (PRIO) + Hardware Multiqueue-aware Multi Band Queuing (MULTIQ) + Elementary classification (BASIC) + Universal 32bit comparisons w/ hashing (U32) + Extended Matches + U32 key + Actions + SKB Editing + + Device Drivers -> SCSI Device Support -> SCSI Low-level drivers + LibFC + LibFCoE + FCoE + + Enable the block layer + Block layer SG support v4 + + Device Drivers -> Network Device Support -> Ethernet (10000 Mbit) + Intel(R) 10GbE PCI Express adapters support + Data Center Bridging (DCB) Support + + [ Replace with desired driver if not using Intel adapter. ] + + Exit Saving Changes + +3) Compile the kernel + # make && make modules_install && make install + + If you're going to build fcoe-utils, you can run 'make headers_install' + at this stage to skip a later step. + +4) Configure Grub + Change the default kernel so that this new kernel is used when booting + # reboot + + +## +# DCB +####### + +DEPENDENCIES + +* libnl +* libnl-devel +* libconfig +* libconfig-devel +* autoconf +* autotools +* sysconftool +* automake +* libtool +* gcc-c++ +* flex +* readline-devel + +PROCESS + +1) Obtain the latest source + + git clone git://open-lldp.org/open-lldp + + (note: fcoe-utils versions >= 1.0.20 require an lldpad version >= 0.9.43) + +2) Obtain libconfig version 1.3.2 or greater included in distribution + devel packages source can be obtained directly from + http://www.hyperrealm.com/libconfig/ + +3) Build and install libconfig -- see libconfig documentation + +4) Obtain and install devel netlink library (libnl) version 1.1 or greater + included in distribution devel packages source can be obtained directly + from http://www.infradead.org/~tgr/libnl/ + +5) Bootstrap, configure, make and make install + # cd open-lldp + # ./bootstrap.sh + # rpm --eval "%configure" | sh + # make + # make install + + +## +# HBA API Wrapper Library +########################## + +DEPENDENCIES + +* autoconf +* autotools +* sysconftool +* automake +* libtool + +PROCESS + +1) Obtain the latest source + + git clone git://open-fcoe.org/libHBAAPI.git + +2) Bootstrap, configure, make and make install + # cd libHBAAPI + # ./bootstrap.sh + # rpm --eval "%configure" | sh + # make + # make install + + +## +# libhbalinux +############## + +DEPENDENCIES + +* HBA API Wrapper + +* autoconf +* autotools +* sysconftool +* automake +* libtool +* libpciaccess-devel + +PROCESS + +1) Obtain the latest source + + git clone git://open-fcoe.org/libhbalinux.git + +2) Bootstrap, configure, make and make install + # cd libhbalinux + # ./bootstrap.sh + # rpm --eval "%configure" | sh + # make + # make install + + +## +# fcoe-utils +############ + +DEPENDENCIES + +* libnl (see DCB section) +* libhbalinux +* lldpad v0.9.26 or greater (optional configure flag to build without) + +* autoconf +* autotools +* sysconftool +* automake +* libtool + +PROCESS + +1) Obtain the latest source + + git clone git://open-fcoe.org/fcoe-utils.git + +2) Bootstrap, configure, make and make install + # cd fcoe-utils + # ./bootstrap.sh + Either execute the next line for System V init script install + # rpm --eval "%configure" | sh + or the following line to install systemd unit scripts + # rpm --eval "%configure --with-systemdsystemunitdir=/" | sh + # make + # make install + +## +# Example Configuration +####################### + +This example configures interface eth3 to automatically connect to storage over +a discovered VLAN. + +1) Configure FCoE on the interface + # cd /etc/fcoe/ + # cp cfg-ethx cfg-eth3 + +2) Start lldpad and configure the interface for DCB. + # service lldpad start + # dcbtool sc eth3 dcb on + # dcbtool sc eth3 pfc e:1 + # dcbtool sc eth3 app:fcoe e:1 + + As a convenience there is a script that will confirm if DCB has been + configured correctly for FCoE. The script is run as follows, + + /debug/dcbcheck.sh eth3 + (note: this is on the root device, not the VLAN) + + Follow the suggestions and repeatedly run the script until it states that + DCB is configured correctly. + +3) Start fcoe + # service fcoe start + After a few moments your storage should appear (assuming everything is + configured correctly on the fabric) + +4) Setup lldpad and fcoe to start when booting + # chkconfig lldpad on + # chkconfig fcoe on + + +## +# Reporting Errors +################## + +Most steps cannot be skipped. Resolve issues before moving forward. + +Please run '/debug/fcoedump.sh eth3.-fcoe &>dump.out' +and provide the dump.out file with the bug report. Defects can be filed +against the distribution used, reported to the Ethernet vendor of the +card in use, or mailed to fcoe-devel@open-fcoe.org. + + +## +# Debugging Tools +################### + +1) Discover VLANs manually + # fipvlan -a + Fibre Channel Forwarders Discovered + interface | VLAN | FCF MAC + ------------------------------------ + eth3 | 101 | 00:0d:ec:a3:3a:80 + + VLANs should be automatically discovered by the FCoE start up process which + adds a VLAN to the real device. The VLAN can be identified by the name + ethx.vid-fcoe. + +2) fcping can be used to ping targets to verify connectivity. + + # fcping -h eth3.-fcoe -F E10AEF -c 3 + sending echo to 0xE10AEF + echo 1 REJECT received 0.116 ms + echo 2 REJECT received 0.222 ms + echo 3 REJECT received 0.226 ms + 3 frames sent, 3 received 3 errors, 0.000% loss, avg. rt time 0.188 ms diff --git a/README b/README new file mode 100644 index 0000000..5f4f4ae --- /dev/null +++ b/README @@ -0,0 +1,74 @@ + FCoE Management Tools Installation Guide + ======================================== + +The FCoE Management Tools included in this package are + + fcoeadm - program to create, reset, destroy, and display FCoE interfaces + fcoemon - program to monitor the events from the DCB daemon + +Requirements: + + The HBAAPI library and the HBAAPI vendor library source must be built +and installed before you can build the management tools. Both the HBAAPI library, +libHBAAPI, and the HBAAPI vendor library, libhbalinux, may be downloaded from +www.Open-FCoE.org. The instructions in the package describe how to download and +build the libraries. + +Best Practices : + +Any newly discovered disk can be mounted using udev rules, autofs and possibly by +other methods as needed. However, if a fcoe disk is needed by any other system service +that follows the fcoe service then the disk will need to be mounted at some point +after the fcoe service, but before the service that requires the disk. That mounting +could possibly be added to the fcoe service script itself, or to a new service script +that would run after fcoe, but before the script that requires the fcoe disk. +The following is an example of how to mount a fcoe disk formatted with an ext3 file +system using /etc/fstab. + + mount_fcoe_disks_from_fstab() + { + local timeout=20 + local done=1 + local fcoe_disks=($(egrep 'by-path\/fc-.*_netdev' /etc/fstab | cut -d ' ' -f1)) + + test -z $fcoe_disks && return 0 + + echo -n "Waiting for fcoe disks . " + while [ $timeout -gt 0 ]; do + for disk in ${fcoe_disks[*]}; do + if ! test -b $disk; then + done=0 + break + fi + done + + test $done -eq 1 && break; + sleep 1 + echo -n ". " + done=1 + let timeout-- + done + + if test $timeout -eq 0; then + echo "timeout!" + else + echo "done!" + fi + + # mount any newly discovered disk + mount -a 2>/dev/null + } + + The mount_fcoe_disks_from_fstab function should be invoked after the +fcoemon daemon is started by the fcoe service script to mount any fcoe disk +specified by path in /etc/fstab as:- + + /dev/disk/by-path/fc-0xXX:0xXX /mnt/fcoe-disk1 ext3 defaults,_netdev 0 0 + /dev/disk/by-path/fc-0xYY:0xYY /mnt/fcoe-disk2 ext3 defaults,_netdev 0 0 + + The /dev/disk/by-path entries with "fc-" and "_netdev" in their names +enables mount_fcoe_disks_from_fstab to identify the fcoe disks so that it can +wait for the fcoe disks to be discovered. If they are not added to the system +within the timeout period the function will exit, the timeout is set to 20 +seconds but can be changed to suit the environment. See man fstab(5) for for +more details on mounting filesystems. diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..361d5c5 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,2 @@ +#! /bin/sh +exec autoreconf --install -s diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..95950d9 --- /dev/null +++ b/build.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# This script parses the package name and version out of configure.ac, +# uses them to assemble the names of the distribution tar.gz and source rpm, +# then calls make to generate those files. +# Any arguments will be passed on to configure when building the tarball. + +AC_INIT=`grep AC_INIT configure.ac` +PACKAGE=`echo ${AC_INIT} | awk 'BEGIN { FS="[][]" }; { print $2 };'` +VERSION=`echo ${AC_INIT} | awk 'BEGIN { FS="[][]" }; { print $4 };'` + +tgz=${PACKAGE}-${VERSION}.tar.gz +srpm=${PACKAGE}-${VERSION}-1.src.rpm + +rm -f ${tgz} ${srpm} + +make -f - <= 0.9.46]) +AC_SUBST([LLDPAD_CFLAGS]) + +PKG_CHECK_MODULES([PCIACCESS], [pciaccess]) +AC_SUBST([PCIACCESS_LIBS]) + +PKG_PROG_PKG_CONFIG +AC_ARG_WITH([systemdsystemunitdir], + AS_HELP_STRING([--with-systemdsystemunitdir=DIR], + [Directory for systemd service files]), [], + [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) +AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) +AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir"]) + +AC_CONFIG_FILES([Makefile fcoe-utils.spec include/fcoe_utils_version.h]) +AC_OUTPUT + diff --git a/contrib/fcc.sh b/contrib/fcc.sh new file mode 100755 index 0000000..72a9bb9 --- /dev/null +++ b/contrib/fcc.sh @@ -0,0 +1,538 @@ +#! /bin/bash +# +# Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. +# +# This program is free software; you may redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# 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. +# +# Author: Joe Eykholt (jeykholt at cisco dot com) +# +# Please send comments and changes to jeykholt at cisco dot com + +VERSION="fcc v1.0.13 11/10/2010" + +fcoe_dir=/sys/module/fcoe +fcoe_lib_dir=/sys/module/libfcoe +fdir=/sys/class/fc_host +sdir=/sys/class/scsi_host +cmdname=`basename $0` + +usage() { +cat < [ ...]]' + +cmd: + create Start FCoE on an Ethernet interface. + create-vn Start FCoE (VN_port-VN_port) on an Ethernet interface. + delete / del Delete an FCoE instance + destroy Same as delete + enable / en Same as create + help Show this usage message + info Show HBA detailed info + list List the HBAs with remote port and LUN status + luns Show LUN list and status + scan Scan the HBA + stats Show HBA statistics + reload Reload fcoe and fnic modules + reset Reset the HBA + version Show version +USAGE +} + +verify_hba() { + local x=$1 + + if [ ! -d $fdir/$x ] + then + echo "$cmdname: invalid HBA name $x" >&2 + exit + fi +} + +hba_stats() { + local x=$1 + local y + + verify_hba $x + + printf "\n$x Statistics:\n" + ( + cd $fdir/$x/statistics + for y in * + do + # + # avoid the write-only files. + # test -r doesn't help if we're root + # + if [ "$y" == "reset_statistics" ] + then + continue + fi + val=`cat $y` + if [ "$val" != 0xffffffffffffffff ] + then + printf "%-30s%8lld\n" $y: $val + fi + done + ) +} + + +# +# format a /sys file containing a hex WWN or FC_ID +# from 0x123456\n to 12:34:56 or from 0xffffffff to - (unknown WWN or FC_ID) +# +fmt_hex() { + sed -e 's/^0xff*$/-/' -e 's/0x//' -e 's/../&:/g' -e 's/:$//' < $1 +} + +rport_list() { + local x + local hba=$1 + + rdir=/sys/class/fc_remote_ports + host=`echo $hba | sed -e 's/host//'` + rports=`ls -d $rdir/rport-$host:* 2>/dev/null` + if [ -z "$rports" ] + then + return + fi + + printf "\n$hba Remote Ports:\n" + + + fmt="%-8s %-23s %-8s %-8s %-15s\n" + + printf "$fmt" Path "Port Name" "Port ID" State Roles + for x in $rports + do + btl="`echo $x | sed -e 's/.*-\(.*-.*\)/\1/'`" + printf "$fmt" "$btl" \ + "`fmt_hex $x/port_name`" \ + "`fmt_hex $x/port_id`" \ + "`cat $x/port_state`" \ + "`cat $x/roles`" + done +} + +# +# Translate capacity to readable units. +# compute sizes in base-10 marketing units +# +lun_cap() { + local sizek=0 sizem=0 sizeg=0 sizet=0 + local size + local onek=1000 + local cap + + size=$1 + let sizek="$size * 512 / $onek" + let sizem="$sizek / $onek" + let sizeg="$sizem / $onek" + let sizet="$sizeg / $onek" + if [ "$sizet" -gt 0 ] + then + cap="$sizet TB" + elif [ "$sizeg" -gt 0 ] + then + cap="$sizeg GB" + elif [ "$sizem" -gt 0 ] + then + cap="$sizem MB" + else + cap="$sizek KB" + fi + echo $cap +} + +lun_list() { + local x + local lun + local hba=$1 + local ddir=/sys/class/scsi_device + + host=`echo $hba | sed -e 's/host//'` + + local luns=`(cd $ddir && + ls -d $host:* | sort -n -t: -k1 -k2 -k3 -k4) 2>/dev/null` + + if [ -z "$luns" ] + then + return + fi + + printf "\n$hba LUNs:\n" + + fmt="%-10s %-8s %6s %-15s %-20s %-8s\n" + + printf "$fmt" Path Device Size Vendor Model State + + for lun in $luns + do + ( + local size=0 + cap=- + + cd $ddir/$lun/device + if [ -d block ] + then + dev=`ls block | tail -1` + size=`cat block/$dev/size` + cap=`lun_cap $size` + elif [ -d char ] + then + dev=`ls char | tail -1` + cap=- + elif [ -d scsi_tape ] + then + dev=`ls scsi_tape | egrep '^st[0-9]*$'` + cap=- + elif [ -d scsi_generic ] + then + dev=`ls scsi_generic | tail -1` + cap=- + else + dev="`ls -d block:* char:* 2>/dev/null | + sed -e 's/.*\://'`" + if [ -L block:$dev -o -d block:$dev ] + then + size=`cat block:$dev/size` + cap=`lun_cap $size` + fi + fi + + printf "$fmt" "$lun" "$dev" "$cap"\ + "`cat vendor`" \ + "`cat model`" \ + "`cat state`" + ) + done +} + +sym_name() { + local hba=$1 + local file + + file=$fdir/$hba/symbolic_name + if [ -f "$file" ] + then + sed -e 's/.*over //' < $file + else + exit 1 + fi +} + +# +# Lookup the host name for a given symbolic name +# +hba_name() { + local sym=$1 + local hba + + if [ -d "$fdir/$sym" ] + then + echo $sym + exit + fi + + for hba in $all_hbas + do + if [ "`sym_name $hba`" = "$sym" ] + then + echo $hba + exit + fi + done + exit 1 +} + +hba_state() { + local x + + echo "FC HBAs:" + fmt="%-8s %-23s %-8s %-8s %-15s\n" + printf "$fmt" HBA "Port Name" "Port ID" State Device + for x in $hbas + do + ( + cd $fdir/$x + printf "$fmt" "$x" \ + "`fmt_hex $fdir/$x/port_name`" \ + "`fmt_hex $fdir/$x/port_id`" \ + "`cat $fdir/$x/port_state`" \ + "`sym_name $x`" + ) 2>/dev/null + done +} + +hba_info() { + local x=`hba_name $1` + local fmt="\t%-20s %s\n" + + verify_hba $x + printf "\n$x Info:\n" + ( + cd $fdir/$x + + printf "$fmt" "Symbolic Name" "`cat symbolic_name`" + printf "$fmt" "Port Name" "`fmt_hex port_name`" + printf "$fmt" "Node Name" "`fmt_hex node_name`" + printf "$fmt" "Port Type" "`cat port_type`" + echo + printf "$fmt" "Port State" "`cat port_state`" + printf "$fmt" "Port ID" "`fmt_hex port_id`" + printf "$fmt" "Fabric Name" "`fmt_hex fabric_name`" + echo + printf "$fmt" "Max Frame Size" "`cat maxframe_size`" + printf "$fmt" "Speed" "`cat speed`" + echo + ) +} + +scsi_state() { + local x + local dev + + printf "\nSCSI States:\n" + fmt="%-8s %-10s %-15s %-10s %8s\n" + printf "$fmt" HBA Device Mode State Busy + for x in $scsi_hbas + do + ( + cd $fdir/$x + dev="`cat $sdir/$x/proc_name``cat $sdir/$x/unique_id`" + printf "$fmt" "$x" "$dev" \ + "`cat $sdir/$x/active_mode`" \ + "`cat $sdir/$x/state`" \ + "`cat $sdir/$x/host_busy`" + ) 2>/dev/null + done +} + +hba_list() { + local x + + hba_state + # scsi_state + + for x in $hbas + do + rport_list $x + lun_list $x + done +} + +# +# Do a command for a list of arguments +# +repeat() { + local cmd=$1 + local x + shift + + for x + do + $cmd $x + done +} + +fcoe_ctl() { + local cmd=$1 + local hba=$2 + local file=$fcoe_lib_dir/parameters/$cmd + + if [ -w "$file" ] + then + echo $hba > $file + elif [ -f "$file" ] + then + echo "$cmdname: no permission to $cmd $hba" >&2 + else + echo "$cmdname: file $file doesn't exist. " \ + "Check for fcoe module." >&2 + fi +} + +fc_host_ctl() { + local hba=$2 + local host=$2 + local cmd=$1 + local value + local file + local dir + + dir=$fdir/$host + if [ ! -d "$dir" ] + then + host=`hba_name $hba` + dir=$fdir/$host + if [ $? != 0 ] + then + echo "$cmdname: hba $hba not found" >&2 + exit 1 + fi + fi + + case "$cmd" in + reset) + file=$dir/issue_lip + value=1 + ;; + scan) + file=$dir/device/scsi_host/$host/scan + value="- - -" + esac + + if [ -w "$file" ] + then + echo $value > $file + elif [ -f "$file" ] + then + echo "$cmdname: no permission to $cmd $hba" 1>&2 + exit 2 + else + echo "$cmdname: $cmd not supported for $hba" 1>&2 + exit 2 + fi +} + +load_mod() +{ + if [ ! -d $fcoe_dir ] + then + modprobe fcoe + echo "$cmdname: loading fcoe module" >&2 + sleep 1 + if [ ! -d $fcoe_dir ] + then + echo "$cmdname: $fcoe_dir not found" >&2 + exit 2 + fi + fi +} + +# +# Start of main script code. +# +scsi_hbas= +if [ -d "$sdir" ] +then + scsi_hbas=`ls $sdir 2>/dev/null` +fi +if [ -d "$fdir" ] +then + # sort the list of HBAs by the number after the 't' in "host9" + all_hbas=`ls $fdir 2>/dev/null | sort -n -tt -k2` +fi +hbas="$all_hbas" + +if [ $# -lt 1 ] +then + hba_list + exit 0 +fi + +if [ $# -lt 2 ] +then + cmd=$1 + hba_spec=no +else + cmd=$1 + hba=$2 + shift + if [ $# -eq 0 ] + then + hba_spec=n + elif [ $# -eq 1 -a "$1" = all ] + then + hba_spec=y + else + hba_spec=y + hbas="$@" + scsi_hbas="$@" + fi +fi + +hba_required() +{ + if [ "$hba_spec" != y ] + then + echo "$cmdname: $cmd requires HBA name" >&2 + exit 2 + fi +} + +case "$cmd" in + create | enable | en) + hba_required + load_mod + repeat "fcoe_ctl create" $hbas + ;; + create-vn) + hba_required + load_mod + repeat "fcoe_ctl create_vn2vn" $hbas + ;; + delete | del | destroy) + if [ ! -d $fcoe_lib_dir ] + then + echo "$cmdname: $fcoe_lib_dir not found" >&2 + exit 2 + fi + hba_required + repeat "fcoe_ctl destroy" $hbas + ;; + info) + repeat hba_info $hbas + ;; + list) + hba_list + ;; + lun*) + repeat lun_list $hbas + ;; + names) + repeat echo $hbas + ;; + stat*) + repeat hba_stats $hbas + ;; + realname) + hba_name $hba + ;; + reload) + mods= + for mod in fcoe fnic libfcoe libfc + do + if [ -d /sys/module/$mod ] + then + mods="$mods $mod" + fi + done + rmmod $mods + for mod in $mods + do + modprobe $mod + done + ;; + reset | scan) + hba_required + repeat "fc_host_ctl $cmd" $hbas + ;; + version) + echo $VERSION + ;; + *) + usage + ;; +esac diff --git a/contrib/fcoe-setup.sh b/contrib/fcoe-setup.sh new file mode 100755 index 0000000..2142c42 --- /dev/null +++ b/contrib/fcoe-setup.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# +# fcoe-setup.sh +# +# Create VLAN interface for FCoE +# + +scan_vlan() { + local ifname=$1 + local vlan=$2 + + cat /proc/net/vlan/config | tail +3 | while read vif s1 vid s2 if ; do + if [ "$if" = "$ifname" ] && [ "$vid" == "$vlan" ] ; then + echo "$vif" + fi + done +} + +create_vlan () { + local ifname=$1 + local vlan=$2 + local vif + + vif=$(scan_vlan $ifname $vlan) + + if [ -z "$vif" ] ; then + vif="$ifname.$vlan" + ip link add dev $vif link $ifname type vlan id $vlan + fi + ip link set $vif up + echo "$vif" +} + +check_ifcfg () { + local vif=$1 + local ifname=$2 + local vid=$3 + local ifcfg=/etc/sysconfig/network/ifcfg-$vif + + if [ -f "$ifcfg" ] ; then + echo "Interface is configured properly" + else + echo "Creating ifcfg configuration ifcfg-$vif" + cat > $ifcfg < $fcoecfg <&2 + echo "Please re-compile the kernel with CONFIG_DCB=y." >&2 + exit 1 +fi + +# Ensure that a value was passed in for the interface name +if [ "${IFNAME}" == "" ] ; then + echo "Please provide the interface name to check." >&2 + exit 1 +fi + +# Ensure that the interface name provided is valid +if ifconfig ${IFNAME} 2>&1 | grep -q "Device not found" ; then + echo "Please provide a valid interface name." >&2 + exit 1 +fi + +# Determine if we can communicate with DCBD +if dcbtool gc ${PHYSDEV} dcb | grep Status | grep -q Failed ; then + echo "Unable to communicate with the DCB daemon (dcbd) or DCB capable driver." >&2 + exit 1 +fi + +# Determine if DCB is on +if dcbtool gc ${PHYSDEV} dcb | grep 'DCB State' | grep -q off ; then + echo "DCB is not on, execute the following command to turn it on" >&2 + echo "dcbtool sc ${PHYSDEV} dcb on" >&2 + ret=1 +fi + +# Determine if PFC is enabled +if dcbtool gc ${PHYSDEV} pfc | grep Enable | grep -q false ; then + echo "PFC is not enabled, execute the following command to turn it on" >&2 + echo "dcbtool sc ${PHYSDEV} pfc e:1" >&2 + ret=1 +fi + +# Determine if the FCoE APP TLV is enabled +if dcbtool gc ${PHYSDEV} app:fcoe | grep Enable | grep -q false ; then + echo "The FCoE APP TLV is not enabled, execute the following command to turn it on" >&2 + echo "dcbtool sc ${PHYSDEV} app:fcoe e:1" >&2 + ret=1 +fi + +if [ ${ret} -eq 0 ] ; then + echo "DCB is correctly configured for FCoE" +fi + +exit ${ret} diff --git a/debug/fcoedump.sh b/debug/fcoedump.sh new file mode 100755 index 0000000..950718a --- /dev/null +++ b/debug/fcoedump.sh @@ -0,0 +1,250 @@ +#!/bin/bash +############################################################################### +# +# fcoe_debug: print debugging information for fcoe +# $1: interface +############################################################################### + +if [ -z $1 ]; then + echo "Usage: `basename $0` eth[0-9]" + exit 1; +fi + +DEVICE=$1 + +if [ -r /proc/net/vlan/$DEVICE ] ; then + PHYSDEV=$(grep '^Device:' /proc/net/vlan/$DEVICE | awk '{print $2}') +else + PHYSDEV=$DEVICE +fi + +kernel_info() +{ + echo -e "\n###KERNEL INFO###" + uname -a + + if [ -f "/proc/config.gz" ] + then + echo -e "\nzcat /proc/config.gz" + zcat /proc/config.gz + else + echo -e "\n/proc/config.gz does not exist." + fi +} + +system_info() +{ + echo -e "\n###System Info###" + + echo -e "#date" + date + + echo -e "#ps axf" + ps axf + + echo -e "#lsscsi" + lsscsi + + echo -e "#lspci" + lspci + + echo -e "#grep dcbnl_init /proc/kallsyms" + grep dcbnl_init /proc/kallsyms + + echo -e "#grep scsi_transport_fc /proc/kallsyms" + grep scsi_transport_fc /proc/kallsyms + + echo -e "#grep libfc /proc/kallsyms" + grep libfc /proc/kallsyms | grep -v libfcoe + + echo -e "#grep libfcoe /proc/kallsyms" + grep libfcoe /proc/kallsyms + + echo -e "#grep fcoe /proc/kallsyms" + grep fcoe /proc/kallsyms | grep -v libfcoe + + echo -e "#lsmod" + lsmod +} + +adapter_info() +{ + if [ $DEVICE != $PHYSDEV ] + then + echo -e "\n###Adapter INFO VLAN $DEVICE" + echo -e "#ethtool:" + ethtool $DEVICE + echo -e "#ethtool interface:" + ethtool -i $DEVICE + echo -e "#ethtool offloads:" + ethtool -k $DEVICE + echo -e "#ifconfig:" + ifconfig $DEVICE + fi + + echo -e "\n###Adapter INFO $PHYSDEV" + echo -e "#ethtool:" + ethtool $PHYSDEV + echo -e "#ethtool interface:" + ethtool -i $PHYSDEV + echo -e "#ethtool pause:" + ethtool -a $PHYSDEV + echo -e "#ethtool offloads:" + ethtool -k $PHYSDEV + echo -e "#ethtool stats:" + ethtool -S $PHYSDEV + echo -e "#ifconfig:" + ifconfig $PHYSDEV +} + +dcbtool_info() +{ + echo -e "\n#### Showing dcbtool info for $PHYSDEV" + dcbtool -v + dcbtool gc $PHYSDEV dcb + echo -e "\n########## Getting dcb config for $PHYSDEV" + dcbtool gc $PHYSDEV pg + echo + dcbtool gc $PHYSDEV pfc + echo + dcbtool gc $PHYSDEV app:0 + echo + dcbtool gc $PHYSDEV ll:0 + echo -e "\n########## Getting dcb oper for $PHYSDEV" + dcbtool go $PHYSDEV pg + echo + dcbtool go $PHYSDEV pfc + echo + dcbtool go $PHYSDEV app:0 + echo + dcbtool go $PHYSDEV ll:0 + echo -e "\n########## Getting dcb peer for $PHYSDEV" + dcbtool gp $PHYSDEV pg + echo + dcbtool gp $PHYSDEV pfc + echo + dcbtool gp $PHYSDEV app:0 + echo + dcbtool gp $PHYSDEV ll:0 +} + +lldptool_info() +{ + echo -e "\n#### Showing lldptool info for $PHYSDEV" + lldptool -v + + echo -e "\n########## Showing last received TLV for $PHYSDEV" + lldptool -t -i $PHYSDEV -n + + echo -e "\n########## Showing last sent TLV for $PHYSDEV" + lldptool -t -i $PHYSDEV + + echo -e "\n########## Showing configured APP TLV for $PHYSDEV" + lldptool -t -i $PHYSDEV -V APP -c + + echo -e "\n########## Showing configured PFC TLV for $PHYSDEV" + lldptool -t -i $PHYSDEV -V PFC -c + + echo -e "\n########## Showing configured ETS-CFG TLV for $PHYSDEV" + lldptool -t -i $PHYSDEV -V ETS-CFG -c + + echo -e "\n########## Showing configured ETS-REC TLV for $PHYSDEV" + lldptool -t -i $PHYSDEV -V ETS-REC -c +} + +dcb_info() +{ + echo -e "\n###DCB INFO" + echo -e "#tc config" + tc qdisc + tc filter show dev $PHYSDEV | grep -v filter + + ## Intentionally allow both services status to show + ## even though they should be mutually exclusive. If + ## you see both in a dump you know there's a problem. + [ -f /etc/init.d/boot.lldpad ] && + echo -e "#service boot.lldpad status:" && + service boot.lldpad status + [ -f /etc/init.d/lldpad ] && + echo -e "#service lldpad status:" && + service lldpad status + + which dcbtool 2>&1 > /dev/null + [ $? -eq 0 ] && dcbtool_info + + which lldptool 2>&1 > /dev/null + [ $? -eq 0 ] && lldptool_info +} + +fcoe_info() +{ + echo -e "\n###FCOE Info" + + ## Intentionally allow both services status to show + ## even though they should be mutually exclusive. If + ## you see both in a dump you know there's a problem. + [ -f /etc/init.d/boot.fcoe ] && + echo -e "#service boot.fcoe status" && + service boot.fcoe status + [ -f /etc/init.d/fcoe ] && + echo -e "#service fcoe status" && + service fcoe status + + echo -e "#fcoeadm output " + fcoeadm -v + echo -e "#fcoeadm -i " + fcoeadm -i + echo -e "#fcoeadm -t " + fcoeadm -t + + # Trigger fcoemon to dump its internal structures + # to the log file that we will capture later in + # this script. + kill -10 `pidof fcoemon` +} + +bsg_info() +{ + echo -e "\n###BSG Info" + echo -e "#find /dev/bsg/" + find /dev/bsg/ 2>&1 +} + +sysfs_dump() +{ + echo -e "\n###SYSFS dump" + echo -e "#sysfs fc_host dump" + find /sys/class/fc_host/host*/ -type f -print -exec cat '{}' \; + echo -e "#sysfs fc_transport dump" + find /sys/class/fc_transport/target*/ -type f -print -exec cat '{}' \; + echo -e "#sysfs fc_remote_ports dump" + find /sys/class/fc_remote_ports/*/ -type f -print -exec cat '{}' \; + echo -e "#sysfs fc_vport dump" + find /sys/class/fc_vports/*/ -type f -print -exec cat '{}' \; +} + +logfile_dump() +{ + echo "###LOGFILES" + echo "#/var/log/messages" + cat /var/log/messages + echo + echo "#dmesg" + dmesg +} + +fcoe_debug() +{ + kernel_info + system_info + adapter_info + dcb_info + fcoe_info + bsg_info + sysfs_dump + logfile_dump +} + +fcoe_debug + + diff --git a/doc/fcnsq.8 b/doc/fcnsq.8 new file mode 100644 index 0000000..7ba6d7e --- /dev/null +++ b/doc/fcnsq.8 @@ -0,0 +1,128 @@ +'\" t +.\" Title: fcnsq +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.75.2 +.\" Date: 06/09/2010 +.\" Manual: Open-FCoE Tools +.\" Source: Open-FCoE +.\" Language: English +.\" +.TH "FCNSQ" "8" "06/09/2010" "Open\-FCoE" "Open\-FCoE Tools" +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +fcnsq \- Fibre Channel Name Server Query Tool +.SH "SYNOPSIS" +.sp +\fBfcnsq\fR \fIhost\fR \-\-gpn \fIport_id\fR [\-q | \-\-quiet] +.sp +\fBfcnsq\fR \fIhost\fR \-\-gnn \fIport_id\fR [\-q | \-\-quiet] +.sp +\fBfcnsq\fR \fIhost\fR \-\-gspn \fIport_id\fR [\-q | \-\-quiet] +.sp +\fBfcnsq\fR \fIhost\fR \-\-gsnn \fIwwpn\fR [\-q | \-\-quiet] +.sp +\fBfcnsq\fR ? +.SH "DESCRIPTION" +.sp +The \fBfcnsq\fR command performs Fibre Channel Name Server queries for a user\-specified FC Host\&. The supported name server query commands include: Get Port Name (GPN), Get Node Name (GNN), Get Symbolic Node Name (GSNN), and Get Symbolic Port Name (GSPN) as described in FC\-GS\-3 for the Common Transport (CT) services\&. +.sp +\fBfcnsq\fR takes the host bsg name as the input for the target host\&. Host bsg name must be a valid bsg device, e\&.g\&., "host12" as derived from /dev/bsg/fc_host12\&. For each name server query command, the argument is either a Port ID or a World Wide Port Name\&. Port IDs and World Wide Names must be specified in hexadecimal\&. To disable verbose output, use the \fB\-\-quiet\fR option\&. +.SH "OPTIONS" +.PP +\fBhost\fR \fB\-\-gpn\fR \fIport_id\fR +.RS 4 +Get Port Name by Port ID for +\fIhost\fR\&. +.RE +.PP +\fBhost\fR \fB\-\-gnn\fR \fIport_id\fR +.RS 4 +Get Node Name by Port ID for +\fIhost\fR\&. +.RE +.PP +\fBhost\fR \fB\-\-gspn\fR \fIport_id\fR +.RS 4 +Get Symbolic Port Name by Port ID for +\fIhost\fR\&. +.RE +.PP +\fBhost\fR \fB\-\-gsnn\fR \fIwwpn\fR +.RS 4 +Get Symbolic Node Name by World Wide Port Name for +\fIhost\fR\&. +.RE +.PP +\fB\-q\fR, \fB\-\-quiet\fR +.RS 4 +Disable verbose output\&. +.RE +.PP +\fB?\fR +.RS 4 +Display a help message with basic usage instructions\&. +.RE +.SH "EXAMPLES" +.sp +Query Get Port Name (GPN) from host12 for Port ID 0xbf0027 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcnsq host12 \-\-gpn bf0027 +.fi +.if n \{\ +.RE +.\} +.sp +Query Get Node Name (GNN) from host12 for Port ID 0xbf0027 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcnsq host12 \-\-gnn bf0027 +.fi +.if n \{\ +.RE +.\} +.sp +Query Get Symbolic Port Name (GSPN) from host12 for Port ID 0xbf0027 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcnsq host12 \-\-gspn bf0027 +.fi +.if n \{\ +.RE +.\} +.sp +Query Get Symbolic Node Name (GSNN) from host12 for WWWPN 0x1000001B213C9DDA +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcnsq host12 \-\-gsnn 0x1000001B213C9DDA +.fi +.if n \{\ +.RE +.\} +.SH "SEE ALSO" +.sp +\fBfcoeadm\fR(8) +.SH "SUPPORT" +.sp +\fBfcnsq\fR is part of the \fIfcoe\-utils\fR package, maintained through the \fIOpen\-FCoE\fR project\&. Resources for both developers and users can be found at the \fIOpen\-FCoE\fR website http://open\-fcoe\&.org/\&. diff --git a/doc/fcnsq.txt b/doc/fcnsq.txt new file mode 100644 index 0000000..3d84c8b --- /dev/null +++ b/doc/fcnsq.txt @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////// +// vim:syntax=asciidoc:tw=75: +// +// This is an asciidoc text file, which will be converted into a UNIX man +// page using asciidoc and the DocBook XSL stylesheets. +// +// If you are going to update this documentation, please modify this file +// and then regenerate the nroff formated man page using the Makefile. +/////////////////////////////////////////////////////////////////////////// + +fcnsq(8) +========== +:man source: Open-FCoE +:man manual: Open-FCoE Tools + +NAME +---- +fcnsq - Fibre Channel Name Server Query Tool + +SYNOPSIS +-------- +*fcnsq* _host_ --gpn _port_id_ [-q | --quiet] + +*fcnsq* _host_ --gnn _port_id_ [-q | --quiet] + +*fcnsq* _host_ --gspn _port_id_ [-q | --quiet] + +*fcnsq* _host_ --gsnn _wwpn_ [-q | --quiet] + +*fcnsq* ? + +DESCRIPTION +----------- +The *fcnsq* command performs Fibre Channel Name Server queries +for a user-specified FC Host. The supported name server query commands +include: Get Port Name (GPN), Get Node Name (GNN), Get Symbolic Node Name +(GSNN), and Get Symbolic Port Name (GSPN) as described in FC-GS-3 for the +Common Transport (CT) services. + +*fcnsq* takes the host bsg name as the input for the target host. Host +bsg name must be a valid bsg device, e.g., "host12" as derived from +/dev/bsg/fc_host12. For each name server query command, the argument is +either a Port ID or a World Wide Port Name. Port IDs and World Wide Names +must be specified in hexadecimal. To disable verbose output, use the *--quiet* +option. + +OPTIONS +------- +*host* *--gpn* _port_id_:: + Get Port Name by Port ID for _host_. + +*host* *--gnn* _port_id_:: + Get Node Name by Port ID for _host_. + +*host* *--gspn* _port_id_:: + Get Symbolic Port Name by Port ID for _host_. + +*host* *--gsnn* _wwpn_:: + Get Symbolic Node Name by World Wide Port Name for _host_. + +*-q*, *--quiet*:: + Disable verbose output. + +*?*:: + Display a help message with basic usage instructions. + +EXAMPLES +-------- +Query Get Port Name (GPN) from host12 for Port ID 0xbf0027 + + fcnsq host12 --gpn bf0027 + +Query Get Node Name (GNN) from host12 for Port ID 0xbf0027 + + fcnsq host12 --gnn bf0027 + +Query Get Symbolic Port Name (GSPN) from host12 for Port ID 0xbf0027 + + fcnsq host12 --gspn bf0027 + +Query Get Symbolic Node Name (GSNN) from host12 for WWWPN 0x1000001B213C9DDA + + fcnsq host12 --gsnn 0x1000001B213C9DDA + +SEE ALSO +-------- +*fcoeadm*(8) + +SUPPORT +------- +*fcnsq* is part of the _fcoe-utils_ package, maintained through the +_Open-FCoE_ project. Resources for both developers and users can be found +at the _Open-FCoE_ website . diff --git a/doc/fcoeadm.8 b/doc/fcoeadm.8 new file mode 100644 index 0000000..2fefd70 --- /dev/null +++ b/doc/fcoeadm.8 @@ -0,0 +1,297 @@ +'\" t +.\" Title: fcoeadm +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 10/14/2013 +.\" Manual: Open-FCoE Tools +.\" Source: Open-FCoE +.\" Language: English +.\" +.TH "FCOEADM" "8" "10/14/2013" "Open\-FCoE" "Open\-FCoE Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +fcoeadm \- The Open\-FCoE Administration Tool +.SH "SYNOPSIS" +.sp +\fBfcoeadm\fR [\-m|\-\-mode fabric|vn2vn] \-c|\-\-create \fIethX\fR +.sp +\fBfcoeadm\fR \-d|\-\-destroy \fIethX\fR +.sp +\fBfcoeadm\fR \-r|\-\-reset \fIethX\fR +.sp +\fBfcoeadm\fR \-S|\-\-Scan [\fIethX\fR] +.sp +\fBfcoeadm\fR \-i|\-\-interface [\fIethX\fR] +.sp +\fBfcoeadm\fR \-f|\-\-fcf [\fIethX\fR] +.sp +\fBfcoeadm\fR \-t|\-\-target [\fIethX\fR] +.sp +\fBfcoeadm\fR \-l|\-\-lun [\fIethX\fR] +.sp +\fBfcoeadm\fR \-s|\-\-stats \fIethX\fR [\fIinterval\fR] +.sp +\fBfcoeadm\fR \-b|\-\-lesb \fIethX\fR [\fIinterval\fR] +.sp +\fBfcoeadm\fR \-h|\-\-help +.sp +\fBfcoeadm\fR \-v|\-\-version +.SH "DESCRIPTION" +.sp +The \fBfcoeadm\fR utility is the Fibre Channel over Ethernet (FCoE) management tool for the \fIOpen\-FCoE\fR project\&. \fBfcoeadm\fR may be used to create, destroy, and reset an FCoE instance on a given network interface\&. For these operations \fBfcoeadm\fR sends a command to a running \fBfcoemon\fR process, via a socket interface\&. \fBfcoemon\fR will then perform the requested operation\&. +.sp +\fBfcoeadm\fR also provides operations to query information about FCoE instances, including interface information, target information, LUN information, and port statistics\&. For much of this information, \fBfcoeadm\fR relies on the \fIlibhbalinux\fR implementation of the \fIHBA API\fR\&. Only one operation may be given on a command\&. +.SH "OPTIONS" +.PP +\fB\-m\fR, \fB\-\-mode\fR fabric|vn2vn +.RS 4 +Used together with the create option to control the FCoE mode of operation\&. The mode defaults to fabric but this option allows the selection of vn2vn mode\&. +.RE +.SH "OPERATIONS" +.PP +\fB\-c\fR, \fB\-\-create\fR \fIethX\fR +.RS 4 +Creates an FCoE instance based on the specified network interface\&. Note that if there is not an fcoemon configuration file for the interface (/etc/fcoe/cfg\-ethX, see +\fBfcoemon\fR), then the created FCoE instance will not require DCB\&. +.RE +.PP +\fB\-d\fR, \fB\-\-destroy\fR \fIethX\fR +.RS 4 +Destroys the FCoE instance on the specified network interface\&. This will not destroy FCoE instances created by fipvlan\&. +.RE +.PP +\fB\-r\fR, \fB\-\-reset\fR \fIethX\fR +.RS 4 +Resets the FCoE instance on the specified network interface\&. This will not reset FCoE instances created by fipvlan\&. +.RE +.PP +\fB\-S\fR, \fB\-\-Scan\fR \fIethX\fR +.RS 4 +Rescan for new targets and LUNs on the provided instance\&. This command will not rescan any NPIV instances created on the same port\&. This will not rescan any FCoE instance created by fipvlan\&. +.RE +.PP +\fB\-i\fR, \fB\-\-interface\fR [\fIethX\fR] +.RS 4 +Show information about the FCoE instance on the specified network interface, or all FCoE instances if no network interface is specified\&. +.RE +.PP +\fB\-f\fR, \fB\-\-fcf\fR [\fIethX\fR] +.RS 4 +Show information about the discovered Fibre Channel Forwarders (FCFs) on the specified network interface, or all discovered FCFs if no network interface is specified\&. +.RE +.PP +\fB\-t\fR, \fB\-\-target\fR [\fIethX\fR] +.RS 4 +Show information about the discovered targets associated with the FCoE instance on the specified network interface\&. If no network interface is specified, information about discovered targets from all FCoE instances will be shown\&. +.RE +.PP +\fB\-l\fR, \fB\-\-lun\fR [\fIethX\fR] +.RS 4 +Show detailed information about the discovered SCSI LUNs associated with the FCoE instance on the specified network interface\&. If no network interface is specified, information about SCSI LUNs from all FCoE instances will be shown\&. +.RE +.PP +\fB\-s\fR, \fB\-\-stats\fR \fIethX\fR [\fIinterval\fR] +.RS 4 +Show the statistics (including FC4 statistics) of the FCoE connection on the specified network interface\&. The information will be displayed repeatedly until the user cancels the command\&. The statistics will be printed every specified +\fIinterval\fR +(in seconds)\&. +\fIinterval\fR +should be specified as a whole number greater than 0\&. If +\fIinterval\fR +is not specified, a default interval of one second is used\&. +.RE +.PP +\fB\-b\fR, \fB\-\-lesb\fR \fIethX\fR [\fIinterval\fR] +.RS 4 +Show the Link Error Statistics Block (LESB) of the FCoE connection on the specified network interface\&. The information will be displayed repeatedly until the user cancels the command\&. The LESB statistics will be printed every specified +\fIinterval\fR +(in seconds)\&. +\fIinterval\fR +should be specified in whole integers greater than 0\&. If the +\fIinterval\fR +is not specified, a default interval of one second is used\&. +.RE +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Displays the usage message of the +\fBfcoeadm\fR +command\&. +.RE +.PP +\fB\-v\fR, \fB\-\-version\fR +.RS 4 +Displays the version of the +\fBfcoeadm\fR +command\&. +.RE +.SH "INTERFACE NAMES" +.sp +The actual name for \fIethX\fR depends on the \fBAUTO_VLAN\fR setting in the fcoemon interface configuration (/etc/fcoe/cfg\-ethX, see \fBfcoemon\fR)\&. +.sp +If \fBAUTO_VLAN\fR is set to \fIyes\fR, the interface name \fIethX\fR references the network device itself\&. If \fBAUTO_VLAN\fR is set to \fIno\fR, the interface name \fIethX\fR references the VLAN device\&. +.SH "EXAMPLES" +.sp +Creates an FCoE instance on eth2\&.101 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-c eth2\&.101 +.fi +.if n \{\ +.RE +.\} +.sp +Creates an FCoE VN2VN instance on eth2\&.101 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-m vn2vn \-c eth2\&.101 +.fi +.if n \{\ +.RE +.\} +.sp +Destroys the FCoE instance on eth2\&.101 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-d eth2\&.101 +.fi +.if n \{\ +.RE +.\} +.sp +Resets the FCoE instance on eth2\&.101 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-r eth2\&.101 +.fi +.if n \{\ +.RE +.\} +.sp +Show the information of all the adapters and their ports having FCoE instances created +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-i +.fi +.if n \{\ +.RE +.\} +.sp +Show the information of all FCoE instances on interface eth3\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-i eth3 +.fi +.if n \{\ +.RE +.\} +.sp +Show the information of all the discovered targets from all the ports having FCoE instances created (they may be on different adapter cards)\&. A brief listing of discovered LUNs are listed after the target they are associated with, if any +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-t +.fi +.if n \{\ +.RE +.\} +.sp +Show the information of all the discovered targets from a given port (eth3) having FCoE instance created\&. A brief listing of discovered LUNs are listed after each target they are associated with, if any +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-t eth3 +.fi +.if n \{\ +.RE +.\} +.sp +Show the detailed information of all the LUNs discovered on all FCoE connections +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-l +.fi +.if n \{\ +.RE +.\} +.sp +Show the detailed information of all the LUNs associated with a specific interface +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-l eth3\&.101 +.fi +.if n \{\ +.RE +.\} +.sp +Show the statistics information of a specific port eth3 having FCoE instances created\&. The statistics are displayed one line per time interval\&. The default interval is one second if an interval is not specified +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-s eth3 +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcoeadm \-s eth3 3 +.fi +.if n \{\ +.RE +.\} +.SH "SEE ALSO" +.sp +\fBfcoemon\fR(8) +.SH "SUPPORT" +.sp +\fBfcoeadm\fR is part of the \fIfcoe\-utils\fR package, maintained through the \fIOpen\-FCoE\fR project\&. Resources for both developers and users can be found at the \fIOpen\-FCoE\fR website http://open\-fcoe\&.org/ diff --git a/doc/fcoeadm.txt b/doc/fcoeadm.txt new file mode 100644 index 0000000..28ed482 --- /dev/null +++ b/doc/fcoeadm.txt @@ -0,0 +1,207 @@ +/////////////////////////////////////////////////////////////////////////// +// vim:syntax=asciidoc:tw=75: +// +// This is an asciidoc text file, which will be converted into a UNIX man +// page using asciidoc and the DocBook XSL stylesheets. +// +// If you are going to update this documentation, please modify this file +// and then regenerate the nroff formated man page using the Makefile. +/////////////////////////////////////////////////////////////////////////// + +FCOEADM(8) +========== +:man source: Open-FCoE +:man manual: Open-FCoE Tools + +NAME +---- +fcoeadm - The Open-FCoE Administration Tool + +SYNOPSIS +-------- +*fcoeadm* [-m|--mode fabric|vn2vn] -c|--create _ethX_ + +*fcoeadm* -d|--destroy _ethX_ + +*fcoeadm* -r|--reset _ethX_ + +*fcoeadm* -S|--Scan [_ethX_] + +*fcoeadm* -i|--interface [_ethX_] + +*fcoeadm* -f|--fcf [_ethX_] + +*fcoeadm* -t|--target [_ethX_] + +*fcoeadm* -l|--lun [_ethX_] + +*fcoeadm* -s|--stats _ethX_ [_interval_] + +*fcoeadm* -b|--lesb _ethX_ [_interval_] + +*fcoeadm* -h|--help + +*fcoeadm* -v|--version + +DESCRIPTION +----------- +The *fcoeadm* utility is the Fibre Channel over Ethernet (FCoE) management +tool for the _Open-FCoE_ project. *fcoeadm* may be used to create, +destroy, and reset an FCoE instance on a given network interface. For these +operations *fcoeadm* sends a command to a running *fcoemon* process, via a +socket interface. *fcoemon* will then perform the requested operation. + +*fcoeadm* also provides operations to query information about FCoE instances, +including interface information, target information, LUN information, and port +statistics. For much of this information, *fcoeadm* relies on the _libhbalinux_ +implementation of the _HBA API_. Only one operation may be given on a +command. + +OPTIONS +------- +*-m*, *--mode* fabric|vn2vn:: + Used together with the create option to control the FCoE mode of + operation. The mode defaults to fabric but this option allows + the selection of vn2vn mode. + +OPERATIONS +---------- +*-c*, *--create* _ethX_:: + Creates an FCoE instance based on the specified network interface. + Note that if there is not an fcoemon configuration file for the + interface (/etc/fcoe/cfg-ethX, see *fcoemon*), then the created + FCoE instance will not require DCB. + +*-d*, *--destroy* _ethX_:: + Destroys the FCoE instance on the specified network interface. This + will not destroy FCoE instances created by fipvlan. + +*-r*, *--reset* _ethX_:: + Resets the FCoE instance on the specified network interface. This + will not reset FCoE instances created by fipvlan. + +*-S*, *--Scan* _ethX_:: + Rescan for new targets and LUNs on the provided instance. This + command will not rescan any NPIV instances created on the same port. + This will not rescan any FCoE instance created by fipvlan. + +*-i*, *--interface* [_ethX_]:: + Show information about the FCoE instance on the specified network + interface, or all FCoE instances if no network interface is specified. + +*-f*, *--fcf* [_ethX_]:: + Show information about the discovered Fibre Channel Forwarders (FCFs) + on the specified network interface, or all discovered FCFs if no + network interface is specified. + +*-t*, *--target* [_ethX_]:: + Show information about the discovered targets associated with the + FCoE instance on the specified network interface. + If no network interface is specified, information about discovered + targets from all FCoE instances will be shown. + +*-l*, *--lun* [_ethX_]:: + Show detailed information about the discovered SCSI LUNs associated + with the FCoE instance on the specified network interface. + If no network interface is specified, information about SCSI LUNs + from all FCoE instances will be shown. + +*-s*, *--stats* _ethX_ [_interval_]:: + Show the statistics (including FC4 statistics) of the FCoE + connection on the specified network interface. The information will + be displayed repeatedly until the user cancels the command. The + statistics will be printed every specified _interval_ (in seconds). + _interval_ should be specified as a whole number greater than 0. + If _interval_ is not specified, a default interval of one second is + used. + +*-b*, *--lesb* _ethX_ [_interval_]:: + Show the Link Error Statistics Block (LESB) of the FCoE connection + on the specified network interface. The information will be displayed + repeatedly until the user cancels the command. The LESB statistics + will be printed every specified _interval_ (in seconds). _interval_ + should be specified in whole integers greater than 0. If the + _interval_ is not specified, a default interval of one second is used. + +*-h*, *--help*:: + Displays the usage message of the *fcoeadm* command. + +*-v*, *--version*:: + Displays the version of the *fcoeadm* command. + +INTERFACE NAMES +--------------- +The actual name for _ethX_ depends on the *AUTO_VLAN* setting in the fcoemon +interface configuration (/etc/fcoe/cfg-ethX, see *fcoemon*). + +If *AUTO_VLAN* is set to _yes_, the interface name _ethX_ references the +network device itself. If *AUTO_VLAN* is set to _no_, the interface +name _ethX_ references the VLAN device. + +EXAMPLES +-------- +Creates an FCoE instance on eth2.101 + + fcoeadm -c eth2.101 + +Creates an FCoE VN2VN instance on eth2.101 + + fcoeadm -m vn2vn -c eth2.101 + +Destroys the FCoE instance on eth2.101 + + fcoeadm -d eth2.101 + +Resets the FCoE instance on eth2.101 + + fcoeadm -r eth2.101 + +Show the information of all the adapters and their ports having FCoE +instances created + + fcoeadm -i + +Show the information of all FCoE instances on interface eth3. + + fcoeadm -i eth3 + +Show the information of all the discovered targets from all the ports +having FCoE instances created (they may be on different adapter cards). +A brief listing of discovered LUNs are listed after the target they are +associated with, if any + + fcoeadm -t + +Show the information of all the discovered targets from a given port (eth3) +having FCoE instance created. A brief listing of discovered LUNs are listed +after each target they are associated with, if any + + fcoeadm -t eth3 + +Show the detailed information of all the LUNs discovered on all FCoE +connections + + fcoeadm -l + +Show the detailed information of all the LUNs associated with a specific +interface + + fcoeadm -l eth3.101 + +Show the statistics information of a specific port eth3 having FCoE +instances created. The statistics are displayed one line per time interval. +The default interval is one second if an interval is not specified + + fcoeadm -s eth3 + + fcoeadm -s eth3 3 + +SEE ALSO +-------- +*fcoemon*(8) + +SUPPORT +------- +*fcoeadm* is part of the _fcoe-utils_ package, maintained through the +_Open-FCoE_ project. Resources for both developers and users can be found +at the _Open-FCoE_ website diff --git a/doc/fcoemon.8 b/doc/fcoemon.8 new file mode 100644 index 0000000..63271f3 --- /dev/null +++ b/doc/fcoemon.8 @@ -0,0 +1,373 @@ +'\" t +.\" Title: fcoemon +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 09/30/2015 +.\" Manual: Open-FCoE Tools +.\" Source: Open-FCoE +.\" Language: English +.\" +.TH "FCOEMON" "8" "09/30/2015" "Open\-FCoE" "Open\-FCoE Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +fcoemon \- Open\-FCoE service daemon +.SH "SYNOPSIS" +.sp +\fBfcoemon\fR [\-f|\-\-foreground] [\-l|\-\-legacy] [\-d|\-\-debug] [\-s|\-\-syslog] +.sp +\fBfcoemon\fR \-h|\-\-help +.sp +\fBfcoemon\fR \-v|\-\-version +.SH "DESCRIPTION" +.sp +The \fBfcoemon\fR daemon is the core component of the \fIOpen\-FCoE\fR management service\&. +.sp +The primary function of \fBfcoemon\fR is to control FCoE instances\&. \fBfcoemon\fR will create, destroy, reset, enable and disable FCoE instances based on system configuration, administrative commands, and runtime events\&. +.sp +On startup, \fBfcoemon\fR will create FCoE instances defined by the configuration files (see \fIFILES\fR section below)\&. Since FCoE typically relies on the Data Center Bridging (DCB) capabilities of an Ethernet interface, \fBfcoemon\fR establishes a connection with the LLDP daemon \fBlldpad\fR to query the status of the DCB features on relevant Ethernet interfaces and receive DCB configuration change events\&. +.sp +During runtime, \fBfcoemon\fR will monitor network and \fBlldpad\fR events for the relevant Ethernet interfaces and perform appropriate actions (create, destroy, enable, disable) on the FCoE instances\&. \fBfcoemon\fR also provides a client interface via which the \fBfcoeadm\fR utility is able to issue commands\&. +.sp +Installation of the \fIfcoe\-utils\fR package will set up an \fIfcoe\fR service which will control the execution of the \fBfcoemon\fR daemon\&. +.SH "OPTIONS" +.PP +\fB\-f\fR, \fB\-\-foreground\fR +.RS 4 +Run +\fBfcoemon\fR +in the foreground\&. +.RE +.PP +\fB\-d\fR, \fB\-\-debug\fR +.RS 4 +Enable debugging messages\&. +.RE +.PP +\fB\-l\fR, \fB\-\-legacy\fR +.RS 4 +Force fcoemon to use the legacy /sys/module/libfcoe/parameters/ interface\&. The default is to use the newer /sys/bus/fcoe/ interfaces if they are available\&. +.RE +.PP +\fB\-s\fR, \fB\-\-syslog\fR +.RS 4 +Use syslogd for logging\&. The default behavior is to log to stdout and stderr\&. +.RE +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Show help message with basic usage instructions +.RE +.PP +\fB\-v\fR, \fB\-\-version\fR +.RS 4 +Show the version of the +\fBfcoemon\fR +command\&. +.RE +.SH "TERMINOLOGY" +.PP +\fIDCB\fR +.RS 4 +Data Center Bridging A set of Ethernet enhancement standards developed by the IEEE 802\&.1 Working Group\&. +.RE +.PP +\ \& +.RS 4 +See +http://www\&.ieee802\&.org/1/pages/dcbridges\&.html +for more information\&. +.RE +.PP +\fIDCBX\fR +.RS 4 +DCB Capabilities Exchange Protocol, implemented by the DCB module of +\fBlldpad\fR\&. DCBX exchanges DCB capabilities and configuration with a link partner as a series of values transferred using the Link Layer Discovery Protocol (LLDP)\&. +.RE +.PP +\fIPFC\fR +.RS 4 +Priority\-based Flow Control, a +\fIDCB\fR +feature\&. +.RE +.PP +\fIApp:FCoE\fR +.RS 4 +The FCoE instance of application specific parameters in DCBX\&. +.RE +.SH "CRITERIA USED FOR CONTROLLING THE FCOE INSTANCE" +.sp +\fBfcoemon\fR uses two information sources for determining when to create an FCoE instance: the state of the network interface, which may be a VLAN interface, and, if required for the FCoE instance, the state of the DCB configuration on the physical Ethernet interface\&. +.sp +First of all, the network interface must be "up" for the FCoE instance to be created\&. Secondly, if the FCoE configuration indicates that DCB is required, then the following criteria must be satisfied before the FCoE interface is created: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +DCB is enabled on the Ethernet interface\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The PFC DCB feature is enabled and operational\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The App:FCoE DCB feature is enabled and operational\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The priority indicated by the App:FCoE feature is also enabled for PFC\&. +.RE +.sp +Once the FCoE instance is created by \fBfcoemon\fR, it will only be destroyed under the following conditions: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The driver for the Ethernet interface is unloaded\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +A user administratively destroys the FCoE instance using +\fBfcoeadm\fR\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The +\fBfcoemon\fR +daemon is terminated\&. +.RE +.sp +If DCB is required for the FCoE instance, and the DCB settings change after the interface is created, the following criteria are used to disable the FCoE instance: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +DCB is disabled on the Ethernet interface\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The App:FCoE DCB feature is not enabled\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The App:FCoE and PFC features are operational AND the priority indicated by App:FCoE is not enabled for PFC\&. +.RE +.sp +Otherwise, the FCoE instance will always remain enabled\&. +.SH "CONFIGURATION" +.sp +Once the \fIfcoe\-utils\fR and \fBlldpad\fR packages have been installed and the corresponding services are running, there are a few simple configuration steps required to get an FCoE instance up and running\&. The following assumes that DCB will be required for the interface\&. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Ensure that the configuration on the peer device (e\&.g\&. FCoE capable switch) has the necessary configurations (VLANs, DCB, DCBX)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Configure any needed VLAN interfaces on the local system\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Create and configure +\fI/etc/fcoe/cfg\-\fR +files for the network interfaces over which FCoE instances need to be created\&. See the +\fIFILES\fR +sections for details\&. Note that +\fIifname\fR +may be for a VLAN interface\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Restart the +\fBfcoe\fR +service (i\&.e\&. +\fBfcoemon\fR)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The default DCB configuration of an Ethernet interface managed by +\fBlldpad\fR +requires the following configuration using +\fBdcbtool\fR\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +dcbtool sc ethX dcb on <\-\- enable DCB on the interface +dcbtool sc ethX app:fcoe e:1 <\-\- enable App:FCoE on the interface +.fi +.if n \{\ +.RE +.\} +.RE +.sp +These steps only need to be done one time\&. Note that if other DCB configuration changes have been made with \fBdcbtool\fR, then additional changes may need to be made in order to satisfy the DCB criteria for creating an FCoE instance\&. Consult \fBdcbtool\fR for details\&. +.sp +Once these configuration steps have been performed, use \fBfcoeadm\fR to query the status of the FCoE instances\&. +.SH "FILES" +.SS "/etc/fcoe/config" +.sp +This is the primary configuration file for the \fBfcoe\fR system service\&. The default options in this file are: \fBDEBUG="no"\fR and \fBUSE_SYSLOG="yes"\fR\&. The former is used to enable debugging messages from the fcoe service script and \fBfcoemon\fR (via the \fB\-\-debug\fR option)\&. The latter is to indicate if the log messages are to be output to the system log (via the \fB\-\-syslog\fR option)\&. \fBSUPPORTED_DRIVERS\fR is the list of drivers to automatically load during fcoe service start\&. Any changes to this file will require a restart of the \fBfcoe\fR service\&. +.SS "/etc/sysconfig/fcoe" +.sp +On systemd\-enabled systems, this is the primary configuration file used for the \fBfcoe\fR system service\&. Add \fB\-\-debug\fR to \fBFCOEMON_OPTS\fR to enable debug log messages\&. Any changes to this file will require a restart of the \fBfcoe\fR service\&. +.SS "/etc/fcoe/cfg\-" +.sp +These files are read by \fBfcoemon\fR on initialization\&. They are used to indicate which Ethernet or VLAN interfaces should have FCoE instances created\&. The option values in this file normally are: \fBFCOE_ENABLE="yes"\fR, \fBDCB_REQUIRED="yes"\fR, and \fBAUTO_VLAN="yes"\fR, though if the variable values are omitted, they default to "no"\&. +.PP +\fIFCOE_ENABLE\fR +.RS 4 +is used to enable/disable creation of the FCoE instance\&. If FCoE_ENABLE is set to "no", then the other configuration values have no effect\&. +.RE +.PP +\fIDCB_REQUIRED\fR +.RS 4 +indicates if the DCB service is required on the Ethernet interface\&. +.RE +.PP +\fIAUTO_VLAN\fR +.RS 4 +indicates if VLAN discovery should be performed\&. If AUTO_VLAN is set to "yes", then once the link configuration has been validated, +\fBfcoemon\fR +will run run the FIP VLAN discovery protocol on the Ethernet interface\&. Network interfaces for any discovered FCoE VLANs will be automatically created, if they are not already configured, and FCoE instances will be created on the VLAN interfaces\&. If the network interface specified by the filename is already a VLAN interface, the AUTO_VLAN setting is ignored\&. +.RE +.PP +\fIMODE\fR +.RS 4 +indicates whether operation will be in fabric or vn2vn mode\&. The default is fabric\&. +.RE +.PP +\fIFIP_RESP\fR +.RS 4 +indicates whether a FIP responder should be activated on this device to support VLAN discovery in a vn2vn environment\&. The default is "no"\&. Often this will be used on a vn2vn node also serving as a target\&. When using this option, the AUTO_VLAN should not be set and the supported VLANs should have configuration files supplied, constituting a static VLAN configuration that provides the information for the FIP responder to return\&. This should be set on a base device, which should probably have FCOE_ENABLE set to "no" since presumably FCoE operation will be on the configured VLANs\&. +.RE +.sp +Note that the attached Ethernet peer device (e\&.g\&. FCoE capable switch port) must have compatible settings For DCB and FCoE to function properly\&. +.SS "/etc/init\&.d/fcoe" +.sp +This is the \fBfcoe\fR system service script\&. This script is invoked by the init process or by the service command to start and stop the \fBfcoemon\fR\&. On systemd\-enabled systems, \fBfcoemon\fR is controlled via the \fBfcoe\&.service\fR unit\&. +.SH "VLAN NAMING CONVENTIONS" +.sp +If a new VLAN device is created (see the description of the \fIAUTO_VLAN\fR setting above), it will have the name \fIdev\fR\&.\fIvlan\fR\-fcoe; where \fIdev\fR is the name of the Ethernet parent device and \fIvlan\fR is the discovered VLAN ID number\&. +.SH "SEE ALSO" +.sp +\fBfcoeadm\fR(8) \fBlldpad\fR(8) \fBlldptool\fR(8) \fBdcbtool\fR(8) +.SH "SUPPORT" +.sp +\fBfcoemon\fR is part of the \fIfcoe\-utils\fR package, maintained through the \fIOpen\-FCoE\fR project\&. Resources for both developers and users can be found at the \fIOpen\-FCoE\fR website http://open\-fcoe\&.org/ diff --git a/doc/fcoemon.txt b/doc/fcoemon.txt new file mode 100644 index 0000000..3bfb358 --- /dev/null +++ b/doc/fcoemon.txt @@ -0,0 +1,244 @@ +/////////////////////////////////////////////////////////////////////////// +// vim:syntax=asciidoc:tw=75: +// +// This is an asciidoc text file, which will be converted into a UNIX man +// page using asciidoc and the DocBook XSL stylesheets. +// +// If you are going to update this documentation, please modify this file +// and then regenerate the nroff formatted man page using the Makefile. +/////////////////////////////////////////////////////////////////////////// + +FCOEMON(8) +========== +:man source: Open-FCoE +:man manual: Open-FCoE Tools + +NAME +---- +fcoemon - Open-FCoE service daemon + +SYNOPSIS +-------- +*fcoemon* [-f|--foreground] [-l|--legacy] [-d|--debug] [-s|--syslog] + +*fcoemon* -h|--help + +*fcoemon* -v|--version + +DESCRIPTION +----------- +The *fcoemon* daemon is the core component of the _Open-FCoE_ management +service. + +The primary function of *fcoemon* is to control FCoE instances. *fcoemon* +will create, destroy, reset, enable and disable FCoE instances based on +system configuration, administrative commands, and runtime events. + +On startup, *fcoemon* will create FCoE instances defined by the +configuration files (see _FILES_ section below). Since FCoE typically +relies on the Data Center Bridging (DCB) capabilities of an Ethernet +interface, *fcoemon* establishes a connection with the LLDP daemon *lldpad* +to query the status of the DCB features on relevant Ethernet interfaces and +receive DCB configuration change events. + +During runtime, *fcoemon* will monitor network and *lldpad* events for the +relevant Ethernet interfaces and perform appropriate actions (create, +destroy, enable, disable) on the FCoE instances. *fcoemon* also provides a +client interface via which the *fcoeadm* utility is able to issue commands. + +Installation of the _fcoe-utils_ package will set up an _fcoe_ service which +will control the execution of the *fcoemon* daemon. + +OPTIONS +------- +*-f*, *--foreground*:: + Run *fcoemon* in the foreground. +*-d*, *--debug*:: + Enable debugging messages. +*-l*, *--legacy*:: + Force fcoemon to use the legacy /sys/module/libfcoe/parameters/ + interface. The default is to use the newer /sys/bus/fcoe/ interfaces + if they are available. +*-s*, *--syslog*:: + Use syslogd for logging. The default behavior is to log to stdout + and stderr. +*-h*, *--help*:: + Show help message with basic usage instructions +*-v*, *--version*:: + Show the version of the *fcoemon* command. + + +TERMINOLOGY +----------- +_DCB_:: + Data Center Bridging A set of Ethernet enhancement standards + developed by the IEEE 802.1 Working Group. + :: + See for more + information. + +_DCBX_:: + DCB Capabilities Exchange Protocol, implemented by the DCB module + of *lldpad*. DCBX exchanges DCB capabilities and configuration with + a link partner as a series of values transferred using the Link Layer + Discovery Protocol (LLDP). + +_PFC_:: + Priority-based Flow Control, a _DCB_ feature. + +_App:FCoE_:: + The FCoE instance of application specific parameters in DCBX. + +CRITERIA USED FOR CONTROLLING THE FCOE INSTANCE +----------------------------------------------- +*fcoemon* uses two information sources for determining when to create an +FCoE instance: the state of the network interface, which may be a VLAN +interface, and, if required for the FCoE instance, the state of the DCB +configuration on the physical Ethernet interface. + +First of all, the network interface must be "up" for the FCoE instance to +be created. Secondly, if the FCoE configuration indicates that DCB is +required, then the following criteria must be satisfied before the FCoE +interface is created: + +* DCB is enabled on the Ethernet interface. +* The PFC DCB feature is enabled and operational. +* The App:FCoE DCB feature is enabled and operational. +* The priority indicated by the App:FCoE feature is also enabled for PFC. + +Once the FCoE instance is created by *fcoemon*, it will only be destroyed +under the following conditions: + +* The driver for the Ethernet interface is unloaded. +* A user administratively destroys the FCoE instance using *fcoeadm*. +* The *fcoemon* daemon is terminated. + +If DCB is required for the FCoE instance, and the DCB settings change after +the interface is created, the following criteria are used to disable the +FCoE instance: + +* DCB is disabled on the Ethernet interface. +* The App:FCoE DCB feature is not enabled. +* The App:FCoE and PFC features are operational AND the priority indicated + by App:FCoE is not enabled for PFC. + +Otherwise, the FCoE instance will always remain enabled. + +CONFIGURATION +------------- +Once the _fcoe-utils_ and *lldpad* packages have been installed and the +corresponding services are running, there are a few simple configuration +steps required to get an FCoE instance up and running. The following +assumes that DCB will be required for the interface. + +* Ensure that the configuration on the peer device (e.g. FCoE capable + switch) has the necessary configurations (VLANs, DCB, DCBX). +* Configure any needed VLAN interfaces on the local system. +* Create and configure _/etc/fcoe/cfg-_ files for the network + interfaces over which FCoE instances need to be created. See the _FILES_ + sections for details. Note that _ifname_ may be for a VLAN interface. +* Restart the *fcoe* service (i.e. *fcoemon*). +* The default DCB configuration of an Ethernet interface managed by + *lldpad* requires the following configuration using *dcbtool*. + + dcbtool sc ethX dcb on <-- enable DCB on the interface + dcbtool sc ethX app:fcoe e:1 <-- enable App:FCoE on the interface + +These steps only need to be done one time. Note that if other DCB +configuration changes have been made with *dcbtool*, then additional +changes may need to be made in order to satisfy the DCB criteria for +creating an FCoE instance. Consult *dcbtool* for details. + +Once these configuration steps have been performed, use *fcoeadm* to query +the status of the FCoE instances. + +FILES +----- +/etc/fcoe/config +~~~~~~~~~~~~~~~~ +This is the primary configuration file for the *fcoe* system service. The +default options in this file are: *DEBUG="no"* and *USE_SYSLOG="yes"*. The +former is used to enable debugging messages from the fcoe service script +and *fcoemon* (via the *--debug* option). The latter is to indicate if the +log messages are to be output to the system log (via the *--syslog* +option). *SUPPORTED_DRIVERS* is the list of drivers to automatically load +during fcoe service start. Any changes to this file will require a restart +of the *fcoe* service. + +/etc/sysconfig/fcoe +~~~~~~~~~~~~~~~~~~~ +On systemd-enabled systems, this is the primary configuration file used for +the *fcoe* system service. Add *--debug* to *FCOEMON_OPTS* to enable debug +log messages. Any changes to this file will require a restart of the *fcoe* +service. + +/etc/fcoe/cfg- +~~~~~~~~~~~~~~~~~~~~~~ +These files are read by *fcoemon* on initialization. They are used to +indicate which Ethernet or VLAN interfaces should have FCoE instances +created. The option values in this file normally are: *FCOE_ENABLE="yes"*, +*DCB_REQUIRED="yes"*, and *AUTO_VLAN="yes"*, though if the variable +values are omitted, they default to "no". + +_FCOE_ENABLE_:: + is used to enable/disable creation of the FCoE instance. If + FCoE_ENABLE is set to "no", then the other configuration values + have no effect. + +_DCB_REQUIRED_:: + indicates if the DCB service is required on the Ethernet interface. + +_AUTO_VLAN_:: + indicates if VLAN discovery should be performed. If AUTO_VLAN is + set to "yes", then once the link configuration has been validated, + *fcoemon* will run run the FIP VLAN discovery protocol on the + Ethernet interface. Network interfaces for any discovered FCoE + VLANs will be automatically created, if they are not already + configured, and FCoE instances will be created on the VLAN + interfaces. If the network interface specified by the filename is + already a VLAN interface, the AUTO_VLAN setting is ignored. + +_MODE_:: + indicates whether operation will be in fabric or vn2vn mode. The + default is fabric. + +_FIP_RESP_:: + indicates whether a FIP responder should be activated on this + device to support VLAN discovery in a vn2vn environment. The + default is "no". Often this will be used on a vn2vn node also + serving as a target. When using this option, the AUTO_VLAN + should not be set and the supported VLANs should have + configuration files supplied, constituting a static VLAN + configuration that provides the information for the FIP responder + to return. This should be set on a base device, which should + probably have FCOE_ENABLE set to "no" since presumably FCoE + operation will be on the configured VLANs. + +Note that the attached Ethernet peer device (e.g. FCoE capable switch port) +must have compatible settings For DCB and FCoE to function properly. + +/etc/init.d/fcoe +~~~~~~~~~~~~~~~~ +This is the *fcoe* system service script. This script is invoked by the +init process or by the service command to start and stop the *fcoemon*. +On systemd-enabled systems, *fcoemon* is controlled via the *fcoe.service* unit. + +VLAN NAMING CONVENTIONS +----------------------- +If a new VLAN device is created (see the description of the _AUTO_VLAN_ +setting above), it will have the name _dev_._vlan_-fcoe; where _dev_ is the +name of the Ethernet parent device and _vlan_ is the discovered VLAN ID +number. + +SEE ALSO +-------- +*fcoeadm*(8) +*lldpad*(8) +*lldptool*(8) +*dcbtool*(8) + +SUPPORT +------- +*fcoemon* is part of the _fcoe-utils_ package, maintained through the +_Open-FCoE_ project. Resources for both developers and users can be found +at the _Open-FCoE_ website diff --git a/doc/fcping.8 b/doc/fcping.8 new file mode 100644 index 0000000..d5d3f09 --- /dev/null +++ b/doc/fcping.8 @@ -0,0 +1,202 @@ +'\" t +.\" Title: fcping +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.75.2 +.\" Date: 05/24/2011 +.\" Manual: Open-FCoE Tools +.\" Source: Open-FCoE +.\" Language: English +.\" +.TH "FCPING" "8" "05/24/2011" "Open\-FCoE" "Open\-FCoE Tools" +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +fcping \- Fibre Channel Ping (ELS ECHO) tool +.SH "SYNOPSIS" +.sp +\fBfcping\fR [\-fqx] [ \-i \fIinterval\fR ] [ \-c \fIcount\fR ] [ \-s \fIsize\fR ] \-h \fIsource\fR \fItarget\fR +.sp +source = \fIinterface\fR | \fIMAC\fR | \fIWWPN\fR | \fIFC\-ID\fR +.sp +target = \-P \fIWWPN\fR | \-N \fIWWPN\fR | \-F \fIFC\-ID\fR +.SH "DESCRIPTION" +.sp +The \fBfcping\fR command performs the Fibre Channel FC\-LS\-2 ECHO ELS command and displays the reply\&. The max payload for \fBfcping\fR is 2108 bytes\&. When no \fB\-c\fR is given, \fBfcping\fR will continue until user interrupts\&. +.SH "OPTIONS" +.PP +\fB\-f\fR +.RS 4 +Flood ping +.RE +.PP +\fB\-q\fR +.RS 4 +Quiet! just print summary +.RE +.PP +\fB\-x\fR +.RS 4 +Hex dump of responses +.RE +.PP +\fB\-i\fR \fIinterval\fR +.RS 4 +Wait +\fIinterval\fR +seconds between each ping +.RE +.PP +\fB\-c\fR \fIcount\fR +.RS 4 +Stop after sending +\fIcount\fR +pings +.RE +.PP +\fB\-h\fR \fIsource\fR +.RS 4 +Source is +\fIinterface\fR +like ethX, +\fIMAC\fR +address, +\fIWWPN\fR, or +\fIFC\-ID\fR +.RE +.PP +\fB\-s\fR \fIsize\fR +.RS 4 +Byte\-length of ping request payload (max 2108) +.RE +.PP +\fB\-F\fR \fIFC\-ID\fR +.RS 4 +Destination port ID +.RE +.PP +\fB\-P\fR \fIWWPN\fR +.RS 4 +Destination world\-wide port name +.RE +.PP +\fB\-N\fR \fIWWNN\fR +.RS 4 +Destination world\-wide node name +.RE +.SH "EXAMPLES" +.sp +Sends ELS ECHO to Port FC\-ID 0xbf0027 from interface n0\&.170 with max 3 pings\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcping \-c 3 \-h n0\&.170 \-F 0xbf0027 +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +sending echo to 0xBF0027 +echo 1 accepted 16\&.170 ms +echo 2 accepted 7\&.053 ms +echo 3 accepted 6\&.803 ms +3 frames sent, 3 received 0 errors, 0\&.000% loss, avg\&. rt time 10\&.009 ms +.fi +.if n \{\ +.RE +.\} +.sp +Sends ELS ECHO to WWNN 0x1000001B213C9DDA from interface n0\&.170 with max 3 pings and payload size 1500 bytes +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcping \-c 3 \-h n0\&.170 \-s 1500 \-N 0x1000001B213C9DDA +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +sending echo to 0xBF0027 +echo 1 accepted 6\&.597 ms +echo 2 accepted 6\&.925 ms +echo 3 accepted 12\&.419 ms +3 frames sent, 3 received 0 errors, 0\&.000% loss, avg\&. rt time 8\&.647 ms +.fi +.if n \{\ +.RE +.\} +.sp +Sends ELS ECHO to WWNN 0x1000001B213C9DDA from interface n0\&.170 continuously with payload of 32 bytes and hex\-dump the reply to the screen +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcping \-x \-h n0\&.170 \-s 32 \-N 0x1000001B213C9DDA +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +sending echo to 0xBF0027 +echo 1 accepted 6\&.937 ms +response length 32 + 0 02000000 00000001 00010203 04050607 + 10 08090a0b 0c0d0e0f 10111213 14151617 +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +echo 2 accepted 6\&.923 ms +response length 32 + 0 02000000 00000002 00010203 04050607 + 10 08090a0b 0c0d0e0f 10111213 14151617 +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +echo 3 accepted 84\&.929 ms +response length 32 + 0 02000000 00000003 00010203 04050607 + 10 08090a0b 0c0d0e0f 10111213 14151617 +.fi +.if n \{\ +.RE +.\} +.SH "SEE ALSO" +.sp +\fBfcoeadm\fR(8) +.SH "SUPPORT" +.sp +\fBfcping\fR is part of the \fIfcoe\-utils\fR package, maintained through the \fIOpen\-FCoE\fR project\&. Resources for both developers and users can be found at the \fIOpen\-FCoE\fR website http://open\-fcoe\&.org/\&. diff --git a/doc/fcping.txt b/doc/fcping.txt new file mode 100644 index 0000000..d73c5a6 --- /dev/null +++ b/doc/fcping.txt @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////// +// vim:syntax=asciidoc:tw=75: +// +// This is an asciidoc text file, which will be converted into a UNIX man +// page using asciidoc and the DocBook XSL stylesheets. +// +// If you are going to update this documentation, please modify this file +// and then regenerate the nroff formated man page using the Makefile. +/////////////////////////////////////////////////////////////////////////// + +fcping(8) +========== +:man source: Open-FCoE +:man manual: Open-FCoE Tools + +NAME +---- +fcping - Fibre Channel Ping (ELS ECHO) tool + +SYNOPSIS +-------- +*fcping* [-fqx] [ -i _interval_ ] [ -c _count_ ] [ -s _size_ ] -h _source_ _target_ + +source = _interface_ | _MAC_ | _WWPN_ | _FC-ID_ + +target = -P _WWPN_ | -N _WWPN_ | -F _FC-ID_ + +DESCRIPTION +----------- +The *fcping* command performs the Fibre Channel FC-LS-2 ECHO ELS command +and displays the reply. The max payload for *fcping* is 2108 bytes. When +no *-c* is given, *fcping* will continue until user interrupts. + +OPTIONS +------- +*-f*:: + Flood ping + +*-q*:: + Quiet! just print summary + +*-x*:: + Hex dump of responses + +*-i* _interval_:: + Wait _interval_ seconds between each ping + +*-c* _count_:: + Stop after sending _count_ pings + +*-h* _source_:: + Source is _interface_ like ethX, _MAC_ address, _WWPN_, or _FC-ID_ + +*-s* _size_:: + Byte-length of ping request payload (max 2108) + +*-F* _FC-ID_:: + Destination port ID + +*-P* _WWPN_:: + Destination world-wide port name + +*-N* _WWNN_:: + Destination world-wide node name + +EXAMPLES +-------- +Sends ELS ECHO to Port FC-ID 0xbf0027 from interface n0.170 with max 3 pings. + + fcping -c 3 -h n0.170 -F 0xbf0027 + + sending echo to 0xBF0027 + echo 1 accepted 16.170 ms + echo 2 accepted 7.053 ms + echo 3 accepted 6.803 ms + 3 frames sent, 3 received 0 errors, 0.000% loss, avg. rt time 10.009 ms + + +Sends ELS ECHO to WWNN 0x1000001B213C9DDA from interface n0.170 with max 3 pings +and payload size 1500 bytes + + fcping -c 3 -h n0.170 -s 1500 -N 0x1000001B213C9DDA + + sending echo to 0xBF0027 + echo 1 accepted 6.597 ms + echo 2 accepted 6.925 ms + echo 3 accepted 12.419 ms + 3 frames sent, 3 received 0 errors, 0.000% loss, avg. rt time 8.647 ms + +Sends ELS ECHO to WWNN 0x1000001B213C9DDA from interface n0.170 continuously +with payload of 32 bytes and hex-dump the reply to the screen + + fcping -x -h n0.170 -s 32 -N 0x1000001B213C9DDA + + sending echo to 0xBF0027 + echo 1 accepted 6.937 ms + response length 32 + 0 02000000 00000001 00010203 04050607 + 10 08090a0b 0c0d0e0f 10111213 14151617 + + echo 2 accepted 6.923 ms + response length 32 + 0 02000000 00000002 00010203 04050607 + 10 08090a0b 0c0d0e0f 10111213 14151617 + + echo 3 accepted 84.929 ms + response length 32 + 0 02000000 00000003 00010203 04050607 + 10 08090a0b 0c0d0e0f 10111213 14151617 + + +SEE ALSO +-------- +*fcoeadm*(8) + +SUPPORT +------- +*fcping* is part of the _fcoe-utils_ package, maintained through the +_Open-FCoE_ project. Resources for both developers and users can be found +at the _Open-FCoE_ website . diff --git a/doc/fcrls.8 b/doc/fcrls.8 new file mode 100644 index 0000000..be7672c --- /dev/null +++ b/doc/fcrls.8 @@ -0,0 +1,93 @@ +'\" t +.\" Title: fcrls +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.76.1 +.\" Date: 02/06/2013 +.\" Manual: Open-FCoE Tools +.\" Source: Open-FCoE +.\" Language: English +.\" +.TH "FCRLS" "8" "02/06/2013" "Open\-FCoE" "Open\-FCoE Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +fcrls \- Fibre Channel Read Link Error Status Block (RLS) tool +.SH "SYNOPSIS" +.sp +\fBfcrls\fR \-\-port \fIrport_bsg_name\fR [\-\-quiet] +.sp +\fBfcrls\fR \-\-fcid \fIrport_port_FC\-ID\fR [\-\-quiet] +.sp +\fBfcrls\fR \-\-help +.SH "DESCRIPTION" +.sp +The \fBfcrls\fR command performs Fibre Channel Read Link Error Status Block (RLS) ELS request (see FC\-LS\-2) via BSG over a given rport bsg name or FC\-ID\&. +.sp +\fBfcrls\fR takes either a rport bsg name or a rport port FC\-ID as the input\&. To disable verbose output, use the \fB\-\-quiet\fR option\&. +.SH "OPTIONS" +.PP +\fB\-\-port\fR \fIrport_bsg_name\fR +.RS 4 +Send RLS to a rport by FC remote port bsg name, e\&.g\&., rport\-7:0\-1\&. +.RE +.PP +\fB\-\-fcid\fR \fIrport_port_FC\-ID\fR +.RS 4 +Send RLS to a rport by port FC\-ID, e\&.g\&., 0xce000d\&. +.RE +.PP +\fB\-\-quiet\fR +.RS 4 +Disable verbose output\&. +.RE +.PP +\fB\-\-help\fR +.RS 4 +Display a help message with basic usage instructions\&. +.RE +.SH "EXAMPLES" +.sp +Sends RLS to the FC remote port as rport\-7:0\-1 +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcrls \-\-port rport\-7:0\-1 +.fi +.if n \{\ +.RE +.\} +.sp +Sends RLS to the FC remote port with 0xce000d as its port FC\-ID +.sp +.if n \{\ +.RS 4 +.\} +.nf +fcrls \-\-fcid 0xce000d +.fi +.if n \{\ +.RE +.\} +.SH "SEE ALSO" +.sp +\fBfcoeadm\fR(8) +.SH "SUPPORT" +.sp +\fBfcrls\fR is part of the \fIfcoe\-utils\fR package, maintained through the \fIOpen\-FCoE\fR project\&. Resources for both developers and users can be found at the \fIOpen\-FCoE\fR website http://open\-fcoe\&.org/\&. diff --git a/doc/fcrls.txt b/doc/fcrls.txt new file mode 100644 index 0000000..2bcfaa2 --- /dev/null +++ b/doc/fcrls.txt @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////// +// vim:syntax=asciidoc:tw=75: +// +// This is an asciidoc text file, which will be converted into a UNIX man +// page using asciidoc and the DocBook XSL stylesheets. +// +// If you are going to update this documentation, please modify this file +// and then regenerate the nroff formated man page using the Makefile. +/////////////////////////////////////////////////////////////////////////// + +FCRLS(8) +========== +:man source: Open-FCoE +:man manual: Open-FCoE Tools + +NAME +---- +fcrls - Fibre Channel Read Link Error Status Block (RLS) tool + +SYNOPSIS +-------- +*fcrls* --port _rport_bsg_name_ [--quiet] + +*fcrls* --fcid _rport_port_FC-ID_ [--quiet] + +*fcrls* --help + +DESCRIPTION +----------- +The *fcrls* command performs Fibre Channel Read Link Error Status Block (RLS) +ELS request (see FC-LS-2) via BSG over a given rport bsg name or FC-ID. + +*fcrls* takes either a rport bsg name or a rport port FC-ID as the input. To +disable verbose output, use the *--quiet* option. + +OPTIONS +------- +*--port* _rport_bsg_name_:: + Send RLS to a rport by FC remote port bsg name, e.g., rport-7:0-1. + +*--fcid* _rport_port_FC-ID_:: + Send RLS to a rport by port FC-ID, e.g., 0xce000d. + +*--quiet*:: + Disable verbose output. + +*--help*:: + Display a help message with basic usage instructions. + +EXAMPLES +-------- +Sends RLS to the FC remote port as rport-7:0-1 + + fcrls --port rport-7:0-1 + +Sends RLS to the FC remote port with 0xce000d as its port FC-ID + + fcrls --fcid 0xce000d + +SEE ALSO +-------- +*fcoeadm*(8) + +SUPPORT +------- +*fcrls* is part of the _fcoe-utils_ package, maintained through the +_Open-FCoE_ project. Resources for both developers and users can be found +at the _Open-FCoE_ website . diff --git a/doc/fipvlan.8 b/doc/fipvlan.8 new file mode 100644 index 0000000..1d2b707 --- /dev/null +++ b/doc/fipvlan.8 @@ -0,0 +1,140 @@ +'\" t +.\" Title: fipvlan +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: 12/02/2013 +.\" Manual: Open-FCoE Tools +.\" Source: Open-FCoE +.\" Language: English +.\" +.TH "FIPVLAN" "8" "12/02/2013" "Open\-FCoE" "Open\-FCoE Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +fipvlan \- Fibre Channel over Ethernet VLAN Discovery +.SH "SYNOPSIS" +.sp +\fBfipvlan\fR [\-c|\-\-create] [\-d|\-\-debug] [\-s|\-\-start] [\-m|\-\-mode fabric|vn2vn] [\-u|\-\-link\-up] \fIinterfaces\fR +.sp +\fBfipvlan\fR \-a|\-\-auto [\-c|\-\-create] [\-d|\-\-debug] [\-s|\-\-start] [\-m|\-\-mode fabric|vn2vn] [\-l|\-\-link\-retry \fIcount\fR] [\-u|\-\-link\-up] +.sp +\fBfipvlan\fR \-h|\-\-help +.sp +\fBfipvlan\fR \-v|\-\-version +.SH "DESCRIPTION" +.sp +The \fBfipvlan\fR command performs Fibre Channel over Ethernet (FCoE) Initialization Protocol (FIP) VLAN Discovery over Ethernet interfaces\&. \fBfipvlan\fR can be used as a diagnostic tool to determine which VLANs have FCoE services available on a network, prior to configuring VLAN interfaces and the \fIOpen\-FCoE\fR initiator\&. \fBfipvlan\fR can also be used to create VLAN interfaces as they are discovered, and to start the \fIOpen\-FCoE\fR initiator\&. The \fB\-\-create\fR and \fB\-\-start\fR options are primarily intended to be used as part of an \fIOpen\-FCoE\fR boot solution\&. FCoE instances started in this way cannot be destroyed or reset by fcoeadm\&. +.sp +\fBfipvlan\fR takes a list of network interface names to run the VLAN discovery protocol over, or the \fB\-\-auto\fR option to use all available Ethernet interfaces\&. +.sp +\fBfipvlan\fR will enable any interface which is found disabled\&. If no response is received on that interface it will be shutdown again when \fBfipvlan\fR terminates\&. +.SH "OPTIONS" +.PP +\fB\-a\fR, \fB\-\-auto\fR +.RS 4 +Use all Ethernet interfaces currently available +.RE +.PP +\fB\-c\fR, \fB\-\-create\fR +.RS 4 +Create network interfaces for discovered FCoE VLANs\&. If a VLAN device already exists for a discovered VLAN, a new VLAN device will not be created\&. +.RE +.PP +\fB\-d\fR, \fB\-\-debug\fR +.RS 4 +Enable debugging output +.RE +.PP +\fB\-s\fR, \fB\-\-start\fR +.RS 4 +Start the +\fIOpen\-FCoE\fR +initiator on discovered FCoE VLANs +.RE +.PP +\fB\-m\fR, \fB\-\-mode\fR +.RS 4 +fabric|vn2vn Specify whether VLAN discovery is performed in the default fabric mode, or in VN2VN mode\&. +.RE +.PP +\fB\-f\fR, \fB\-\-suffix\fR \fIsuffix\fR +.RS 4 +Append the specified string +\fIsuffix\fR +to VLAN interface names\&. +.RE +.PP +\fB\-l\fR, \fB\-\-link\-retry\fR \fIcount\fR +.RS 4 +Retry check for link up to +\fIcount\fR +times\&. The link state is checked every 1000 ms\&. The default number of retries is 20\&. +.RE +.PP +\fB\-u\fR, \fB\-\-link\-up\fR +.RS 4 +Leave link up if a FIP response has been received\&. +.RE +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Display a help message with basic usage instructions +.RE +.PP +\fB\-v\fR, \fB\-\-version\fR +.RS 4 +Display the +\fBfipvlan\fR +version string +.RE +.SH "VLAN NAMING CONVENTIONS" +.sp +If a new VLAN device is created, it will have the name \fIdev\fR\&.\fIvlan\fR; where \fIdev\fR is the name of the Ethernet parent device and \fIvlan\fR is the discovered VLAN ID number\&. An optional suffix may be appended to this with the the \fB\-f\fR command line option\&. +.SH "EXAMPLES" +.sp +Display all discoverable VLANs with FCoE services +.sp +.if n \{\ +.RS 4 +.\} +.nf +fipvlan \-\-auto +.fi +.if n \{\ +.RE +.\} +.sp +Discover FCoE VLANs on interface eth2, create VLAN devices and start the \fIOpen\-FCoE\fR initiator +.sp +.if n \{\ +.RS 4 +.\} +.nf +fipvlan \-\-create \-\-start eth2 +.fi +.if n \{\ +.RE +.\} +.sp +In this example if FCoE services were available on VLAN 101 of network interface eth2, then a VLAN interface eth2\&.101 would be created and used as the parent device for the initiator\&. +.SH "SEE ALSO" +.sp +\fBfcoeadm\fR(8) \fBfcoemon\fR(8) +.SH "SUPPORT" +.sp +\fBfipvlan\fR is part of the \fIfcoe\-utils\fR package, maintained through the \fIOpen\-FCoE\fR project\&. Resources for both developers and users can be found at the \fIOpen\-FCoE\fR website http://open\-fcoe\&.org/ diff --git a/doc/fipvlan.txt b/doc/fipvlan.txt new file mode 100644 index 0000000..5ba0324 --- /dev/null +++ b/doc/fipvlan.txt @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////// +// vim:syntax=asciidoc:tw=75: +// +// This is an asciidoc text file, which will be converted into a UNIX man +// page using asciidoc and the DocBook XSL stylesheets. +// +// If you are going to update this documentation, please modify this file +// and then regenerate the nroff formated man page using the Makefile. +/////////////////////////////////////////////////////////////////////////// + +FIPVLAN(8) +========== +:man source: Open-FCoE +:man manual: Open-FCoE Tools + +NAME +---- +fipvlan - Fibre Channel over Ethernet VLAN Discovery + +SYNOPSIS +-------- +*fipvlan* [-c|--create] [-d|--debug] [-s|--start] + [-m|--mode fabric|vn2vn] [-u|--link-up] _interfaces_ + +*fipvlan* -a|--auto [-c|--create] [-d|--debug] + [-s|--start] [-m|--mode fabric|vn2vn] [-l|--link-retry _count_] + [-u|--link-up] + +*fipvlan* -h|--help + +*fipvlan* -v|--version + +DESCRIPTION +----------- +The *fipvlan* command performs Fibre Channel over Ethernet (FCoE) +Initialization Protocol (FIP) VLAN Discovery over Ethernet interfaces. +*fipvlan* can be used as a diagnostic tool to determine which VLANs have +FCoE services available on a network, prior to configuring VLAN interfaces +and the _Open-FCoE_ initiator. *fipvlan* can also be used to create VLAN +interfaces as they are discovered, and to start the _Open-FCoE_ initiator. +The *--create* and *--start* options are primarily intended to be used as +part of an _Open-FCoE_ boot solution. FCoE instances started in this way +cannot be destroyed or reset by fcoeadm. + +*fipvlan* takes a list of network interface names to run the VLAN discovery +protocol over, or the *--auto* option to use all available Ethernet +interfaces. + +*fipvlan* will enable any interface which is found disabled. If no +response is received on that interface it will be shutdown again when +*fipvlan* terminates. + +OPTIONS +------- +*-a*, *--auto*:: + Use all Ethernet interfaces currently available + +*-c*, *--create*:: + Create network interfaces for discovered FCoE VLANs. If a VLAN + device already exists for a discovered VLAN, a new VLAN device will + not be created. + +*-d*, *--debug*:: + Enable debugging output + +*-s*, *--start*:: + Start the _Open-FCoE_ initiator on discovered FCoE VLANs + +*-m*, *--mode*:: fabric|vn2vn + Specify whether VLAN discovery is performed in the default + fabric mode, or in VN2VN mode. + +*-f*, *--suffix* _suffix_:: + Append the specified string _suffix_ to VLAN interface names. + +*-l*, *--link-retry* _count_:: + Retry check for link up to _count_ times. The link state is + checked every 1000 ms. The default number of retries is 20. + +*-u*, *--link-up*:: + Leave link up if a FIP response has been received. + +*-h*, *--help*:: + Display a help message with basic usage instructions + +*-v*, *--version*:: + Display the *fipvlan* version string + +VLAN NAMING CONVENTIONS +----------------------- +If a new VLAN device is created, it will have the name _dev_._vlan_; +where _dev_ is the name of the Ethernet parent device and _vlan_ is the +discovered VLAN ID number. An optional suffix may be appended to this +with the the *-f* command line option. + + +EXAMPLES +-------- +Display all discoverable VLANs with FCoE services + + fipvlan --auto + +Discover FCoE VLANs on interface eth2, create VLAN devices and start the +_Open-FCoE_ initiator + + fipvlan --create --start eth2 + +In this example if FCoE services were available on VLAN 101 of network +interface eth2, then a VLAN interface eth2.101 would be created and +used as the parent device for the initiator. + +SEE ALSO +-------- +*fcoeadm*(8) +*fcoemon*(8) + +SUPPORT +------- +*fipvlan* is part of the _fcoe-utils_ package, maintained through the +_Open-FCoE_ project. Resources for both developers and users can be found +at the _Open-FCoE_ website diff --git a/etc/cfg-ethx b/etc/cfg-ethx new file mode 100644 index 0000000..c5aaf36 --- /dev/null +++ b/etc/cfg-ethx @@ -0,0 +1,28 @@ +## Type: yes/no +## Default: no +# Enable/Disable FCoE service at the Ethernet port +# Normally set to "yes" +FCOE_ENABLE="yes" + +## Type: yes/no +## Default: no +# Indicate if DCB service is required at the Ethernet port +# Normally set to "yes" +DCB_REQUIRED="yes" + +## Type: yes/no +## Default: no +# Indicate if VLAN discovery should be handled by fcoemon +# Normally set to "yes" +AUTO_VLAN="yes" + +## Type: fabric/vn2vn +## Default: fabric +# Indicate the mode of the FCoE operation, either fabric or vn2vn +# Normally set to "fabric" +MODE="fabric" + +## Type: yes/no +## Default: no +# Indicate whether to run a FIP responder for VLAN discovery in vn2vn mode +#FIP_RESP="yes" diff --git a/etc/config b/etc/config new file mode 100644 index 0000000..c993f35 --- /dev/null +++ b/etc/config @@ -0,0 +1,14 @@ +## Type: yes/no +## Default: no +# Switch on/off debug messages (script & C code) +DEBUG="no" + +## Type: yes/no +## Default: yes +# All the messages go to syslog and stderr (script & C code) +USE_SYSLOG="yes" + +## Type: string. Driver names separated by space +## Default: list of default drivers +# All supported drivers listed here are loaded when service starts +SUPPORTED_DRIVERS="fcoe bnx2fc" diff --git a/etc/initd/initd.fedora b/etc/initd/initd.fedora new file mode 100755 index 0000000..f20ed24 --- /dev/null +++ b/etc/initd/initd.fedora @@ -0,0 +1,158 @@ +#!/bin/sh +# +# Copyright(c) 2010-2011 Intel Corporation. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. +# +# chkconfig: - 21 80 +# +# Maintained at www.Open-FCoE.org + +### BEGIN INIT INFO +# Provides: fcoe +# Required-Start: network +# Required-Stop: +# Default-Start: +# Default-Stop: +# Short-Description: Open-FCoE Initiator +# Description: Open-FCoE Initiator +### END INIT INFO + +CONFIG_DIR=/etc/fcoe +LOG_FILE="/var/log/fcoemon.log" +LOCKFILE="/var/lock/subsys/fcoe" +FCOEMON=/usr/sbin/fcoemon +FCOEADM=/usr/sbin/fcoeadm +FCOEMON_OPTS= + +. /etc/init.d/functions +. $CONFIG_DIR/config + +if [ "$USE_SYSLOG" = "yes" ] || [ "$USE_SYSLOG" = "YES" ]; then + FCOEMON_OPTS+=" --syslog" +fi + +if [ "$DEBUG" = "yes" ] || [ "$DEBUG" = "YES" ]; then + FCOEMON_OPTS+=" --debug" +fi + +test -x $FCOEADM || { + echo "$FCOEADM not installed"; + if [ "$1" = "stop" ]; then exit 0; + else + failure + fi +} + +test -x $FCOEMON || { + echo "$FCOEMON not installed"; + if [ "$1" = "stop" ]; then exit 0; + else + failure + fi +} + +start() +{ + echo -n $"Starting FCoE initiator service: " + + pid=$($FCOEADM -p 2> /dev/null) + if [ -z "$pid" ]; then + modprobe -q libfc + modprobe -q sg + modprobe -q -a $SUPPORTED_DRIVERS + + daemon ${FCOEMON} ${FCOEMON_OPTS} + echo + touch ${LOCKFILE} + echo + else + echo "(already running)" + fi +} + +stop() +{ + local force=$1 + + pid=$($FCOEADM -p 2> /dev/null) + if [ "$force" == "force" ] + then + action "Destroying any active fcoe interface/s" + [ "$pid" ] && kill -HUP $pid + modprobe -r $SUPPORTED_DRIVERS libfc + else + [ "$pid" ] && kill -TERM $pid + fi + + action $"Stopping FCoE initiator service: " + + rm -f ${LOCKFILE} +} + +status() +{ + [ -f ${LOCKFILE} ] || { echo "$FCOEMON is stopped" ; return 3; } + + status=0 + pid=$($FCOEADM -p 2> /dev/null) + echo "$FCOEMON is running, pid=$pid" + + interfaces=`$FCOEADM -i 2>&1 | \ + awk '/Symbolic Name:/{print $6}' | \ + sort | awk '{printf("%s ", $1)}'` + + if [ -z "$interfaces" ]; then + echo "No interfaces created." + status=2 + else + echo "Created interfaces: $interfaces" + fi + + return $status +} + +case "$1" in + start) + start + ;; + + stop) + stop $2 + ;; + + restart) + stop $2 + start + ;; + + force-reload) + stop force + start + ;; + + status) + status + exit $? + ;; + condrestart|try-restart) + status || exit 0 + $0 restart + ;; + *) + echo -n "Usage: $0 {start|stop [force]|status|restart [force]|" + echo "force-reload|condrestart|try-restart}" + exit 1 + ;; +esac diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse new file mode 100755 index 0000000..661a484 --- /dev/null +++ b/etc/initd/initd.suse @@ -0,0 +1,178 @@ +#!/bin/sh +# +# Copyright(c) 2010-2011 Intel Corporation. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms and conditions of the GNU General Public License, +# version 2, as published by the Free Software Foundation. +# +# This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. +# +# Maintained at www.Open-FCoE.org +# +# +# Open-FCoE User-Space Software +# Based on: +# Template LSB system startup script for example service/daemon FOO +# Copyright (C) 1995--2005 Kurt Garloff, SUSE / Novell Inc. +# +# /etc/init.d/fcoe This shell script takes care of starting and stopping +# of the fcoemon daemon +# and its symbolic link +# /sbin/rcfcoe +# +# chkconfig: 345 20 80 +# +### BEGIN INIT INFO +# Provides: fcoe +# Required-Start: network +# Required-Stop: +# Default-Start: 3 5 +# Default-Stop: 3 5 +# Description: Open-FCoE Initiator +### END INIT INFO + +CONFIG_DIR=/etc/fcoe +LOG_FILE="/var/log/fcoemon.log" +FCOEMON=/usr/sbin/fcoemon +FCOEADM=/usr/sbin/fcoeadm +FCOEMON_OPTS= + +. /etc/rc.status +rc_reset + +. $CONFIG_DIR/config + +if [ "$USE_SYSLOG" = "yes" ] || [ "$USE_SYSLOG" = "YES" ]; then + FCOEMON_OPTS+=" --syslog" +fi + +if [ "$DEBUG" = "yes" ] || [ "$DEBUG" = "YES" ]; then + FCOEMON_OPTS+=" --debug" +fi + +test -x $FCOEADM || { + echo "$FCOEADM not installed"; + if [ "$1" = "stop" ]; then exit 0; + else + rc_failed + rc_status -v + rc_exit + fi +} + +test -x $FCOEMON || { + echo "$FCOEMON not installed"; + if [ "$1" = "stop" ]; then exit 0; + else + rc_failed + rc_status -v + rc_exit + fi +} + +startup_fcoe_modules() +{ + modprobe -a $SUPPORTED_DRIVERS > /dev/null 2>&1 +} + +start() +{ + echo -n $"Starting FCoE initiator service: " + + pid=$($FCOEADM -p 2> /dev/null) + if [ -z "$pid" ] ; then + startup_fcoe_modules + + startproc -l ${LOG_FILE} ${FCOEMON} ${FCOEMON_OPTS} + else + echo -n "(already running)" + rc_reset + fi + rc_status -v +} + +stop() +{ + local force=$1 + + echo -n $"Stopping FCoE initiator service: " + pid=$($FCOEADM -p) + if [ "$force" == "force" ] + then + echo -n "Destroying any active fcoe interface/s" + [ "$pid" ] && kill -HUP $pid + rc_status -v + else + [ "$pid" ] && kill -TERM $pid + fi + + + rc_status -v +} + +status() +{ + echo -n "Checking status for fcoe service " + pid=$($FCOEADM -p 2> /dev/null) + if [ $? -eq 12 ]; then + rc_failed 3 + rc_status -v + elif [ -z "$pid" ] ; then + rc_failed 1 + rc_status -v + else + rc_reset + rc_status -v + interfaces=`$FCOEADM -i 2>&1 | \ + awk '/Symbolic Name:/{print $6}' | \ + sort | awk '{printf("%s ", $1)}'` + if [ -z "$interfaces" ]; then + echo "No interfaces created." + else + echo "Created interfaces: $interfaces" + fi + fi +} + +case "$1" in + start) + start + ;; + + stop) + stop $2 + ;; + + restart) + stop $2 + start + rc_status + ;; + + force-reload) + echo "force-reload not yet implemented" + ;; + + reload) + echo "reload not yet implemented" + rc_failed 3 + ;; + + status) + status + ;; + + *) + echo "Usage: $0 {start|stop [force]|status|restart [force]}" + exit 1 + ;; +esac +rc_exit diff --git a/etc/systemd/fcoe.service b/etc/systemd/fcoe.service new file mode 100644 index 0000000..5e5c8a2 --- /dev/null +++ b/etc/systemd/fcoe.service @@ -0,0 +1,14 @@ +[Unit] +Description=Open-FCoE initiator daemon +After=syslog.target network.target + +[Service] +Type=simple +EnvironmentFile=/etc/sysconfig/fcoe +ExecStartPre=/sbin/modprobe -qa $SUPPORTED_DRIVERS +ExecStart=/usr/sbin/fcoemon $FCOEMON_OPTS + +[Install] +WantedBy=multi-user.target +Also=lldpad.socket +Also=fcoemon.socket diff --git a/etc/systemd/fcoemon.socket b/etc/systemd/fcoemon.socket new file mode 100644 index 0000000..4de8715 --- /dev/null +++ b/etc/systemd/fcoemon.socket @@ -0,0 +1,6 @@ +[Socket] +ListenDatagram=@/com/intel/fcoemon +PassCredentials=true + +[Install] +WantedBy=sockets.target diff --git a/fcnsq.c b/fcnsq.c new file mode 100644 index 0000000..1597cd5 --- /dev/null +++ b/fcnsq.c @@ -0,0 +1,454 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef __u8 u8; +typedef __u16 u16; +typedef __u32 u32; +typedef __u64 u64; +#include +#include +#include "fc_gs.h" +#include "fc_ns.h" +#include "scsi_bsg_fc.h" + +static bool quiet = false; + +__attribute__((__format__(__printf__, 2, 3))) +static int print_result(const char *prefix, const char *format, ...) +{ + va_list ap; + int rc; + + va_start(ap, format); + if (!quiet) + printf("%s: ", prefix); + rc = vprintf(format, ap); + va_end(ap); + return rc; +} + +__attribute__((__format__(__printf__, 1, 2))) +static int print_err(const char *format, ...) +{ + va_list ap; + int rc; + + if (quiet) + return 0; + va_start(ap, format); + rc = vprintf(format, ap); + va_end(ap); + return rc; +} + +#define ntoh24(n) (u32) ((n)[0] << 16 | (n)[1] << 8 | (n)[2]) +#define hton24(h) { (h) >> 16 & 0xff, (h) >> 8 & 0xff, (h) & 0xff } + +static u64 ntohll(u64 netll) +{ + u8 *_netll = (u8 *)&netll; + return (u64) _netll[0] << (7 * 8) | + (u64) _netll[1] << (6 * 8) | + (u64) _netll[2] << (5 * 8) | + (u64) _netll[3] << (4 * 8) | + (u64) _netll[4] << (3 * 8) | + (u64) _netll[5] << (2 * 8) | + (u64) _netll[6] << (1 * 8) | + (u64) _netll[7]; +} + +static u64 htonll(u64 hostll) +{ + u64 netll; + u8 *_netll = (u8 *)&netll; + _netll[0] = hostll >> (7 * 8) & 0xff; + _netll[1] = hostll >> (6 * 8) & 0xff; + _netll[2] = hostll >> (5 * 8) & 0xff; + _netll[3] = hostll >> (4 * 8) & 0xff; + _netll[4] = hostll >> (3 * 8) & 0xff; + _netll[5] = hostll >> (2 * 8) & 0xff; + _netll[6] = hostll >> (1 * 8) & 0xff; + _netll[7] = hostll & 0xff; + return netll; +} + +static char* rjt_reason[] = { + [1] = "Invalid command code", + [2] = "Invalid version level", + [3] = "Logical error", + [4] = "Invalid CT_IU size", + [5] = "Logical busy", + [7] = "Protocol error", + [9] = "Unable to perform command request", + [0xB] = "Command not supported", + [0xD] = "Server not available", + [0xE] = "Session could not be established", +}; + +static char* rjt_explan[] = { + [0] = "No additional explanation", + [1] = "Port Identifier not registered", + [2] = "Port Name not registered", + [3] = "Node Name not registered", + [4] = "Class of Service not registered", + [6] = "Initial Process Associator not registered", + [7] = "FC-4 Types not registered", + [8] = "Symbolic Port Name not registered", + [9] = "Symbolic Node Name not registered", + [0xA] = "Port Type not registered", + [0xC] = "Fabric Port Name not registered", + [0xD] = "Hard Address not registered", + [0xF] = "FC-4 Features not registered", + [0x10] = "Access denied", + [0x11] = "Unacceptable Port Identifier", + [0x12] = "Data base empty", + [0x13] = "No object registered in the specified scope", + [0x14] = "Domain ID not present", + [0x15] = "Port number not present", + [0x16] = "No device attached", + [0xf0] = "Authorization Exception", + [0xf1] = "Authentication Exception", + [0xf2] = "Data base full", + [0xf3] = "Data base empty", + [0xf4] = "Processing request", + [0xf5] = "Unable to verify connection", + [0xf6] = "Devices not in a common zone", +}; + +static u16 ct_rjt(u8 reason, u8 explan) { + return (u16) reason << 8 | explan; +} + +static u8 ct_rjt_reason(u16 rjt) { + return (u8)(rjt >> 8); +} + +static u8 ct_rjt_explan(u16 rjt) { + return (u8) rjt & 0xff; +} + +static int ns_query(int bsg, void *req, int req_len, void *resp, int resp_len) +{ + char sense[96]; + + struct fc_bsg_request cdb = { + .msgcode = FC_BSG_HST_CT, + .rqst_data.h_ct = { + .port_id = hton24(0xfffffc), + } + }; + + struct sg_io_v4 sgio = { + .guard = 'Q', + .protocol = BSG_PROTOCOL_SCSI, + .subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT, + .request_len = sizeof(cdb), + .request = (uintptr_t) &cdb, + .dout_xfer_len = req_len, + .dout_xferp = (uintptr_t) req, + .din_xfer_len = resp_len, + .din_xferp = (uintptr_t) resp, + .max_response_len = sizeof(sense), + .response = (uintptr_t) &sense, + .timeout = 1000, + }; + + return ioctl(bsg, SG_IO, &sgio); +} + +static u16 gn_id(int bsg, u32 fcid, u16 cmd, u64 *wwn) +{ + struct { + struct fc_ct_hdr hdr; + u64 wwn; + } __attribute__((__packed__)) gn_resp; + + struct { + struct fc_ct_hdr hdr; + u8 resv; + u8 port_id[3]; + } __attribute__((__packed__)) gn = { + .hdr = { + .ct_rev = FC_CT_REV, + .ct_fs_type = FC_FST_DIR, + .ct_fs_subtype = FC_NS_SUBTYPE, + .ct_cmd = htons(cmd), + .ct_mr_size = htons(sizeof(gn_resp)), + .ct_vendor = 0, + }, + .port_id = hton24(fcid), + }; + + if (ns_query(bsg, &gn, sizeof(gn), &gn_resp, sizeof(gn_resp)) < 0) + return ~0; + if (gn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) + return ct_rjt(gn_resp.hdr.ct_reason, gn_resp.hdr.ct_explan); + *wwn = ntohll(gn_resp.wwn); + return 0; +} + +#define FC_NS_GPN_ID 0x0112 +static int gpn_id(int bsg, u32 fcid) +{ + u64 wwpn; + u16 rjt; + + rjt = gn_id(bsg, fcid, FC_NS_GPN_ID, &wwpn); + if (rjt) + goto fail; + print_result("Port Name", "%16.16jx\n", (uintmax_t)wwpn); + return 0; +fail: + if (rjt == (u16) ~0) + print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); + else + print_err("%s command failed: %s, %s\n", __func__, + rjt_reason[ct_rjt_reason(rjt)], + rjt_explan[ct_rjt_explan(rjt)]); + return rjt; +} + +#define FC_NS_GNN_ID 0x0113 +static int gnn_id(int bsg, u32 fcid) +{ + u64 wwnn; + u16 rjt; + + rjt = gn_id(bsg, fcid, FC_NS_GNN_ID, &wwnn); + if (rjt) + goto fail; + print_result("Node Name", "%16.16jx\n", (uintmax_t)wwnn); + return 0; +fail: + if (rjt == (u16) ~0) + print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); + else + print_err("%s command failed: %s, %s\n", __func__, + rjt_reason[ct_rjt_reason(rjt)], + rjt_explan[ct_rjt_explan(rjt)]); + return rjt; +} + +#define FC_NS_GSPN_ID 0x0118 +static int gspn_id(int bsg, u32 fcid) +{ + struct { + struct fc_ct_hdr hdr; + u8 len; + char name[255]; + } __attribute__((__packed__)) gspn_resp; + + struct { + struct fc_ct_hdr hdr; + u8 resv; + u8 port_id[3]; + } __attribute__((__packed__)) gspn = { + .hdr = { + .ct_rev = FC_CT_REV, + .ct_fs_type = FC_FST_DIR, + .ct_fs_subtype = FC_NS_SUBTYPE, + .ct_cmd = htons(FC_NS_GSPN_ID), + .ct_mr_size = htons(sizeof(gspn_resp)), + .ct_vendor = 0, + }, + .port_id = hton24(fcid), + }; + + if (ns_query(bsg, &gspn, sizeof(gspn), &gspn_resp, sizeof(gspn_resp)) < 0) { + print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); + return ~0; + } + if (gspn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) { + print_err("%s command failed: %s, %s\n", __func__, + rjt_reason[gspn_resp.hdr.ct_reason], + rjt_explan[gspn_resp.hdr.ct_explan]); + return ct_rjt(gspn_resp.hdr.ct_reason, gspn_resp.hdr.ct_explan); + } + print_result("Symbolic Port Name", "%s\n", gspn_resp.name); + return 0; +} + +#define FC_NS_GSNN_NN 0x0139 +static int gsnn_nn(int bsg, u64 wwnn) +{ + struct { + struct fc_ct_hdr hdr; + u8 len; + char name[255]; + } __attribute__((__packed__)) gsnn_resp; + + struct { + struct fc_ct_hdr hdr; + u64 wwnn; + } __attribute__((__packed__)) gsnn = { + .hdr = { + .ct_rev = FC_CT_REV, + .ct_fs_type = FC_FST_DIR, + .ct_fs_subtype = FC_NS_SUBTYPE, + .ct_cmd = htons(FC_NS_GSNN_NN), + .ct_mr_size = htons(sizeof(gsnn_resp)), + .ct_vendor = 0, + }, + .wwnn = htonll(wwnn), + }; + + if (ns_query(bsg, &gsnn, sizeof(gsnn), &gsnn_resp, sizeof(gsnn_resp)) < 0) { + print_err("%s ioctl failed: %s\n", __func__, strerror(errno)); + return ~0; + } + if (gsnn_resp.hdr.ct_cmd != htons(FC_FS_ACC)) { + print_err("%s command failed: %s, %s\n", __func__, + rjt_reason[gsnn_resp.hdr.ct_reason], + rjt_explan[gsnn_resp.hdr.ct_explan]); + return ct_rjt(gsnn_resp.hdr.ct_reason, gsnn_resp.hdr.ct_explan); + } + print_result("Symbolic Node Name", "%s\n", gsnn_resp.name); + return 0; +} + +enum commands { + NONE = 0, + GPN_ID, + GNN_ID, + GSPN_ID, + GSNN_NN, +}; + +static const struct option options[] = { + { "gpn", required_argument, NULL, GPN_ID }, + { "gnn", required_argument, NULL, GNN_ID }, + { "gspn", required_argument, NULL, GSPN_ID }, + { "gsnn", required_argument, NULL, GSNN_NN }, + { "quiet", no_argument, NULL, 'q' }, + { NULL, 0, NULL, 0 }, +}; + +static void help(int status) +{ + printf( + "Usage: fcnsq [options]\n" + "Commands:\n" + " --gpn \n" + " --gnn \n" + " --gspn \n" + " --gsnn \n" + "Options:\n" + " --quiet|-q print minimal results on success, and no error messages\n" + "\n" + "Port IDs and World Wide Names must be specified in hexadecimal.\n" + ); + exit(status); +} + +int main(int argc, char *argv[]) +{ + char *bsg; + int bsg_dev; + u32 port_id = 0; + u64 wwnn = 0; + int rc = 0; + enum commands cmd = 0; + char c; + uintmax_t wwnn_tmp = 0; + + while(1) { + c = getopt_long_only(argc, argv, "", options, NULL); + if (c < 0) + break; + switch(c) { + case '?': + help(-1); + break; + case 'q': + quiet = true; + break; + case GPN_ID: + case GNN_ID: + case GSPN_ID: + if (cmd) + help(-1); + cmd = c; + if (sscanf(optarg, "%x", &port_id) != 1) + help(-1); + break; + case GSNN_NN: + if (cmd) + help(-1); + cmd = c; + if (sscanf(optarg, "%jx", &wwnn_tmp) == 1) + wwnn = (u64)wwnn_tmp; + else + help(-1); + break; + } + } + + if (cmd == NONE) + help(-1); + + if (asprintf(&bsg, "/dev/bsg/fc_%s", argv[optind]) < 0) { + if (!quiet) + perror(NULL); + return -1; + } + bsg_dev = open(bsg, O_RDWR); + if (bsg_dev < 0) { + if (!quiet) + perror(bsg); + return -1; + } + switch (cmd) { + case GPN_ID: + rc = gpn_id(bsg_dev, port_id); + break; + case GNN_ID: + rc = gnn_id(bsg_dev, port_id); + break; + case GSPN_ID: + rc = gspn_id(bsg_dev, port_id); + break; + case GSNN_NN: + rc = gsnn_nn(bsg_dev, wwnn); + break; + default: + help(-1); + break; + }; + close(bsg_dev); + free(bsg); + return rc; +} + diff --git a/fcoe-utils.spec.in b/fcoe-utils.spec.in new file mode 100644 index 0000000..dff3fbd --- /dev/null +++ b/fcoe-utils.spec.in @@ -0,0 +1,70 @@ +Name: fcoe-utils +Version: @VERSION@ +Release: 1%{?dist} +Summary: Fibre Channel over Ethernet utilities + +Group: Applications/System +License: GPLv2 +URL: http://www.open-fcoe.org +Source0: http://www.open-fcoe.org/openfc/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +BuildRequires: libHBAAPI-devel lldpad-devel libpciaccess0 +Requires: lldpad +Requires(post): chkconfig +Requires(preun): chkconfig initscripts +Requires(postun): initscripts + +%description +Fibre Channel over Ethernet utilities +fcoeadm - command line tool for configuring FCoE interfaces +fcoemon - service to configure DCB Ethernet QOS filters, works with dcbd + +%prep +%setup -q + + +%build +%configure +make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT + + +%clean +rm -rf $RPM_BUILD_ROOT + + +%post +/sbin/chkconfig --add fcoe + +%preun +if [ $1 = 0 ]; then + /sbin/service fcoe stop + /sbin/chkconfig --del fcoe +fi + +%postun +if [ $1 = 1 ]; then + /sbin/service fcoe condrestart +fi + + +%files +%defattr(-,root,root,-) +%doc README +%doc COPYING +%{_sbindir}/* +%{_mandir}/man8/* +%{_sysconfdir}/fcoe +%config(noreplace) %{_sysconfdir}/fcoe/config +%{_sysconfdir}/init.d/fcoe + + +%changelog +* Mon Mar 2 2009 Chris Leech - 1.0.7-1 +- initial rpm build of fcoe tools + diff --git a/fcoe_clif.h b/fcoe_clif.h new file mode 100644 index 0000000..560f19e --- /dev/null +++ b/fcoe_clif.h @@ -0,0 +1,73 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOE_CLIF_H_ +#define _FCOE_CLIF_H_ + +/* + * A DCB file is incorrectly including linux/if.h which is redefining + * IFF_UP. This makes it so we cannot include net/if.h. We have to + * redefine IFNAMSIZ to work around this until DCB is corrected. + +*/ + +#define CLIF_IFNAME "fcm_clif" + +#define CLIF_PID_FILE _PATH_VARRUN "fcoemon.pid" + +#define CLIF_CMD_RESPONSE_TIMEOUT 30 +#define MAX_MSGBUF 512 + +enum clif_action { + CLIF_NONE = 0, + CLIF_CREATE_CMD, + CLIF_DESTROY_CMD, + CLIF_RESET_CMD, + CLIF_SCAN_CMD, + CLIF_PID_CMD +}; + +/** + * struct clif - Internal structure for client interface library + * + * This structure is used by fcoeadm client interface to store internal data. + */ +struct clif_sock_info { + int socket_fd; + struct sockaddr_un local; + struct sockaddr_un dest; +}; + +enum clif_flags { + CLIF_FLAGS_NONE = 0, + CLIF_FLAGS_FABRIC = 0x00, + CLIF_FLAGS_VN2VN = 0x01, + CLIF_FLAGS_MODE_MASK = 0x0F, /* Mask for mode encoding */ +}; + +/* + * Description of fcoemon and fcoeadm socket data structure interface + */ +struct clif_data { + enum clif_action cmd; + char ifname[IFNAMSIZ]; + enum clif_flags flags; +}; + +#endif /* _FCOE_CLIF_H_ */ diff --git a/fcoeadm.c b/fcoeadm.c new file mode 100644 index 0000000..776b4e3 --- /dev/null +++ b/fcoeadm.c @@ -0,0 +1,539 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fcoe_utils.h" +#include "fcoe_utils_version.h" +#include "fcoe_clif.h" +#include "fcoeadm_display.h" + +static const char optstring[] = "cdrSiftlm:sbhpv"; +static const struct option fcoeadm_opts[] = { + {"create", no_argument, 0, 'c'}, + {"destroy", no_argument, 0, 'd'}, + {"reset", no_argument, 0, 'r'}, + {"interface", no_argument, 0, 'i'}, + {"Scan", no_argument, 0, 'S'}, + {"fcf", no_argument, 0, 'f'}, + {"target", no_argument, 0, 't'}, + {"lun", no_argument, 0, 'l'}, + {"mode", required_argument, 0, 'm'}, + {"pid", no_argument, 0, 'p'}, + {"stats", no_argument, 0, 's'}, + {"lesb", no_argument, 0, 'b'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} +}; + +char progname[20]; + +static void fcoeadm_help(void) +{ + printf("Version %s\n", FCOE_UTILS_VERSION); + printf("Usage: %s\n" + "\t [-m|--mode fabric|vn2vn] [-c|--create] \n" + "\t [-d|--destroy] \n" + "\t [-r|--reset] \n" + "\t [-S|--Scan] \n" + "\t [-i|--interface] []\n" + "\t [-f]--fcf] []\n" + "\t [-t|--target] []\n" + "\t [-l|--lun] []\n" + "\t [-s|--stats] []\n" + "\t [-b|--lesb] []\n" + "\t [-p|--pid]\n" + "\t [-v|--version]\n" + "\t [-h|--help]\n\n", progname); +} + +static enum fcoe_status fcoeadm_clif_request(struct clif_sock_info *clif_info, + const struct clif_data *cmd, + size_t cmd_len, char *reply, + size_t *reply_len) +{ + struct timeval tv; + int ret; + fd_set rfds; + + if (send(clif_info->socket_fd, cmd, cmd_len, 0) < 0) + return ENOMONCONN; + + tv.tv_sec = CLIF_CMD_RESPONSE_TIMEOUT; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(clif_info->socket_fd, &rfds); + ret = select(clif_info->socket_fd + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(clif_info->socket_fd, &rfds)) { + ret = recv(clif_info->socket_fd, reply, *reply_len, 0); + if (ret < 0) + return EINTERR; + *reply_len = ret; + return SUCCESS; + } else { + return EINTERR; + } +} + +static int fcoeadm_request(struct clif_sock_info *clif_info, + struct clif_data *data) +{ + char rbuf[MAX_MSGBUF]; + size_t len; + int rc = SUCCESS; + + /* + * TODO: This is odd that we read the response code back as a + * string. We should just write the error code into a member + * of clif_data and then just read it directly. + */ + + len = MAX_MSGBUF - 1; + rc = fcoeadm_clif_request(clif_info, data, sizeof(struct clif_data), + rbuf, &len); + + if (!rc) { + rbuf[len] = '\0'; + rc = atoi(rbuf); + } + + return rc; +} + +static inline void fcoeadm_close_cli(struct clif_sock_info *clif_info) +{ + close(clif_info->socket_fd); +} + +/* + * Create fcoeadm client interface + */ +static enum fcoe_status fcoeadm_open_cli(struct clif_sock_info *clif_info) +{ + enum fcoe_status rc = SUCCESS; + struct sockaddr_un *lp; + socklen_t addrlen; + + clif_info->socket_fd = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (clif_info->socket_fd < 0) + return ENOMONCONN; + + lp = &clif_info->local; + memset(lp, 0, sizeof(*lp)); + lp->sun_family = AF_LOCAL; + lp->sun_path[0] = '\0'; + snprintf(&lp->sun_path[1], sizeof(lp->sun_path) - 1, + "%s/%lu", CLIF_IFNAME, (unsigned long int)getpid); + addrlen = sizeof(sa_family_t) + strlen(lp->sun_path + 1) + 1; + if (bind(clif_info->socket_fd, (struct sockaddr *)lp, addrlen) < 0) { + rc = ENOMONCONN; + goto err_close; + } + + clif_info->dest.sun_family = AF_LOCAL; + clif_info->dest.sun_path[0] = '\0'; + snprintf(&clif_info->dest.sun_path[1], + sizeof(clif_info->dest.sun_path) - 1, + "%s", CLIF_IFNAME); + addrlen = sizeof(sa_family_t) + strlen(clif_info->dest.sun_path + 1) + 1; + if (connect(clif_info->socket_fd, (struct sockaddr *)&clif_info->dest, + addrlen) < 0) { + rc = ENOMONCONN; + goto err_close; + } + + return rc; + +err_close: + close(clif_info->socket_fd); + return rc; +} + +/* + * Send request to fcoemon + */ +static enum fcoe_status +fcoeadm_action(enum clif_action cmd, char *ifname, enum clif_flags flags) +{ + struct clif_data data; + struct clif_sock_info clif_info; + int rc; + + if (ifname) + strncpy(data.ifname, ifname, sizeof(data.ifname)); + else + data.ifname[0] = '\0'; + data.cmd = cmd; + data.flags = flags; + + rc = fcoeadm_open_cli(&clif_info); + if (!rc) { + rc = fcoeadm_request(&clif_info, &data); + if (rc > 0 && cmd == CLIF_PID_CMD) { + printf("%d\n", rc); + rc = 0; + } + fcoeadm_close_cli(&clif_info); + } + + return rc; +} + +#define MAX_ARG_LEN 32 + +/* + * getopts_long(3) does not handle optional arguments + * correctly. It will not allow a ' ' between the option + * and its argument. For required arguments the user can + * specify, '-i X' or '-iX' but with optional arguments + * only the first style is valid. + * + * This is being worked around by making '-i/-t/-l' have + * no arguments, but then process any following argv + * elements. + */ +int main(int argc, char *argv[]) +{ + enum clif_action cmd = CLIF_NONE; + enum fcoe_status rc = SUCCESS; + enum clif_flags flags = CLIF_FLAGS_NONE; + int opt, stat_interval; + int op = -1; + char *ifname = NULL; + + /* + * This has to be first because the error print macro + * expects progname to be valid. + */ + strncpy(progname, basename(argv[0]), sizeof(progname)); + + /* check if we have sysfs */ + if (fcoe_checkdir(SYSFS_MOUNT)) { + rc = ENOSYSFS; + goto err; + } + + for (;;) { + opt = getopt_long(argc, argv, optstring, fcoeadm_opts, NULL); + if (opt < 0) + break; + switch (opt) { + case 'm': + if (strcasecmp(optarg, "vn2vn") == 0) { + flags &= ~CLIF_FLAGS_MODE_MASK; + flags |= CLIF_FLAGS_VN2VN; + } else if (strcasecmp(optarg, "fabric") == 0) { + flags &= ~CLIF_FLAGS_MODE_MASK; + } else { + rc = EINVALARG; + } + break; + + default: + if (op == -1) + op = opt; + else + rc = EINVALARG; + break; + + case '?': + rc = EIGNORE; + break; + } + } + + if (op == -1) + fcoeadm_help(); + else if (rc == SUCCESS) { + switch (op) { + case 'd': + cmd = CLIF_DESTROY_CMD; + flags = 0; /* No flags allowed on destroy yet */ + /* fall through */ + case 'c': + if (cmd == CLIF_NONE) + cmd = CLIF_CREATE_CMD; + + if (argc - optind != 1) { + rc = EBADNUMARGS; + break; + } + + ifname = argv[optind]; + rc = fcoeadm_action(cmd, ifname, flags); + break; + case 'r': + cmd = CLIF_RESET_CMD; + /* fall through */ + case 'S': + if (cmd == CLIF_NONE) + cmd = CLIF_SCAN_CMD; + + if (argc - optind != 1) { + rc = EBADNUMARGS; + break; + } + + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + if (!rc) + rc = fcoeadm_action(cmd, ifname, flags); + break; + + case 'i': + if (argc - optind > 1) { + rc = EBADNUMARGS; + break; + } + + /* + * If there's an additional argument + * treat it as the interface name. + */ + if (optind != argc) { + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + } + + if (!rc) + rc = display_adapter_info(ifname); + break; + + case 'f': + if (argc - optind > 1) { + rc = EBADNUMARGS; + break; + } + + /* + * If there's an aditional argument + * treat it as the interface name. + */ + if (optind != argc) { + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + } + + if (!rc) + rc = display_fcf_info(ifname); + break; + + case 't': + if (argc - optind > 1) { + rc = EBADNUMARGS; + break; + } + + /* + * If there's an aditional argument + * treat it as the interface name. + */ + if (optind != argc) { + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + } + + if (!rc) + rc = display_target_info(ifname, DISP_TARG); + break; + + case 'l': + if (argc - optind > 1) { + rc = EBADNUMARGS; + break; + } + + /* + * If there's an aditional argument + * treat it as the interface name. + */ + if (optind != argc) { + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + } + + if (!rc) + rc = display_target_info(ifname, DISP_LUN); + break; + + case 's': + if (argc - optind > 2) { + rc = EBADNUMARGS; + break; + } + + if (optind != argc) { + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + } + + if (!rc && ++optind != argc) { + stat_interval = atoi(argv[optind]); + if (stat_interval <= 0) + rc = EINVALARG; + } else if (!rc && optind == argc) + stat_interval = DEFAULT_STATS_INTERVAL; + + if (!rc) + rc = display_port_stats(ifname, stat_interval); + break; + case 'p': + rc = fcoeadm_action(CLIF_PID_CMD, NULL, flags); + break; + + case 'b': + if (argc - optind > 2) { + rc = EBADNUMARGS; + break; + } + + if (optind != argc) { + ifname = argv[optind]; + rc = fcoe_validate_fcoe_conn(ifname); + } + + if (!rc && ++optind != argc) { + stat_interval = atoi(argv[optind]); + if (stat_interval <= 0) + rc = EINVALARG; + } else if (!rc && optind == argc) + stat_interval = DEFAULT_STATS_INTERVAL; + + if (!rc) + rc = display_port_lesb_stats(ifname, + stat_interval); + break; + + case 'v': + if (argc - optind != 0) { + rc = EBADNUMARGS; + break; + } + + printf("%s\n", FCOE_UTILS_VERSION); + break; + + case 'h': + if (argc - optind != 0) { + rc = EBADNUMARGS; + break; + } + + fcoeadm_help(); + break; + + case '?': + rc = EIGNORE; + break; + } + } + +err: + if (rc) { + switch (rc) { + case EFAIL: + FCOE_LOG_ERR("Command failed\n"); + break; + + case ENOACTION: + FCOE_LOG_ERR("No action was taken\n"); + break; + + case EFCOECONN: + FCOE_LOG_ERR("Connection already created on " + "interface %s\n", ifname); + break; + + case ENOFCOECONN: + case ENOFCHOST: + FCOE_LOG_ERR("No connection created on " + "interface %s\n", ifname); + break; + + case EINVALARG: + FCOE_LOG_ERR("Invalid argument\n"); + break; + + case EBADNUMARGS: + /* + * Overloading E2BIG for too many argumets + * and too few arguments. + */ + FCOE_LOG_ERR("Incorrect number of arguments\n"); + break; + + case EIGNORE: + /* + * getopt_long will print the initial error, just break + * through to get the --help suggestion. + */ + break; + + case ENOETHDEV: + FCOE_LOG_ERR("Invalid interface name %s\n", ifname); + break; + + case ENOSYSFS: + FCOE_LOG_ERR("sysfs not mounted\n"); + break; + + case ENOMONCONN: + FCOE_LOG_ERR("Could not connect to fcoemon\n"); + break; + + case ECONNTMOUT: + FCOE_LOG_ERR("Connection to fcoemon timed out\n"); + break; + + case EHBAAPIERR: + FCOE_LOG_ERR("libHBAAPI or libhbalinux error\n"); + break; + + case EINTERR: + FCOE_LOG_ERR("Internal error\n"); + break; + + case EBADCLIFMSG: + FCOE_LOG_ERR("Messaging error\n"); + break; + + default: + /* + * This will catch EOPNOTSUPP which should never happen + */ + FCOE_LOG_ERR("Unknown error code %d\n", rc); + break; + } + + fprintf(stderr, "Try \'%s --help\' for more information.\n", + progname); + } + + return rc; +} diff --git a/fcoeadm_display.c b/fcoeadm_display.c new file mode 100644 index 0000000..120c608 --- /dev/null +++ b/fcoeadm_display.c @@ -0,0 +1,931 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net_types.h" +#include "fc_types.h" +#include "fc_scsi.h" +#include "fcoeadm_display.h" +#include "fcoe_utils.h" +#include "fcoemon_utils.h" +#include "libopenfcoe.h" +#include "sysfs_hba.h" + +/* Define FC4 Type */ +#define FC_TYPE_FCP 0x08 /* SCSI FCP */ + +/* Constant defined in fcoe_def.h of fcoe driver */ +#define FCOE_WORD_TO_BYTE 4 + +/* Minimum byte size of the received inquiry data */ +#define MIN_INQ_DATA_SIZE 36 + +#define FCP_TARG_STR "FCP Target" + +#define SYSFS_HOST_DIR "/sys/class/fc_host" + +/* + * Options for displaying target/LUN info. + */ +struct target_info_arguments { + char *ifname; + enum disp_style style; +}; + +struct sa_nameval port_states[] = { + { "Not Present", HBA_PORTSTATE_UNKNOWN }, + { "Online", HBA_PORTSTATE_ONLINE }, + { "Offline", HBA_PORTSTATE_OFFLINE }, + { "Blocked", HBA_PORTSTATE_UNKNOWN }, + { "Bypassed", HBA_PORTSTATE_BYPASSED }, + { "Diagnostics", HBA_PORTSTATE_DIAGNOSTICS }, + { "Linkdown", HBA_PORTSTATE_LINKDOWN }, + { "Error", HBA_PORTSTATE_ERROR }, + { "Loopback", HBA_PORTSTATE_LOOPBACK }, + { "Deleted", HBA_PORTSTATE_UNKNOWN }, + { NULL, 0 } +}; + + +/* + * table of /sys port speed strings to HBA-API values. + */ +struct sa_nameval port_speeds[] = { + { "Unknown", HBA_PORTSPEED_UNKNOWN }, + { "1 Gbit", HBA_PORTSPEED_1GBIT }, + { "2 Gbit", HBA_PORTSPEED_2GBIT }, + { "4 Gbit", HBA_PORTSPEED_4GBIT }, + { "10 Gbit", HBA_PORTSPEED_10GBIT }, + { "8 Gbit", HBA_PORTSPEED_8GBIT }, + { "16 Gbit", HBA_PORTSPEED_16GBIT }, + { "32 Gbit", HBA_PORTSPEED_32GBIT }, + { "20 Gbit", HBA_PORTSPEED_20GBIT }, + { "40 Gbit", HBA_PORTSPEED_40GBIT }, + { "Not Negotiated", HBA_PORTSPEED_NOT_NEGOTIATED }, + { NULL, 0 } +}; + +static int is_fcp_target(struct port_attributes *rp_info) +{ + if (!strncmp(rp_info->roles, FCP_TARG_STR, strlen(FCP_TARG_STR))) + return 0; + + return -EINVAL; +} + +static void show_hba_info(struct hba_info *hba_info) +{ + printf(" Description: %s\n", hba_info->model_description); + printf(" Revision: %s\n", hba_info->hardware_version); + printf(" Manufacturer: %s\n", hba_info->manufacturer); + printf(" Serial Number: %s\n", hba_info->serial_number); + printf(" Driver: %s %s\n", hba_info->driver_name, + hba_info->driver_version); + printf(" Number of Ports: %d\n", hba_info->nports); + printf("\n"); +} + +static void show_port_info(struct port_attributes *lp_info) +{ + printf(" Symbolic Name: %s\n", + lp_info->symbolic_name); + + printf(" OS Device Name: %s\n", + lp_info->device_name); + + printf(" Node Name: %s\n", + lp_info->node_name); + + printf(" Port Name: %s\n", + lp_info->port_name); + + printf(" Fabric Name: %s\n", + lp_info->fabric_name); + + printf(" Speed: %s\n", + lp_info->speed); + + printf(" Supported Speed: %s\n", + lp_info->supported_speeds); + + printf(" MaxFrameSize: %s\n", + lp_info->maxframe_size); + + printf(" FC-ID (Port ID): %s\n", + lp_info->port_id); + + printf(" State: %s\n", + lp_info->port_state); + /* TODO: Display PortSupportedFc4Types and PortActiveFc4Types */ +} + +static void show_target_info(const char *symbolic_name, + struct port_attributes *rp_info) +{ + char *ifname; + + ifname = get_ifname_from_symbolic_name(symbolic_name); + + printf(" Interface: %s\n", ifname); + printf(" Roles: %s\n", rp_info->roles); + printf(" Node Name: %s\n", rp_info->node_name); + printf(" Port Name: %s\n", rp_info->port_name); + printf(" Target ID: %s\n", rp_info->scsi_target_id); + printf(" MaxFrameSize: %s\n", rp_info->maxframe_size); + printf(" OS Device Name: %s\n", rp_info->device_name); + printf(" FC-ID (Port ID): %s\n", rp_info->port_id); + printf(" State: %s\n", rp_info->port_state); + printf("\n"); +} + +static void +show_short_lun_info_header(void) +{ + printf(" LUN ID Device Name Capacity " + "Block Size Description\n"); + printf(" ------ ----------- ---------- ---------- " + "----------------------------\n"); +} + +static void sa_dir_crawl(char *dir_name, + void (*func)(char *dirname, enum disp_style style), + enum disp_style style) +{ + DIR *dir; + struct dirent *dp; + void (*f)(char *dirname, enum disp_style style); + char path[1024]; + + f = func; + + dir = opendir(dir_name); + if (!dir) + return; + + while ((dp = readdir(dir)) != NULL) { + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + snprintf(path, sizeof(path), "%s/%s", dir_name, dp->d_name); + + f(path, style); + } + closedir(dir); +} + +static char *format_capstr(uint64_t size, unsigned int blksize) +{ + double cap_abbr; + char *capstr; + uint64_t cap; + char *abbr; + int ret; + + cap = size * blksize; + + cap_abbr = cap / (1024.0 * 1024.0); + abbr = "MiB"; + if (cap_abbr >= 1024) { + cap_abbr /= 1024.0; + abbr = "GiB"; + } + if (cap_abbr >= 1024) { + cap_abbr /= 1024.0; + abbr = "TiB"; + } + if (cap_abbr >= 1024) { + cap_abbr /= 1024.0; + abbr = "PiB"; + } + + ret = asprintf(&capstr, "%0.2f %s", cap_abbr, abbr); + if (ret == -1) + return "Unknown"; + + return capstr; +} + +static void show_full_lun_info(unsigned int hba, unsigned int port, + unsigned int tgt, unsigned int lun) +{ + char vendor[256]; + char model[256]; + char rev[256]; + char *osname; + char *capstr; + uint64_t lba = 0; + uint32_t blksize = 0; + char path[1024]; + char npath[1024]; + DIR *dir; + struct dirent *dp; + struct port_attributes *rport_attrs; + struct port_attributes *port_attrs; + + snprintf(path, sizeof(path), + "/sys/class/scsi_device/%u:%u:%u:%u", + hba, port, tgt, lun); + + rport_attrs = get_rport_attribs_by_device(path); + if (!rport_attrs) + return; + + port_attrs = get_port_attribs_by_device(path); + if (!port_attrs) + goto free_rport; + + strncat(path, "/device/", sizeof(path) - strlen(path) - 1); + + sa_sys_read_line(path, "rev", rev, sizeof(rev)); + sa_sys_read_line(path, "model", model, sizeof(model)); + sa_sys_read_line(path, "vendor", vendor, sizeof(vendor)); + + strncat(path, "block", sizeof(path) - strlen(path) - 1); + + dir = opendir(path); + if (!dir) + goto free_port; + + while ((dp = readdir(dir)) != NULL) { + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + + + osname = dp->d_name; + + snprintf(npath, sizeof(npath), "%s/%s/", path, osname); + sa_sys_read_u64(npath, "size", &lba); + + snprintf(npath, sizeof(npath), "%s/%s/queue/", path, osname); + sa_sys_read_u32(npath, "hw_sector_size", &blksize); + } + + closedir(dir); + + /* Show lun info */ + printf(" LUN #%d Information:\n", lun); + printf(" OS Device Name: %s\n", + osname); + printf(" Description: %s %s (rev %s)\n", + vendor, model, rev); + printf(" Ethernet Port FCID: %s\n", + port_attrs->port_id); + printf(" Target FCID: %s\n", + rport_attrs->port_id); + if (tgt == 0xFFFFFFFFU) + printf(" Target ID: (None)\n"); + else + printf(" Target ID: %u\n", tgt); + printf(" LUN ID: %d\n", lun); + + capstr = format_capstr(lba, blksize); + printf(" Capacity: %s\n", capstr); + printf(" Capacity in Blocks: %" PRIu64 "\n", lba); + printf(" Block Size: %" PRIu32 " bytes\n", blksize); + printf(" Status: Attached\n"); + + printf("\n"); + +free_rport: + free(rport_attrs); +free_port: + free(port_attrs); +} + +static void show_short_lun_info(unsigned int hba, unsigned int port, + unsigned int tgt, unsigned int lun) +{ + struct dirent *dp; + char vendor[256]; + char path[1024]; + char npath[1024]; + char model[256]; + char rev[256]; + DIR *dir; + uint32_t blksize = 0; + char *capstr = "Unknown"; + char *osname = "Unknown"; + uint64_t size; + + snprintf(path, sizeof(path), + "/sys/class/scsi_device/%u:%u:%u:%u/device/", + hba, port, tgt, lun); + + sa_sys_read_line(path, "rev", rev, sizeof(rev)); + sa_sys_read_line(path, "model", model, sizeof(model)); + sa_sys_read_line(path, "vendor", vendor, sizeof(vendor)); + + strncat(path, "block", sizeof(path) - strlen(path) - 1); + + dir = opendir(path); + if (!dir) + return; + + while ((dp = readdir(dir)) != NULL) { + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + + + osname = dp->d_name; + + snprintf(npath, sizeof(npath), "%s/%s/", path, osname); + sa_sys_read_u64(npath, "size", &size); + + snprintf(npath, sizeof(npath), "%s/%s/queue/", path, osname); + sa_sys_read_u32(npath, "hw_sector_size", &blksize); + } + + closedir(dir); + + capstr = format_capstr(size, blksize); + + /* Show the LUN info */ + printf("%10d %-11s %10s %7d %s %s (rev %s)\n", + lun, osname, + capstr, blksize, + vendor, model, rev); + + free(capstr); + return; +} + +static void list_scsi_device(char *d_name, enum disp_style style) +{ + unsigned int port; + unsigned int hba; + unsigned int tgt; + unsigned int lun; + char *last; + + last = strrchr(d_name, '/'); + + if (sscanf(last, "/%u:%u:%u:%u", &hba, &port, &tgt, &lun) != 4) + return; + + + if (style == DISP_TARG) + show_short_lun_info(hba, port, tgt, lun); + else + show_full_lun_info(hba, port, tgt, lun); +} + +static void search_rport_targets(char *d_name, enum disp_style style) +{ + if (!strstr(d_name, "target")) + return; + + sa_dir_crawl(d_name, list_scsi_device, style); +} + +static void list_luns_by_rport(char *rport, enum disp_style style) +{ + char path[1024]; + char link[1024]; + char *substr; + int len; + int ret; + + snprintf(path, sizeof(path), "/sys/class/fc_remote_ports/%s", rport); + + ret = readlink(path, link, sizeof(link)); + if (ret == -1) + return; + + if (link[ret] != '\0') + link[ret] = '\0'; + + substr = strstr(link, "net"); + snprintf(path, sizeof(path), "/sys/class/%s", substr); + + substr = strstr(path, "fc_remote_ports"); + + len = strlen(path) - strlen(substr); + path[len] = '\0'; + + sa_dir_crawl(path, search_rport_targets, style); +} + +static void scan_device_map(char *port, char *rport, enum disp_style style) +{ + if (style == DISP_TARG) + show_short_lun_info_header(); + + list_luns_by_rport(rport, style); + + /* Newline at the end of the short lun report */ + if (style == DISP_TARG) + printf("\n"); +} + +static void show_port_stats_header(const char *ifname, int interval) +{ + printf("\n"); + printf("%-7s interval: %-2d Err Inv " + "IvTx Link Cntl Input Input Output Output\n", + ifname, interval); + printf("Seconds TxFrames TxBytes RxFrames RxBytes " + "Frms CRC Byte Fail Reqs Requests MBytes " + "Requests MBytes\n"); + printf("------- --------- ------------ --------- -------------- " + "---- ---- ---- ---- ---- --------- --------- " + "--------- ---------\n"); +} + +static void +show_port_stats_in_row(uint64_t start_time, + struct port_statistics *port_stats) + +{ + printf("%-7"PRIu64" ", + port_stats->seconds_since_last_reset - start_time); + printf("%-9"PRIu64" ", port_stats->tx_frames); + printf("%-12"PRIu64" ", port_stats->tx_words * FCOE_WORD_TO_BYTE); + printf("%-9"PRIu64" ", port_stats->rx_frames); + printf("%-14"PRIu64" ", port_stats->rx_words * FCOE_WORD_TO_BYTE); + printf("%-4"PRIu64" ", port_stats->error_frames); + printf("%-4"PRIu64" ", port_stats->invalid_crc_count); + printf("%-4"PRIu64" ", + port_stats->invalid_tx_word_count * FCOE_WORD_TO_BYTE); + printf("%-4"PRIu64" ", port_stats->link_failure_count); + printf("%-4"PRIu64" ", port_stats->fcp_control_requests); + printf("%-9"PRIu64" ", port_stats->fcp_input_requests); + printf("%-9"PRIu64" ", port_stats->fcp_input_megabytes); + printf("%-9"PRIu64" ", port_stats->fcp_output_requests); + printf("%-9"PRIu64" ", port_stats->fcp_output_megabytes); + printf("\n"); +} + +enum fcoe_status display_port_stats(const char *ifname, int interval) +{ + struct port_statistics *port_stats; + enum fcoe_status rc = EINTERR; + uint64_t start_time = 0; + char *host; + int i, num_hbas; + + num_hbas = get_number_of_adapters(); + if (num_hbas < 0) + return rc; + + host = get_host_from_netdev(ifname); + if (!host) + return rc; + + i = 0; + while (1) { + unsigned int secs_left; + + port_stats = get_port_statistics(host); + if (!port_stats) + goto free_host; + + + if (!start_time) + start_time = port_stats->seconds_since_last_reset; + + if (!(i % 52)) + show_port_stats_header(ifname, interval); + + show_port_stats_in_row(start_time, port_stats); + i++; + + /* wait for the requested time interval in seconds */ + secs_left = interval; + do { + secs_left = sleep(secs_left); + } while (secs_left); + } + + rc = SUCCESS; + free(port_stats); + +free_host: + free(host); + + return rc; +} + +static int get_host_from_vport(struct dirent *dp, void *arg) +{ + if (!strncmp(dp->d_name, "host", strlen("host"))) { + struct port_attributes *port_attrs; + + port_attrs = get_port_attribs(dp->d_name); + if (!port_attrs) + return 0; + printf("\n"); + show_port_info(port_attrs); + free(port_attrs); + } + + return 0; +} + +static int crawl_vports(struct dirent *dp, void *arg) +{ + char *oldpath = arg; + + if (!strncmp(dp->d_name, "vport", strlen("vport"))) { + char path[1024]; + + snprintf(path, sizeof(path), "%s/%s", oldpath, dp->d_name); + sa_dir_read(path, get_host_from_vport, NULL); + } + return 0; +} + +static void show_host_vports(const char *host) +{ + char path[1024]; + + snprintf(path, sizeof(path), "%s/%s/device/", SYSFS_HOST_DIR, host); + sa_dir_read(path, crawl_vports, path); + +} + +static enum fcoe_status display_one_adapter_info(char *ifname) +{ + struct port_attributes *port_attrs; + struct hba_info *hba_info; + enum fcoe_status rc = EINTERR; + char *pcidev; + char *host; + + pcidev = get_pci_dev_from_netdev(ifname); + if (!pcidev) + return rc; + + host = get_host_from_netdev(ifname); + if (!host) + goto free_pcidev; + + hba_info = get_hbainfo_by_pcidev(pcidev); + if (!hba_info) + goto free_host; + + port_attrs = get_port_attribs(host); + if (!port_attrs) + goto free_hba_info; + + /* + * Display the adapter header. + */ + show_hba_info(hba_info); + show_port_info(port_attrs); + show_host_vports(host); + + rc = SUCCESS; + + free(port_attrs); +free_hba_info: + free(hba_info); +free_host: + free(host); +free_pcidev: + free(pcidev); + return rc; +} + +static int search_fc_adapter(struct dirent *dp, void *arg) +{ + display_one_adapter_info(dp->d_name); + return 0; +} + +enum fcoe_status display_adapter_info(char *ifname) +{ + enum fcoe_status rc = SUCCESS; + int num_hbas; + int err; + + if (ifname) + return display_one_adapter_info(ifname); + + num_hbas = get_number_of_adapters(); + if (!num_hbas) + return ENOACTION; + + err = sa_dir_read("/sys/class/net/", search_fc_adapter, NULL); + if (err) + return EINTERR; + + return rc; +} + + +static char *get_ifname_from_rport(char *rport) +{ + char link[1024]; + char ifname[32]; + ssize_t ret; + char *path; + char *offs; + int err; + int i = 0; + + err = asprintf(&path, "%s/%s", "/sys/class/fc_remote_ports", rport); + if (err == -1) + return NULL; + + ret = readlink(path, link, sizeof(link)); + free(path); + if (ret == -1) + return NULL; + + if (link[ret] != '\0') + link[ret] = '\0'; + + offs = strstr(link, "/net/"); + if (!offs) + return NULL; + + offs = offs + 5; + + for (i = 0; offs[i] != '\0'; i++) + if (offs[i] == '/') + break; + + strncpy(ifname, offs, i); + if (ifname[i] != '\0') + ifname[i] = '\0'; + + return strdup(ifname); +} + +static enum fcoe_status display_one_target_info(char *ifname, char *rport, + enum disp_style style) +{ + struct port_attributes *rport_attrs; + struct port_attributes *port_attrs; + enum fcoe_status rc = SUCCESS; + char *host; + + rport_attrs = get_rport_attribs(rport); + if (!rport_attrs) + return EINTERR; + + /* + * Skip any targets that are not FCP targets + */ + if (is_fcp_target(rport_attrs)) + goto free_rport_attribs; + + rc = EINTERR; + host = get_host_from_netdev(ifname); + if (!host) + goto free_rport_attribs; + + port_attrs = get_port_attribs(host); + if (!port_attrs) + goto free_host; + + show_target_info(port_attrs->symbolic_name, + rport_attrs); + + if (strncmp(port_attrs->port_state, "Online", 6)) + goto free_port_attribs; + + /* + * This will print the LUN table + * under the target. + */ + scan_device_map(ifname, rport, style); + +free_port_attribs: + free(port_attrs); +free_host: + free(host); +free_rport_attribs: + free(rport_attrs); + + return rc; +} + +static bool rport_is_child(const char *rport, const char *ifname) +{ + + char link[1024]; + ssize_t ret; + char *path; + char *offs; + int err; + + err = asprintf(&path, "%s/%s", "/sys/class/fc_remote_ports", rport); + if (err == -1) + return false; + + ret = readlink(path, link, sizeof(link)); + free(path); + if (ret == -1) + return false; + + offs = strstr(link, ifname); + + return offs ? true : false; +} + +static int search_rports(struct dirent *dp, void *arg) +{ + struct target_info_arguments *ta; + bool allocated = false; /* ifname is malloc()ed? */ + char *ifname; + char *rport; + + + ta = arg; + rport = dp->d_name; + ifname = ta->ifname; + + if (ifname) { + bool child; + + child = rport_is_child(rport, ifname); + if (!child) + return 0; + } else { + ifname = get_ifname_from_rport(rport); + if (!ifname) + return 0; + allocated = true; + } + + display_one_target_info(ifname, rport, ta->style); + + if (allocated) + free(ifname); + + return 0; +} + +enum fcoe_status display_target_info(char *ifname, + enum disp_style style) +{ + struct target_info_arguments args; + + args.ifname = ifname; + args.style = style; + + sa_dir_read("/sys/class/fc_remote_ports/", search_rports, (void *) &args); + + return SUCCESS; + +} + +static struct sa_table fcoe_ctlr_table; + +static void print_fcoe_fcf_device(void *ep, UNUSED void *arg) +{ + struct fcoe_fcf_device *fcf = (struct fcoe_fcf_device *)ep; + char temp[MAX_STR_LEN]; + char mac[MAX_STR_LEN]; + int len = sizeof(temp); + const char *buf; + + printf("\n"); + printf(" FCF #%u Information\n", fcf->index); + buf = sa_enum_decode(temp, len, fcf_state_table, fcf->state); + if (!buf) + buf = temp; + printf(" Connection Mode: %s\n", buf); + printf(" Fabric Name: 0x%016" PRIx64 "\n", fcf->fabric_name); + printf(" Switch Name 0x%016" PRIx64 "\n", fcf->switch_name); + mac2str(fcf->mac, mac, MAX_STR_LEN); + printf(" MAC Address: %s\n", mac); + printf(" FCF Priority: %u\n", fcf->priority); + printf(" FKA Period: %u seconds\n", fcf->fka_period); + printf(" Selected: "); + (fcf->selected == 1) ? printf("Yes\n") : printf("No\n"); + printf(" VLAN ID: %u\n", fcf->vlan_id); + printf("\n"); +} + +static void print_interface_fcoe_fcf_device(void *ep, void *arg) +{ + struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)ep; + const char *ifname = arg; + const char *buf; + char temp[MAX_STR_LEN]; + int len = sizeof(temp); + + if (!ifname || !strncmp(ifname, ctlr->ifname, IFNAMSIZ)) { + printf(" Interface: %s\n", ctlr->ifname); + buf = sa_enum_decode(temp, len, fip_conn_type_table, + ctlr->mode); + if (!buf) + buf = temp; + printf(" Connection Type: %s\n", buf); + + sa_table_iterate(&ctlr->fcfs, print_fcoe_fcf_device, NULL); + } +} + +/* + * NULL ifname indicates to dispaly all fcfs + */ +enum fcoe_status display_fcf_info(const char *ifname) +{ + enum fcoe_status rc = SUCCESS; + + sa_table_init(&fcoe_ctlr_table); + read_fcoe_ctlr(&fcoe_ctlr_table); + + sa_table_iterate(&fcoe_ctlr_table, print_interface_fcoe_fcf_device, + (void *)ifname); + sa_table_iterate(&fcoe_ctlr_table, free_fcoe_ctlr_device, NULL); + + return rc; +} + +static void print_interface_fcoe_lesb_stats(void *ep, void *arg) +{ + struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)ep; + const char *ifname = arg; + + if (!ifname || !strncmp(ifname, ctlr->ifname, IFNAMSIZ)) { + printf("%-8u ", ctlr->lesb_link_fail); + printf("%-9u ", ctlr->lesb_vlink_fail); + printf("%-7u ", ctlr->lesb_miss_fka); + printf("%-7u ", ctlr->lesb_symb_err); + printf("%-9u ", ctlr->lesb_err_block); + printf("%-9u ", ctlr->lesb_fcs_error); + printf("\n"); + } +} + +static void +print_interface_fcoe_lesb_stats_header(const char *ifname, int interval) +{ + printf("\n"); + printf("%-7s interval: %-2d\n", ifname, interval); + printf("LinkFail VLinkFail MissFKA SymbErr ErrBlkCnt FCSErrCnt\n"); + printf("-------- --------- ------- ------- --------- ---------\n"); +} + +enum fcoe_status display_port_lesb_stats(const char *ifname, + int interval) +{ + enum fcoe_status rc = SUCCESS; + int i = 0; + + while (1) { + unsigned int secs_left; + + sa_table_init(&fcoe_ctlr_table); + read_fcoe_ctlr(&fcoe_ctlr_table); + + if (!(i % 52)) + print_interface_fcoe_lesb_stats_header(ifname, + interval); + + sa_table_iterate(&fcoe_ctlr_table, + print_interface_fcoe_lesb_stats, + (void *)ifname); + + sa_table_iterate(&fcoe_ctlr_table, + free_fcoe_ctlr_device, NULL); + + i++; + + secs_left = interval; + do { + secs_left = sleep(secs_left); + } while (secs_left); + } + + return rc; +} diff --git a/fcoeadm_display.h b/fcoeadm_display.h new file mode 100644 index 0000000..689c6bc --- /dev/null +++ b/fcoeadm_display.h @@ -0,0 +1,39 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOEADM_DISPLAY_H_ +#define _FCOEADM_DISPLAY_H_ + +#define DEFAULT_STATS_INTERVAL 1 + +enum disp_style { + DISP_LUN = 0, + DISP_TARG, +}; + +enum fcoe_status display_adapter_info(char *ifname); +enum fcoe_status display_fcf_info(const char *ifname); +enum fcoe_status display_target_info(char *ifname, + enum disp_style style); +enum fcoe_status display_port_stats(const char *ifname, + int stat_interval); +enum fcoe_status display_port_lesb_stats(const char *ifname, + int stat_interval); + +#endif /* _FCOEADM_DISPLAY_H_ */ diff --git a/fcoemon.c b/fcoemon.c new file mode 100644 index 0000000..9a400c5 --- /dev/null +++ b/fcoemon.c @@ -0,0 +1,3872 @@ +/* + * Copyright(c) 2010-2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#define __STDC_FORMAT_MACROS 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "scsi_netlink_fc.h" +#include "fcoe_utils_version.h" +#include "fcoemon_utils.h" +#include "fcoemon.h" +#include "fcoe_clif.h" +#include "fcoe_utils.h" +#include "sysfs_hba.h" +#include "strarr.h" + +#include "fip.h" +#include "rtnetlink.h" + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + +#define CONFIG_DIR SYSCONFDIR "/fcoe" +#define CONFIG_MIN_VAL_LEN (1 + 2) +#define CONFIG_MAX_VAL_LEN (20 + 2) +#define DCB_APP_0_DEFAULT_ENABLE 1 +#define DCB_APP_0_DEFAULT_WILLING 1 +#define FILE_NAME_LEN (NAME_MAX + 1) +#define CFG_FILE_PREFIX "cfg-" +#define DEF_CFG_FILE CFG_FILE_PREFIX "ethx" +#define FCOE_VLAN_SUFFIX "-fcoe" +#define FCOE_VLAN_FORMAT "%s.%d" FCOE_VLAN_SUFFIX +#define FCOE_VID_SCAN_FORMAT "%*[^.].%d" FCOE_VLAN_SUFFIX + +#define VLAN_DIR "/proc/net/vlan" + +#define DCBD_CONNECT_TIMEOUT (10 * 1000 * 1000) /* 10 seconds */ +#define DCBD_CONNECT_RETRY_TIMEOUT (1 * 1000 * 1000) /* 1 seconds */ +#define DCBD_REQ_RETRY_TIMEOUT (200 * 1000) /* 0.2 seconds */ +#define DCBD_MAX_REQ_RETRIES 10 +#define FCM_PING_REQ_LEN 1 /* byte-length of dcbd PING request */ +#define FCM_PING_RSP_LEN 8 /* byte-length of dcbd PING response */ + +#define FCM_VLAN_DISC_TIMEOUT (1000 * 1000) /* 1 seconds */ + +#define DEF_RX_BUF_SIZE 4096 + +#define NLA_DATA(nla) ((void *)((char *)(nla) + NLA_HDRLEN)) +#define NLA_NEXT(nla) (struct rtattr *)((char *)nla + NLMSG_ALIGN(nla->rta_len)) + +#define FCOE_ETH_TYPE 0x8906 + +#define CFG_IF_VAR_FCOEENABLE "FCOE_ENABLE" +#define CFG_IF_VAR_DCBREQUIRED "DCB_REQUIRED" +#define CFG_IF_VAR_AUTOVLAN "AUTO_VLAN" +#define CFG_IF_VAR_MODE "MODE" +#define CFG_IF_VAR_FIP_RESP "FIP_RESP" + +enum fcoe_mode { + FCOE_MODE_FABRIC = 0, + FCOE_MODE_VN2VN = 1, +}; + +static bool force_legacy; +static sigset_t block_sigset; + +void fcm_vlan_disc_timeout(void *arg); + +/* + * fcoe service configuration data + * Note: These information are read in from the fcoe service + * files in CONFIG_DIR + */ +struct fcoe_port { + struct fcoe_port *next; + + /* information from fcoe configuration files in CONFIG_DIR */ + char ifname[IFNAMSIZ]; /* netif on which fcoe i/f is created */ + char real_ifname[IFNAMSIZ]; /* underlying net ifname - e.g. if ifname + is a VLAN */ + int fcoe_enable; + int dcb_required; + enum fcoe_mode mode; + bool fip_resp; + int auto_vlan; + int auto_created; + int ready; + + /* following track data required to manage FCoE interface state */ + enum fcp_action action; /* current state */ + enum fcp_action last_action; /* last action */ + int last_msg_type; /* last rtnetlink msg type received on if name */ + struct sock_info *sock_reply; + + int ifindex; + unsigned char mac[ETHER_ADDR_LEN]; + struct sa_timer vlan_disc_timer; + int vlan_disc_count; + int fip_socket; + int fip_responder_socket; + char fchost[FCHOSTBUFLEN]; + char ctlr[FCHOSTBUFLEN]; + uint32_t last_fc_event_num; +}; + +enum fcoeport_ifname { + FCP_CFG_IFNAME = 0, + FCP_REAL_IFNAME +}; + +/* + * Interact with DCB daemon. + */ +static void fcm_dcbd_timeout(void *); +static void fcm_dcbd_retry_timeout(void *); +static void fcm_dcbd_disconnect(void); +static int fcm_dcbd_request(char *); +static void fcm_dcbd_rx(void *); +static void fcm_dcbd_event(char *, size_t); +static void fcm_dcbd_cmd_resp(char *, cmd_status); +static void fcm_netif_advance(struct fcm_netif *); +static void fcm_fcoe_action(struct fcoe_port *); +static void fcp_set_next_action(struct fcoe_port *, enum fcp_action); +static enum fcoe_status fcm_fcoe_if_action(char *, char *); + +/* + * Used for backwards compatibility amongst libfcoe + * "control" interfaces. + */ +struct libfcoe_interface_template { + enum fcoe_status (*create)(struct fcoe_port *); + enum fcoe_status (*destroy)(struct fcoe_port *); + enum fcoe_status (*enable)(struct fcoe_port *); + enum fcoe_status (*disable)(struct fcoe_port *); +}; + +static const struct libfcoe_interface_template *libfcoe_control; + +static enum fcoe_status fcm_module_create(struct fcoe_port *p) +{ + enum fcoe_status rc; + + switch (p->mode) { + case FCOE_MODE_VN2VN: + rc = fcm_fcoe_if_action(FCOE_CREATE_VN2VN, p->ifname); + break; + + case FCOE_MODE_FABRIC: + default: + rc = fcm_fcoe_if_action(FCOE_CREATE, p->ifname); + break; + } + if (rc) + return rc; + + /* + * This call validates that the interface name + * has an active fcoe session by checking for + * the fc_host in sysfs. + */ + if (fcoe_find_fchost(p->ifname, p->fchost, FCHOSTBUFLEN)) { + FCM_LOG_DBG("Failed to find fc_host for %s\n", p->ifname); + return ENOSYSFS; + } + + return SUCCESS; +} + +static enum fcoe_status fcm_module_destroy(struct fcoe_port *p) +{ + return fcm_fcoe_if_action(FCOE_DESTROY, p->ifname); +} + +static enum fcoe_status fcm_module_enable(struct fcoe_port *p) +{ + return fcm_fcoe_if_action(FCOE_ENABLE, p->ifname); +} + +static enum fcoe_status fcm_module_disable(struct fcoe_port *p) +{ + return fcm_fcoe_if_action(FCOE_DISABLE, p->ifname); +} + +static struct libfcoe_interface_template libfcoe_module_tmpl = { + .create = fcm_module_create, + .destroy = fcm_module_destroy, + .enable = fcm_module_enable, + .disable = fcm_module_disable, +}; + +static enum fcoe_status fcm_bus_enable(struct fcoe_port *p) +{ + return fcm_write_str_to_ctlr_attr(p->ctlr, FCOE_CTLR_ATTR_ENABLED, "1"); +} + +static int fcm_bus_configure(struct fcoe_port *p) +{ + int rc; + + if (p->mode != FCOE_MODE_VN2VN) + return 0; + + rc = fcm_write_str_to_ctlr_attr(p->ctlr, FCOE_CTLR_ATTR_MODE, "vn2vn"); + return rc; +} + +static enum fcoe_status fcm_bus_create(struct fcoe_port *p) +{ + enum fcoe_status rc; + + rc = fcm_write_str_to_sysfs_file(FCOE_BUS_CREATE, p->ifname); + if (rc) + return rc; + + /* + * This call validates that the interface name + * has an active fcoe session by checking for + * the fc_host in sysfs. + */ + if (fcoe_find_fchost(p->ifname, p->fchost, FCHOSTBUFLEN)) { + FCM_LOG_DBG("Failed to find fc_host for %s\n", p->ifname); + return ENOSYSFS; + } + + /* + * The fcoe_ctlr_device lookup only happens when the fcoe_sysfs + * kernel interfaces are used. It is a defect if p->ctlr is used + * outside of these abstracted routines. + */ + if (fcoe_find_ctlr(p->fchost, p->ctlr, FCHOSTBUFLEN)) { + FCM_LOG_DBG("Failed to get ctlr for %s\n", p->ifname); + return ENOSYSFS; + } + + rc = fcm_bus_configure(p); + if (!rc) + rc = fcm_bus_enable(p); + + return rc; +} + +static enum fcoe_status fcm_bus_destroy(struct fcoe_port *p) +{ + return fcm_write_str_to_sysfs_file(FCOE_BUS_DESTROY, p->ifname); +} + +static enum fcoe_status fcm_bus_disable(struct fcoe_port *p) +{ + return fcm_write_str_to_ctlr_attr(p->ctlr, FCOE_CTLR_ATTR_ENABLED, "0"); +} + +static struct libfcoe_interface_template libfcoe_bus_tmpl = { + .create = fcm_bus_create, + .destroy = fcm_bus_destroy, + .enable = fcm_bus_enable, + .disable = fcm_bus_disable, +}; + +struct fcm_clif { + int cl_fd; + int cl_busy; /* non-zero if command pending */ + int cl_ping_pending; + struct sockaddr_un cl_local; +}; + +static struct fcm_clif fcm_clif_st; +static struct fcm_clif *fcm_clif = &fcm_clif_st; +static struct sa_timer fcm_dcbd_timer; + +/* Debugging routine */ +static void print_errors(int errors); + +struct fcm_netif_head fcm_netif_head = TAILQ_HEAD_INITIALIZER(fcm_netif_head); + +static int fcm_fc_socket; + +static int fcm_link_socket; +static int fcm_link_seq; +static void fcm_link_recv(void *); +static void fcm_link_getlink(void); +static int fcm_link_buf_check(size_t); +static void clear_dcbd_info(struct fcm_netif *ff); +static int fcoe_vid_from_ifname(const char *ifname); + +/* + * Table for getopt_long(3). + */ +static struct option fcm_options[] = { + {"debug", 0, NULL, 'd'}, + {"legacy", 0, NULL, 'l'}, + {"syslog", 0, NULL, 's'}, + {"exec", 1, NULL, 'e'}, + {"foreground", 0, NULL, 'f'}, + {"version", 0, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +char progname[20]; + +/* + * Issue with buffer size: It isn't clear how to read more than one + * buffer's worth of GETLINK replies. The kernel seems to just drop the + * interface messages if they don't fit in the buffer, so we just make it + * large enough to fit and expand it if we ever do a read that almost fills it. + */ +static char *fcm_link_buf; +static size_t fcm_link_buf_size = 4096; /* initial size */ +static const size_t fcm_link_buf_fuzz = 300; /* "almost full" remainder */ + +/* + * A value must be surrounded by quates, e.g. "x". + * The minimum length of a value is 1 excluding the quotes. + * The maximum length of a value is 20 excluding the quotes. + */ +static int fcm_remove_quotes(char *buf, int len) +{ + char *s = buf; + char *e = buf + len - 1; + char tmp[CONFIG_MAX_VAL_LEN + 1]; + + if (len < CONFIG_MIN_VAL_LEN) + return -1; + if ((*s >= '0' && *s <= '9') || + (*s >= 'a' && *s <= 'z') || + (*s >= 'A' && *s <= 'Z')) + return -1; + if ((*e >= '0' && *e <= '9') || + (*e >= 'a' && *e <= 'z') || + (*e >= 'A' && *e <= 'Z')) + return -1; + s = buf + 1; + *e = '\0'; + strncpy(tmp, s, len - 1); + strncpy(buf, tmp, len - 1); + + return 0; +} + +/* + * Read a configuration variable for a port from a config file. + * There's no problem if the file doesn't exist. + * The buffer is set to an empty string if the variable is not found. + * + * Returns: 1 found + * 0 not found + * -1 error in format + */ +static size_t fcm_read_config_variable(char *file, char *val_buf, size_t len, + FILE *fp, const char *var_name) +{ + char *s; + char *var; + char *val; + char buf[FILE_NAME_LEN]; + int n; + + val_buf[0] = '\0'; + buf[sizeof(buf) - 1] = '\0'; + rewind(fp); + while ((s = fgets(buf, sizeof(buf) - 1, fp)) != NULL) { + while (isspace(*s)) + s++; + if (*s == '\0' || *s == '#') + continue; + var = s; + if (!isalpha(*var)) + continue; + val = strchr(s, '='); + if (val == NULL) + continue; + *val++ = '\0'; + s = val; + if (strcmp(var_name, var) != 0) + continue; + while (*s != '\0' && !isspace(*s)) + s++; + *s = '\0'; + n = snprintf(val_buf, len, "%s", val); + if (fcm_remove_quotes(val_buf, n) < 0) { + FCM_LOG("Invalid format in config file" + " %s: %s=%s\n", + file, var_name, val); + /* error */ + return -1; + } + /* found */ + FCM_LOG_DBG("%s: %s = %s\n", file, var_name, val); + return 1; + } + /* not found */ + return 0; +} + +static struct fcoe_port *alloc_fcoe_port(char *ifname) +{ + struct fcoe_port *p = NULL; + + p = (struct fcoe_port *) calloc(1, sizeof(struct fcoe_port)); + if (p) { + snprintf(p->ifname, sizeof(p->ifname), "%s", ifname); + p->action = FCP_WAIT; + /* last_action is initialized to FCP_DESTROY_IF to indicate + * that the interface is not created yet. + */ + p->last_action = FCP_DESTROY_IF; + p->fip_socket = -1; + p->fip_responder_socket = -1; + p->fchost[0] = '\0'; + p->last_fc_event_num = 0; + sa_timer_init(&p->vlan_disc_timer, fcm_vlan_disc_timeout, p); + p->ready = 1; + } + + return p; +} + +static bool real_ifname_from_name(char *real_ifname, const char *ifname) +{ + const char *sep; + + sep = index(ifname, '.'); + if (!sep) + return false; + memset(real_ifname, 0, IFNAMSIZ); + memcpy(real_ifname, ifname, sep - ifname); + return true; +} + +static int fcm_read_config_files(void) +{ + char file[80]; + FILE *fp; + char val[CONFIG_MAX_VAL_LEN + 1]; + DIR *dir; + struct dirent *dp; + struct fcoe_port *curr = NULL; + struct fcoe_port *next = NULL; + int rc; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + dir = opendir(CONFIG_DIR); + if (dir == NULL) { + FCM_LOG_ERR(errno, "Failed reading directory %s\n", CONFIG_DIR); + return -1; + } + for (;;) { + dp = readdir(dir); + if (dp == NULL) + break; + if (dp->d_name[0] == '.' && + (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + rc = strncmp(dp->d_name, CFG_FILE_PREFIX, + strlen(CFG_FILE_PREFIX)); + if (rc) + continue; + + if (!strncmp(dp->d_name, DEF_CFG_FILE, + strlen(DEF_CFG_FILE))) + continue; + + next = alloc_fcoe_port(dp->d_name + 4); + + if (!next) { + FCM_LOG_ERR(errno, "failed to allocate fcoe_port %s", + dp->d_name); + continue; + } + strncpy(file, CONFIG_DIR "/", sizeof(file)); + strncat(file, dp->d_name, sizeof(file) - strlen(file)); + fp = fopen(file, "r"); + if (!fp) { + FCM_LOG_ERR(errno, "Failed to read %s\n", file); + free(next); + continue; + } + + real_ifname_from_name(next->real_ifname, next->ifname); + + /* FCOE_ENABLE */ + rc = fcm_read_config_variable(file, val, sizeof(val), + fp, CFG_IF_VAR_FCOEENABLE); + if (rc < 0) { + FCM_LOG("Invalid format for %s variable in %s", + CFG_IF_VAR_FCOEENABLE, file); + fclose(fp); + free(next); + continue; + } + /* if not found, default to "no" */ + if (!strncasecmp(val, "yes", 3) && rc == 1) + next->fcoe_enable = 1; + + /* DCB_REQUIRED */ + rc = fcm_read_config_variable(file, val, sizeof(val), + fp, CFG_IF_VAR_DCBREQUIRED); + if (rc < 0) { + FCM_LOG("Invalid format for %s variable in %s", + CFG_IF_VAR_DCBREQUIRED, file); + fclose(fp); + free(next); + continue; + } + /* if not found, default to "no" */ + if (!strncasecmp(val, "yes", 3) && rc == 1) + next->dcb_required = 1; + + if (next->dcb_required == 1 && fcoe_config.dcb_init == 0) + fcoe_config.dcb_init = 1; + + /* AUTO_VLAN */ + rc = fcm_read_config_variable(file, val, sizeof(val), + fp, CFG_IF_VAR_AUTOVLAN); + if (rc < 0) { + FCM_LOG("Invalid format for %s variable in %s", + CFG_IF_VAR_AUTOVLAN, file); + fclose(fp); + free(next); + continue; + } + /* if not found, default to "no" */ + if (!strncasecmp(val, "yes", 3) && rc == 1) + next->auto_vlan = 1; + + /* FIP_RESP */ + rc = fcm_read_config_variable(file, val, sizeof(val), + fp, CFG_IF_VAR_FIP_RESP); + if (rc < 0) { + FCM_LOG("Invalid format for %s variable in %s", + CFG_IF_VAR_FIP_RESP, file); + fclose(fp); + free(next); + continue; + } + /* if not found, default to "none" */ + next->fip_resp = false; + if (!strcasecmp(val, "yes") && rc == 1) { + FCM_LOG("Starting FIP responder on %s", next->ifname); + next->fip_resp = true; + } + + /* MODE */ + rc = fcm_read_config_variable(file, val, sizeof(val), + fp, CFG_IF_VAR_MODE); + if (rc < 0) { + FCM_LOG("Invalid format for %s variable in %s", + CFG_IF_VAR_MODE, file); + fclose(fp); + free(next); + continue; + } + /* if not found, default to "fabric" */ + next->mode = FCOE_MODE_FABRIC; + if (!strncasecmp(val, "vn2vn", 5) && rc == 1) + next->mode = FCOE_MODE_VN2VN; + + fclose(fp); + + if (!fcoe_config.port) { + fcoe_config.port = next; + curr = next; + } else { + curr->next = next; + curr = next; + } + } + closedir(dir); + + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + + return 0; +} + +/* + * Given an fcoe_port pointer and an ifname, find the next fcoe_port + * in the list with a real ifname of 'ifname'. + * + * Returns: fcoe_port pointer to fcoe port entry + * NULL - if not found + */ +static struct fcoe_port *fcm_find_next_fcoe_port(struct fcoe_port *p, + char *ifname) +{ + struct fcoe_port *np; + struct fcoe_port *found_port = NULL; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + np = fcoe_config.port; + while (np) { + if (np == p) + break; + np = np->next; + } + + if (np) + np = np->next; + + while (np) { + if (!strncmp(ifname, np->real_ifname, IFNAMSIZ)) { + found_port = np; + break; + } + np = np->next; + } + + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + + return found_port; +} + +static struct fcoe_port *fcm_find_fcoe_port(char *ifname, + enum fcoeport_ifname t) +{ + struct fcoe_port *p; + struct fcoe_port *found_port = NULL; + char *fp_ifname; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + p = fcoe_config.port; + while (p) { + switch (t) { + case FCP_CFG_IFNAME: + fp_ifname = p->ifname; + break; + case FCP_REAL_IFNAME: + fp_ifname = p->real_ifname; + break; + default: + FCM_LOG("unhandled interface type [%d] for %s", + t, ifname); + goto found; + } + + if (!strncmp(ifname, fp_ifname, IFNAMSIZ)) { + found_port = p; + goto found; + } + + p = p->next; + } + +found: + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + return found_port; +} + +static struct fcoe_port *fcm_find_port_by_host(uint16_t host_no) +{ + struct fcoe_port *p; + struct fcoe_port *found_port = NULL; + char host[FCHOSTBUFLEN]; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + snprintf(host, FCHOSTBUFLEN, "host%d", host_no); + p = fcoe_config.port; + while (p) { + if (!strncmp(p->fchost, host, FCHOSTBUFLEN)) { + found_port = p; + break; + } + p = p->next; + } + + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + + return found_port; +} + +static void fcm_fc_event_handler(struct fc_nl_event *fc_event) +{ + struct fcoe_port *p = fcm_find_port_by_host(fc_event->host_no); + + if (!p) + return; + + switch (fc_event->event_code) { + case HBA_EVENT_LIP_RESET_OCCURRED: + if (!p->last_fc_event_num && + fc_event->event_num == p->last_fc_event_num) + return; + + if (!p->auto_created && !p->auto_vlan) + return; + + p->last_fc_event_num = fc_event->event_num; + + /* find real interface port and re-activate again */ + p = fcm_find_fcoe_port(p->real_ifname, FCP_CFG_IFNAME); + if (p && p->last_action != FCP_DISABLE_IF) + fcp_set_next_action(p, FCP_ACTIVATE_IF); + break; + default: + FCM_LOG("unsupported fc event:%d for host:%d\n", + fc_event->event_code, fc_event->host_no); + } +} + +static void log_nlmsg_error(struct nlmsghdr *hp, size_t rlen, const char *str) +{ + struct nlmsgerr *ep; + + if (NLMSG_OK(hp, rlen)) { + ep = (struct nlmsgerr *)NLMSG_DATA(hp); + FCM_LOG_DBG("%s, err=%d, type=%d\n", + str, ep->error, ep->msg.nlmsg_type); + } else { + FCM_LOG("%s", str); + } +} + +static void fcm_fc_event_log(struct fc_nl_event *fe) +{ + /* from kernel "include/scsi/scsi_transport_fc.h" */ + enum fc_host_event_code { + FCH_EVT_LIP = 0x1, + FCH_EVT_LINKUP = 0x2, + FCH_EVT_LINKDOWN = 0x3, + FCH_EVT_LIPRESET = 0x4, + FCH_EVT_RSCN = 0x5, + FCH_EVT_ADAPTER_CHANGE = 0x103, + FCH_EVT_PORT_UNKNOWN = 0x200, + FCH_EVT_PORT_OFFLINE = 0x201, + FCH_EVT_PORT_ONLINE = 0x202, + FCH_EVT_PORT_FABRIC = 0x204, + FCH_EVT_LINK_UNKNOWN = 0x500, + FCH_EVT_VENDOR_UNIQUE = 0xffff, + }; + /* from kernel "drivers/scsi/scsi_transport_fc.c" */ + const struct { + enum fc_host_event_code value; + char *name; + } fc_host_event_code_names[] = { + { FCH_EVT_LIP, "lip" }, + { FCH_EVT_LINKUP, "link_up" }, + { FCH_EVT_LINKDOWN, "link_down" }, + { FCH_EVT_LIPRESET, "lip_reset" }, + { FCH_EVT_RSCN, "rscn" }, + { FCH_EVT_ADAPTER_CHANGE, "adapter_chg" }, + { FCH_EVT_PORT_UNKNOWN, "port_unknown" }, + { FCH_EVT_PORT_ONLINE, "port_online" }, + { FCH_EVT_PORT_OFFLINE, "port_offline" }, + { FCH_EVT_PORT_FABRIC, "port_fabric" }, + { FCH_EVT_LINK_UNKNOWN, "link_unknown" }, + { FCH_EVT_VENDOR_UNIQUE, "vendor_unique" }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fc_host_event_code_names); i++) { + if (fe->event_code == fc_host_event_code_names[i].value) { + /* only do u32 data even len is not, e.g. vendor */ + FCM_LOG_DBG("FC_HOST_EVENT %d at %" PRIu64 " secs on " + "host%d code %d=%s datalen %d data=%d\n", + fe->event_num, fe->seconds, + fe->host_no, fe->event_code, + fc_host_event_code_names[i].name, + fe->event_datalen, fe->event_data); + break; + } + } + +} + +static void fcm_fc_event_recv(UNUSED void *arg) +{ + struct nlmsghdr *hp; + struct fc_nl_event *fc_event; + size_t plen; + size_t rlen; + char *buf; + int rc; + + buf = malloc(DEF_RX_BUF_SIZE); + + if (!buf) { + FCM_LOG_ERR(errno, "failed to allocate FC event buffer\n"); + return; + } + + rc = read(fcm_fc_socket, buf, DEF_RX_BUF_SIZE); + if (!rc) + goto free_buf; + + if (rc < 0) { + FCM_LOG_ERR(errno, "fc read error"); + goto free_buf; + } + + hp = (struct nlmsghdr *)buf; + rlen = rc; + for (hp = (struct nlmsghdr *)buf; NLMSG_OK(hp, rlen); + hp = NLMSG_NEXT(hp, rlen)) { + + if (hp->nlmsg_type == NLMSG_DONE) + break; + + if (hp->nlmsg_type == NLMSG_ERROR) { + log_nlmsg_error(hp, rlen, "fc nlmsg error"); + break; + } + + plen = NLMSG_PAYLOAD(hp, 0); + fc_event = (struct fc_nl_event *)NLMSG_DATA(hp); + if (plen < sizeof(*fc_event)) { + FCM_LOG("too short (%zu) to be an FC event", rlen); + break; + } + fcm_fc_event_log(fc_event); + fcm_fc_event_handler(fc_event); + } +free_buf: + free(buf); +} + +static int fcm_fc_events_init(void) +{ + int fd, rc; + struct sockaddr_nl fc_local; + + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_SCSITRANSPORT); + if (fd < 0) { + FCM_LOG_ERR(errno, "fc socket error"); + return fd; + } + memset(&fc_local, 0, sizeof(fc_local)); + fc_local.nl_family = AF_NETLINK; + fc_local.nl_groups = ~0; + fc_local.nl_pid = getpid(); + rc = bind(fd, (struct sockaddr *)&fc_local, sizeof(fc_local)); + if (rc == -1) { + FCM_LOG_ERR(errno, "fc socket bind error"); + close(fd); + return rc; + } + fcm_fc_socket = fd; + + /* Add a given file descriptor readfds set with its rx handler */ + sa_select_add_fd(fd, fcm_fc_event_recv, NULL, NULL, NULL); + return 0; +} + +static int fcm_link_init(void) +{ + int fd; + int rc; + struct sockaddr_nl l_local; + + fcm_link_buf = malloc(fcm_link_buf_size); + ASSERT(fcm_link_buf); + + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd < 0) { + FCM_LOG_ERR(errno, "socket error"); + return fd; + } + memset(&l_local, 0, sizeof(l_local)); + l_local.nl_family = AF_NETLINK; + l_local.nl_groups = RTMGRP_LINK | (1 << (RTNLGRP_DCB - 1)); + l_local.nl_pid = 0; + rc = bind(fd, (struct sockaddr *)&l_local, sizeof(l_local)); + if (rc == -1) { + FCM_LOG_ERR(errno, "bind error"); + close(fd); + return rc; + } + fcm_link_socket = fd; + + /* Add a given file descriptor from a readfds set */ + sa_select_add_fd(fd, fcm_link_recv, NULL, NULL, NULL); + + fcm_link_getlink(); + + return 0; +} + +static struct fcoe_port * +fcm_port_create(char *ifname, enum clif_flags flags, int cmd); + +static struct fcoe_port *fcm_new_vlan(int ifindex, int vid, bool vn2vn) +{ + char real_name[IFNAMSIZ]; + char vlan_name[IFNAMSIZ]; + struct fcoe_port *p; + static const int flags[] = { + [false] = CLIF_FLAGS_FABRIC, + [true] = CLIF_FLAGS_VN2VN, + }; + + if (vn2vn) + FCM_LOG_DBG("Auto VLAN found vn2vn on VID %d\n", vid); + else + FCM_LOG_DBG("Auto VLAN Found FCF on VID %d\n", vid); + + if (rtnl_find_vlan(ifindex, vid, vlan_name)) { + rtnl_get_linkname(ifindex, real_name); + snprintf(vlan_name, sizeof(vlan_name), FCOE_VLAN_FORMAT, + real_name, vid); + vlan_create(ifindex, vid, vlan_name); + } + rtnl_set_iff_up(0, vlan_name); + p = fcm_find_fcoe_port(vlan_name, FCP_CFG_IFNAME); + if (p && !p->fcoe_enable) + return p; + p = fcm_port_create(vlan_name, flags[vn2vn], FCP_ACTIVATE_IF); + p->auto_created = 1; + return p; +} + +static int +fcm_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg) +{ + int vid; + unsigned char mac[ETHER_ADDR_LEN]; + int len = ntohs(fh->fip_desc_len); + struct fip_tlv_hdr *tlv = (struct fip_tlv_hdr *)(fh + 1); + struct fcoe_port *p = arg; + struct fcoe_port *vp; + int desc_mask = 0; + bool vn2vn = false; + + enum { + VALID_MAC = 1, + VALID_VLAN = 2, + }; + + if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) + return -1; + + if (fh->fip_subcode == FIP_VLAN_NOTE_VN2VN && + (!p->auto_vlan || p->mode != FCOE_MODE_VN2VN)) { + FCM_LOG_DBG("%s: vn2vn vlan notif: auto_vlan=%d, mode=%d\n", + __func__, p->auto_vlan, p->mode); + return -1; + } + + if (fh->fip_subcode != FIP_VLAN_NOTE && + fh->fip_subcode != FIP_VLAN_NOTE_VN2VN) { + FCM_LOG_DBG("%s: fip_subcode=%d\n", __func__, fh->fip_subcode); + return -1; + } + + if (fh->fip_subcode == FIP_VLAN_NOTE_VN2VN) + vn2vn = true; + + while (len > 0) { + switch (tlv->tlv_type) { + case FIP_TLV_MAC_ADDR: + memcpy(mac, ((struct fip_tlv_mac_addr *)tlv)->mac_addr, + ETHER_ADDR_LEN); + desc_mask |= VALID_MAC; + break; + /* + * this expects to see the MAC_ADDR TLV first, + * and is broken if not + */ + case FIP_TLV_VLAN: + if (tlv->tlv_len != 1) { + FCM_LOG_ERR(EINVAL, "bad length on VLAN TLV"); + break; + } + vid = ntohs(((struct fip_tlv_vlan *)tlv)->vlan); + FCM_LOG_DBG("%s: vid=%d\n", __func__, vid); + if (vid) { + vp = fcm_new_vlan(sa->sll_ifindex, vid, vn2vn); + vp->dcb_required = p->dcb_required; + } else { + /* We received a 0 vlan id. Activate the + * physical port itself + */ + fcp_set_next_action(p, FCP_ACTIVATE_IF); + p->auto_vlan = 0; + } + desc_mask |= VALID_VLAN; + break; + default: + /* unexpected or unrecognized descriptor */ + FCM_LOG_DBG("ignoring TLV type %d", tlv->tlv_type); + break; + } + len -= tlv->tlv_len; + tlv = ((void *) tlv) + (tlv->tlv_len << 2); + }; + + if (desc_mask == (VALID_MAC | VALID_VLAN)) { + /* cancel the retry timer, valid response received */ + sa_timer_cancel(&p->vlan_disc_timer); + return 0; + } else { + return -1; + } +} + +static void fcm_fip_recv(void *arg) +{ + struct fcoe_port *p = arg; + fip_recv(p->fip_socket, fcm_vlan_disc_handler, p); +} + +static int fcm_vlan_disc_socket(struct fcoe_port *p) +{ + int fd; + int origdev = 1; + + fd = fip_socket(p->ifindex, p->mac, FIP_NONE); + if (fd < 0) { + FCM_LOG_ERR(errno, "socket error"); + return fd; + } + setsockopt(fd, SOL_PACKET, PACKET_ORIGDEV, &origdev, sizeof(origdev)); + sa_select_add_fd(fd, fcm_fip_recv, NULL, NULL, p); + return fd; +} + + +/* fcm_vlan_dev_real_dev - query vlan real_dev + * @vlan_ifname - vlan device ifname to find real interface name for + * @real_ifname - pointer to copy real ifname to + * + * Make an ioctl call to find the real device for vlan_ifname. + * Copy to real_ifname if found. + */ +static void fcm_vlan_dev_real_dev(char *vlan_ifname, char *real_ifname) +{ + int fd; + struct vlan_ioctl_args ifv; + + real_ifname[0] = '\0'; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + + if (fd < 0) { + FCM_LOG_ERR(errno, "open vlan query socket error"); + return; + } + + memset(&ifv, 0, sizeof(ifv)); + ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; + strncpy(ifv.device1, vlan_ifname, strlen(vlan_ifname)+1); + if (ioctl(fd, SIOCGIFVLAN, &ifv) == 0) + strncpy(real_ifname, ifv.u.device2, strlen(ifv.u.device2)+1); + close(fd); +} + +/* fcm_is_linkinfo_vlan - parse nlmsg linkinfo rtattr for vlan kind + * @ap: pointer to the linkinfo rtattr + * + * This function parses the linkinfo rtattr and returns + * 1 if it is kind vlan otherwise returns 0. + */ +static int fcm_is_linkinfo_vlan(struct rtattr *ap) +{ + struct rtattr *info; + int len; + + info = (struct rtattr *) (RTA_DATA(ap)); + + for (len = ap->rta_len; RTA_OK(info, len); info = RTA_NEXT(info, len)) { + if (info->rta_type != IFLA_INFO_KIND) + continue; + + if (strncmp("vlan", RTA_DATA(info), sizeof("vlan"))) + return 0; + else + return 1; + } + + return 0; +} + + +/* fcm_set_next_action - determine the next action for the FCoE interface + * @p - pointer to the fcoe_port structure for the FCoE interface + * @action - requested next action to take on the FCoE interface + * + * Based on the last_action taken on the FCoE interface and the requested + * next action, the next action field in the FCoE interface's fcoe_port + * structure is set. + * Notes: last_action is initialized to FCP_DESTROY_IF when the fcoe_port is + * created and it is never set to FCP_WAIT. + * The requested action FCP_ACTIVATE_IF is resolved to either + * FCP_CREATE_IF or FCP_ENABLE_IF as appropriate. + */ +static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action) +{ + switch (p->last_action) { + case FCP_CREATE_IF: + switch (action) { + case FCP_DESTROY_IF: + case FCP_ENABLE_IF: + case FCP_DISABLE_IF: + case FCP_RESET_IF: + case FCP_SCAN_IF: + p->action = action; + break; + case FCP_ACTIVATE_IF: + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_ENABLE_IF; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + case FCP_DESTROY_IF: + switch (action) { + case FCP_CREATE_IF: + case FCP_ACTIVATE_IF: + case FCP_ENABLE_IF: + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else if (p->fcoe_enable) + p->action = FCP_CREATE_IF; + else + p->action = FCP_WAIT; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + case FCP_ENABLE_IF: + switch (action) { + case FCP_DESTROY_IF: + case FCP_DISABLE_IF: + case FCP_RESET_IF: + case FCP_SCAN_IF: + p->action = action; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + case FCP_DISABLE_IF: + switch (action) { + case FCP_DESTROY_IF: + case FCP_RESET_IF: + p->action = action; + break; + case FCP_ENABLE_IF: + case FCP_ACTIVATE_IF: + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_ENABLE_IF; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + case FCP_RESET_IF: + case FCP_SCAN_IF: + switch (action) { + case FCP_DESTROY_IF: + case FCP_DISABLE_IF: + case FCP_RESET_IF: + case FCP_SCAN_IF: + p->action = action; + break; + case FCP_ENABLE_IF: + case FCP_ACTIVATE_IF: + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else + p->action = FCP_ENABLE_IF; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + case FCP_VLAN_DISC: + switch (action) { + case FCP_ACTIVATE_IF: + if (p->auto_vlan) + p->action = FCP_VLAN_DISC; + else if (p->fcoe_enable) + p->action = FCP_CREATE_IF; + else + p->action = FCP_WAIT; + break; + case FCP_DESTROY_IF: + case FCP_DISABLE_IF: + case FCP_RESET_IF: + case FCP_SCAN_IF: + if (p->fip_socket >= 0) { + sa_timer_cancel(&p->vlan_disc_timer); + sa_select_rem_fd(p->fip_socket); + close(p->fip_socket); + p->fip_socket = -1; + } + p->action = action; + break; + default: + p->action = FCP_WAIT; + break; + } + break; + default: + /* last_action is never set to FCP_WAIT */ + break; + } +} + +static void fcp_action_set(char *ifname, enum fcp_action action) +{ + struct fcoe_port *p; + + p = fcm_find_fcoe_port(ifname, FCP_REAL_IFNAME); + while (p) { + if (p->fcoe_enable) { + switch (action) { + case FCP_ACTIVATE_IF: + /* + * let the VLAN discovery code + * enabled auto-VLANs + */ + if (!p->auto_created) + fcp_set_next_action(p, FCP_ACTIVATE_IF); + else + fcp_set_next_action(p, FCP_WAIT); + break; + default: + fcp_set_next_action(p, action); + } + } + p = fcm_find_next_fcoe_port(p, ifname); + } +} + +/* + * Send DCB_CMD_IEEE_GET request for an interface. + */ +static void ieee_get_req(struct fcm_netif *ff) +{ + int iflen; + int rc; + int seq; + struct { + struct nlmsghdr nl; + struct dcbmsg dcbmsg; + struct rtattr rta; + char ifname[IFNAMSIZ]; + } msg; + + seq = ++fcm_link_seq; + if (!seq) + seq = ++fcm_link_seq; + + iflen = strlen(ff->ifname); + if (iflen >= IFNAMSIZ) + iflen = IFNAMSIZ - 1; + + memset(&msg, 0, sizeof(msg)); + msg.nl.nlmsg_len = NLMSG_ALIGN(sizeof(msg) - sizeof(msg.ifname) + + iflen + 1); + msg.nl.nlmsg_type = RTM_GETDCB; + msg.nl.nlmsg_flags = NLM_F_REQUEST; + msg.nl.nlmsg_seq = seq; + msg.nl.nlmsg_pid = getpid(); + msg.dcbmsg.cmd = DCB_CMD_IEEE_GET; + msg.dcbmsg.dcb_family = AF_UNSPEC; + msg.dcbmsg.dcb_pad = 0; + msg.rta.rta_len = NLMSG_ALIGN(NLA_HDRLEN + iflen + 1); + msg.rta.rta_type = DCB_ATTR_IFNAME; + strncpy(msg.ifname, ff->ifname, iflen); + ff->ieee_resp_pending = seq; + rc = write(fcm_link_socket, &msg, msg.nl.nlmsg_len); + if (rc < 0) { + FCM_LOG_ERR(errno, "%s: %s: write failed\n", __func__, + ff->ifname); + ff->ieee_resp_pending = 0; + } +} + +/* + * clear_ieee_info - Clear IEEE info to unknown values + */ +static void clear_ieee_info(struct fcm_netif *ff) +{ + ff->ieee_pfc_info = 0; + ff->ieee_app_info = 0; + ff->dcbx_cap = 0; +} + +STR_ARR(ieee_states, "Unknown", "Out of range", + [IEEE_INIT] = "IEEE_INIT", + [IEEE_GET_STATE] = "IEEE_GET_STATE", + [IEEE_DONE] = "IEEE_DONE", + [IEEE_ACTIVE] = "IEEE_ACTIVE", +); + +static void +ieee_state_set(struct fcm_netif *ff, enum ieee_state new_state) +{ + if (ff->ff_operstate != IF_OPER_UP) { + ff->ieee_state = IEEE_INIT; + return; + } + + if (fcoe_config.debug) { + FCM_LOG_DEV_DBG(ff, "IEEE state change: %s -> %s", + getstr(&ieee_states, ff->ieee_state), + getstr(&ieee_states, new_state)); + } + + if (new_state == IEEE_GET_STATE) + clear_ieee_info(ff); + + ff->ieee_state = new_state; + ff->ieee_resp_pending = 0; +} + +static struct sa_nameval fcm_dcbd_states[] = FCM_DCBD_STATES; + +static void fcm_dcbd_state_set(struct fcm_netif *ff, + enum fcm_dcbd_state new_state) +{ + if (ff->ff_operstate != IF_OPER_UP) { + ff->ff_dcbd_state = FCD_INIT; + return; + } + + if (fcoe_config.debug) { + char old[32]; + char new[32]; + + FCM_LOG_DEV_DBG(ff, "DCBD state change: %s -> %s", + sa_enum_decode(old, sizeof(old), + fcm_dcbd_states, + ff->ff_dcbd_state), + sa_enum_decode(new, sizeof(new), + fcm_dcbd_states, new_state)); + } + + if (new_state == FCD_GET_DCB_STATE) + clear_dcbd_info(ff); + + if (new_state == FCD_INIT) { + ff->dcbd_retry_cnt = 0; + sa_timer_cancel(&ff->dcbd_retry_timer); + } + + if (new_state == FCD_ERROR) { + ff->dcbd_retry_cnt++; + FCM_LOG_DEV_DBG(ff, "%s: SETTING lldpad RETRY TIMER = %d\n", + ff->ifname, + ff->dcbd_retry_cnt * DCBD_REQ_RETRY_TIMEOUT); + sa_timer_set(&ff->dcbd_retry_timer, + ff->dcbd_retry_cnt * DCBD_REQ_RETRY_TIMEOUT); + } + + ff->ff_dcbd_state = new_state; + ff->response_pending = 0; +} + +static int fip_recv_vlan_req(struct sockaddr_ll *ssa, struct fcoe_port *sp) +{ + struct fip_tlv_vlan *vlan_tlvs = NULL; + int vlan_count = 0; + struct fcoe_port *p; + int rc; + + /* Handle all FCoE ports which are on VLANs over this ifname. */ + p = fcm_find_fcoe_port(sp->real_ifname, FCP_REAL_IFNAME); + for (; p; p = fcm_find_next_fcoe_port(p, sp->real_ifname)) { + int vid; + struct fip_tlv_vlan *vtp; + + if (!p->ready) + continue; + if (p->mode != FCOE_MODE_VN2VN) + continue; + vid = fcoe_vid_from_ifname(p->ifname); + if (vid < 0) + continue; + vtp = realloc(vlan_tlvs, sizeof(*vlan_tlvs)); + if (!vtp) + break; + memset(&vtp[vlan_count], 0, sizeof(*vtp)); + vtp[vlan_count].hdr.tlv_type = FIP_TLV_VLAN; + vtp[vlan_count].hdr.tlv_len = 1; + vtp[vlan_count].vlan = htons(vid); + ++vlan_count; + vlan_tlvs = vtp; + } + + FCM_LOG_DBG("%s: %d vlans found\n", __func__, vlan_count); + rc = fip_send_vlan_notification(sp->fip_responder_socket, + ssa->sll_ifindex, sp->mac, + ssa->sll_addr, vlan_tlvs, vlan_count); + if (rc < 0) { + FCM_LOG_ERR(-rc, "%s: fip_send_vlan_notification error\n", + __func__); + } + if (vlan_tlvs) + free(vlan_tlvs); + return rc; +} + +static int fip_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa, + void *arg) +{ + struct fcoe_port *p = arg; + int rc = -1; + + if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) { + FCM_LOG_DBG("ignoring FIP packet, protocol %d\n", + ntohs(fh->fip_proto)); + return -1; + } + + switch (fh->fip_subcode) { + case FIP_VLAN_REQ: + FCM_LOG_DBG("received VLAN req, subcode=%d\n", fh->fip_subcode); + rc = fip_recv_vlan_req(sa, p); + break; + default: + FCM_LOG_DBG("ignored FIP VLAN packet with subcode %d\n", + fh->fip_subcode); + break; + } + return rc; +} + +static void fip_responder(void *arg) +{ + struct fcoe_port *p = arg; + + fip_recv(p->fip_responder_socket, fip_vlan_disc_handler, p); +} + +static void init_fip_vn2vn_responder(struct fcoe_port *p) +{ + int s; + + if (p->fip_responder_socket >= 0) + return; + + s = fip_socket(p->ifindex, p->mac, FIP_ALL_VN2VN); + if (s < 0) { + FCM_LOG_ERR(errno, "%s: Failed to get fip socket\n", p->ifname); + return; + } + p->fip_responder_socket = s; + FCM_LOG_DBG("%s: Adding FIP responder socket\n", p->ifname); + sa_select_add_fd(s, fip_responder, NULL, NULL, p); +} + +static void update_fcoe_port_state(struct fcoe_port *p, unsigned int type, + u_int8_t operstate, enum fcoeport_ifname t) +{ + struct fcm_netif *ff = NULL; + + if (type != RTM_DELLINK) { + ff = fcm_netif_lookup_create(p->real_ifname); + if (!ff) + return; + + /* Only set the ff_operstate field of the network interface + * element if this routine is being called for the real + * network interface, or, if the interface is a VLAN, if the + * network interface element has not been intialized and the + * VLAN operstate is up (if VLAN is up, then real interface is + * up). + */ + if ((t == FCP_REAL_IFNAME) || + ((t == FCP_CFG_IFNAME) && + (ff->ff_operstate == IF_OPER_UNKNOWN) && + (operstate == IF_OPER_UP))) + ff->ff_operstate = operstate; + + if (t == FCP_REAL_IFNAME && p->fip_resp) + init_fip_vn2vn_responder(p); + + if (!p->fcoe_enable) { + fcp_set_next_action(p, FCP_DESTROY_IF); + return; + } + + if (operstate == IF_OPER_UP) { + if (p->dcb_required) { + /* If DCB is required, do not start the dcbd + * query sequence if this routine is being + * called for a real interface and the FCoE + * interface is configured on a VLAN. + */ + if (!((t == FCP_REAL_IFNAME) && + strncmp(p->ifname, p->real_ifname, + IFNAMSIZ))) { + fcm_dcbd_state_set(ff, + FCD_GET_DCB_STATE); + ieee_state_set(ff, IEEE_GET_STATE); + } + } else { + /* hold off on auto-created VLAN ports until + * VLAN discovery can validate that the setup + * has not changed */ + if (!p->auto_created || !p->auto_vlan) + fcp_set_next_action(p, FCP_ACTIVATE_IF); + } + } else { + fcp_set_next_action(p, FCP_DISABLE_IF); + } + } else { + fcp_set_next_action(p, FCP_DESTROY_IF); + } +} + +static int fcoe_vid_from_ifname(const char *ifname) +{ + int vid = -1; + int rc; + + if (strlen(ifname) <= strlen(FCOE_VLAN_SUFFIX) || + strcmp(&ifname[strlen(ifname) - strlen(FCOE_VLAN_SUFFIX)], + FCOE_VLAN_SUFFIX)) + return vid; + rc = sscanf(ifname, FCOE_VID_SCAN_FORMAT, &vid); + if (rc == 1) + return vid; + return -1; +} + +static void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type) +{ + struct fcoe_port *p; + struct rtattr *ap; + char ifname[IFNAMSIZ]; + char real_dev[IFNAMSIZ]; + u_int8_t operstate; + unsigned char mac[ETHER_ADDR_LEN]; + int is_vlan; + int ifindex; + + is_vlan = 0; + operstate = IF_OPER_UNKNOWN; + + ifindex = ip->ifi_index; + + if (ip->ifi_type != ARPHRD_ETHER) + return; + + len -= sizeof(*ip); + for (ap = (struct rtattr *)(ip + 1); RTA_OK(ap, len); + ap = RTA_NEXT(ap, len)) { + switch (ap->rta_type) { + case IFLA_ADDRESS: + if (RTA_PAYLOAD(ap) == 6) + memcpy(mac, RTA_DATA(ap), ETHER_ADDR_LEN); + break; + + case IFLA_IFNAME: + sa_strncpy_safe(ifname, sizeof(ifname), + RTA_DATA(ap), + RTA_PAYLOAD(ap)); + break; + + case IFLA_OPERSTATE: + operstate = *(uint8_t *) RTA_DATA(ap); + break; + + case IFLA_LINKINFO: + if (fcm_is_linkinfo_vlan(ap)) + is_vlan = 1; + break; + + default: + break; + } + } + + p = fcm_find_fcoe_port(ifname, FCP_CFG_IFNAME); + if (is_vlan) { + /* if not in fcoe port list, then ignore this ifname */ + if (!p) + return; + + p->ifindex = ifindex; + memcpy(p->mac, mac, ETHER_ADDR_LEN); + + /* don't do VLAN discovery on a VLAN */ + p->auto_vlan = 0; + + /* try to find the real device name */ + real_dev[0] = '\0'; + fcm_vlan_dev_real_dev(ifname, real_dev); + if (strlen(real_dev)) + strncpy(p->real_ifname, real_dev, strlen(real_dev)+1); + if (p->ready) + update_fcoe_port_state(p, type, operstate, + FCP_CFG_IFNAME); + p->last_msg_type = type; + } else { + /* the ifname is not a VLAN. handle the case where it has + * an FCoE interface configured on it. + */ + if (p) { + p->ifindex = ifindex; + memcpy(p->mac, mac, ETHER_ADDR_LEN); + strncpy(p->real_ifname, ifname, strlen(ifname)+1); + update_fcoe_port_state(p, type, operstate, + FCP_REAL_IFNAME); + } + + /* handle all FCoE ports which are on VLANs over this + * ifname. + */ + p = fcm_find_fcoe_port(ifname, FCP_REAL_IFNAME); + for (; p; p = fcm_find_next_fcoe_port(p, ifname)) { + int vid; + + if (!p->ready) + continue; + vid = fcoe_vid_from_ifname(p->ifname); + if (vid >= 0 && p->mode == FCOE_MODE_VN2VN) { + struct fcoe_port *vp; + + vp = fcm_new_vlan(ifindex, vid, true); + vp->dcb_required = p->dcb_required; + } + update_fcoe_port_state(p, type, operstate, + FCP_REAL_IFNAME); + } + } +} + +static struct rtattr *find_nested_attr(struct rtattr *rta, __u16 type) +{ + struct rtattr *rta_child; + + rta_child = NLA_DATA(rta); + rta = NLA_NEXT(rta); + + for (; rta > rta_child; rta_child = NLA_NEXT(rta_child)) + if (rta_child->rta_type == type) + return rta_child; + + return NULL; +} + +static struct rtattr *find_attr(struct nlmsghdr *nlh, __u16 type) +{ + struct rtattr *rta; + int len; + + rta = (struct rtattr *)(((char *)NLMSG_DATA(nlh)) + + NLMSG_ALIGN(sizeof(struct dcbmsg))); + len = NLMSG_PAYLOAD(nlh, 0) - sizeof(struct dcbmsg); + + while (RTA_OK(rta, len)) { + if (rta->rta_type == type) + return rta; + + rta = RTA_NEXT(rta, len); + } + + return NULL; +} + +static int ieee_get_dcbx(struct nlmsghdr *nlh) +{ + struct rtattr *rta; + + rta = find_attr(nlh, DCB_ATTR_DCBX); + if (!rta) + return -EIO; + + return *(__u8 *)NLA_DATA(rta); +} + +static int get_pri_mask_from_ieee(struct rtattr *rta, __u8 dcbx_cap) +{ + struct rtattr *rta_parent; + struct rtattr *rta_child; + int rval; + __u8 ieee = dcbx_cap & DCB_CAP_DCBX_VER_IEEE; + + rta_parent = find_nested_attr(rta, DCB_ATTR_IEEE_APP_TABLE); + if (!rta_parent) + return -EIO; + + rta_child = NLA_DATA(rta_parent); + rta_parent = NLA_NEXT(rta_parent); + + rval = 0; + for (; rta_parent > rta_child; rta_child = NLA_NEXT(rta_child)) { + struct dcb_app *app; + + if (rta_child->rta_type != DCB_ATTR_IEEE_APP) + continue; + + app = (struct dcb_app *)NLA_DATA(rta_child); + if (app->protocol != FCOE_ETH_TYPE) + continue; + + if (ieee) { + if (app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE) + rval |= 1 << app->priority; + } else { + if (app->selector == DCB_APP_IDTYPE_ETHTYPE) + return app->priority; + } + } + + return rval; +} + +static void fcm_process_ieee_msg(struct nlmsghdr *nlh) +{ + struct dcbmsg *d; + struct rtattr *rta_parent; + struct rtattr *rta_child; + struct fcm_netif *ff; + int dcbx_cap; + int pri_mask; + char ifname[IFNAMSIZ]; + + d = (struct dcbmsg *)NLMSG_DATA(nlh); + if (d->cmd != DCB_CMD_IEEE_GET && d->cmd != DCB_CMD_IEEE_SET) { + FCM_LOG_DBG("Unexpected command type %d\n", d->cmd); + return; + } + + rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(*d))); + if (rta_parent->rta_type != DCB_ATTR_IFNAME) + return; + + strncpy(ifname, NLA_DATA(rta_parent), sizeof(ifname)); + ff = fcm_netif_lookup_create(ifname); + if (!ff) { + FCM_LOG("Processing IEEE message: %s not found or created\n", + ifname); + return; + } + + dcbx_cap = ieee_get_dcbx(nlh); + if (dcbx_cap < 0) { + FCM_LOG("Processing IEEE message: No DCBx capabilities on %s\n", + ifname); + return; + } + ff->dcbx_cap = dcbx_cap; + if (!ff->ff_dcb_state) + ff->ff_dcb_state = !!(dcbx_cap & DCB_CAP_DCBX_VER_IEEE); + if (d->cmd == DCB_CMD_IEEE_SET && !(dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) { + FCM_LOG("Processing IEEE messgae: %s not in IEEE mode\n", + ifname); + } + + rta_parent = find_attr(nlh, DCB_ATTR_IEEE); + if (!rta_parent) { + FCM_LOG("Processing IEEE message: No DCB attribute found on %s\n", + ifname); + return; + } + + rta_child = find_nested_attr(rta_parent, DCB_ATTR_IEEE_PFC); + if (!rta_child) { + FCM_LOG("Processing IEEE messgae: No PFC attribute found on %s\n", + ifname); + return; + } + + struct ieee_pfc *ieee_pfc = (struct ieee_pfc *)NLA_DATA(rta_child); + + ff->ieee_pfc_info = ieee_pfc->pfc_en; + + pri_mask = get_pri_mask_from_ieee(rta_parent, dcbx_cap); + if (pri_mask < 0) { + FCM_LOG("Processing IEEE message: No Priority mask found on %s\n", + ifname); + return; + } + FCM_LOG_DBG("Processing IEEE message: FCoE Priority mask on %s is 0x%02X\n", + ifname, pri_mask); + ff->ieee_app_info = pri_mask; + + if (ff->ieee_state == IEEE_GET_STATE && d->cmd == DCB_CMD_IEEE_GET && + ff->ieee_resp_pending == nlh->nlmsg_seq) + ieee_state_set(ff, IEEE_DONE); +} + +static void fcm_link_recv(UNUSED void *arg) +{ + int rc; + char *buf; + struct nlmsghdr *hp; + struct ifinfomsg *ip; + unsigned type; + size_t plen; + size_t rlen; + + buf = fcm_link_buf; + rc = read(fcm_link_socket, buf, fcm_link_buf_size); + if (rc <= 0) { + if (rc < 0) + FCM_LOG_ERR(errno, "Error reading from " + "netlink socket with fd %d", + fcm_link_socket); + return; + } + + if (fcm_link_buf_check(rc)) { + fcm_link_getlink(); + return; + } + + hp = (struct nlmsghdr *)buf; + rlen = rc; + for (hp = (struct nlmsghdr *)buf; NLMSG_OK(hp, rlen); + hp = NLMSG_NEXT(hp, rlen)) { + + type = hp->nlmsg_type; + if (hp->nlmsg_type == NLMSG_DONE) + break; + + if (hp->nlmsg_type == NLMSG_ERROR) { + log_nlmsg_error(hp, rlen, "nlmsg error"); + break; + } + + plen = NLMSG_PAYLOAD(hp, 0); + ip = (struct ifinfomsg *)NLMSG_DATA(hp); + if (plen < sizeof(*ip)) { + FCM_LOG("too short (%d) to be a LINK message", rc); + break; + } + + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + FCM_LOG_DBG("Link event: %d flags %05X index %d ", + type, ip->ifi_flags, ip->ifi_index); + + fcm_process_link_msg(ip, plen, type); + break; + + case RTM_GETDCB: + case RTM_SETDCB: + fcm_process_ieee_msg(hp); + break; + + default: + FCM_LOG_DBG("%s: Unexpected type %d\n", __func__, type); + break; + } + } +} + +/* + * Send rt_netlink request for all network interfaces. + */ +static void fcm_link_getlink(void) +{ + struct { + struct nlmsghdr nl; + struct ifinfomsg ifi; /* link level specific information, + not dependent on network protocol */ + } msg; + + int rc; + + memset(&msg, 0, sizeof(msg)); + msg.nl.nlmsg_len = sizeof(msg); + msg.nl.nlmsg_type = RTM_GETLINK; + msg.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_ATOMIC; + msg.nl.nlmsg_seq = ++fcm_link_seq; + msg.nl.nlmsg_pid = getpid(); + msg.ifi.ifi_family = AF_UNSPEC; + msg.ifi.ifi_type = ARPHRD_ETHER; + rc = write(fcm_link_socket, &msg, sizeof(msg)); + if (rc < 0) + FCM_LOG_ERR(errno, "write error"); +} + +/* + * Check for whether buffer needs to grow based on amount read. + * Free's the old buffer so don't use that after this returns non-zero. + */ +static int fcm_link_buf_check(size_t read_len) +{ + char *buf; + size_t len = read_len; + + if (len > fcm_link_buf_size - fcm_link_buf_fuzz) { + len = fcm_link_buf_size; + len = len + len / 2; /* grow by 50% */ + buf = malloc(len); + if (buf != NULL) { + free(fcm_link_buf); + fcm_link_buf = buf; + fcm_link_buf_size = len; + return 1; + } else { + FCM_LOG_ERR(errno, "failed to allocate link buffer"); + } + } + return 0; +} + +static void fcm_fcoe_init(void) +{ + if (fcm_read_config_files()) + exit(1); + + if (force_legacy || access(FCOE_BUS_CREATE, F_OK)) { + FCM_LOG_DBG("Using libfcoe module parameter interfaces\n"); + libfcoe_control = &libfcoe_module_tmpl; + } else { + FCM_LOG_DBG("Using /sys/bus/fcoe interfaces\n"); + libfcoe_control = &libfcoe_bus_tmpl; + } +} + +/* + * Allocate an FCoE interface state structure. + */ +static struct fcm_netif *fcm_netif_alloc(char *ifname) +{ + struct fcm_netif *ff; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + ff = calloc(1, sizeof(*ff)); + if (ff) { + snprintf(ff->ifname, sizeof(ff->ifname), "%s", ifname); + ff->ff_operstate = IF_OPER_UNKNOWN; + ff->ff_enabled = 1; + TAILQ_INSERT_TAIL(&fcm_netif_head, ff, ff_list); + } else { + FCM_LOG_ERR(errno, "failed to allocate fcm_netif"); + } + + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + + return ff; +} + +/* + * Find or create an FCoE network interface by ifname. + * @ifname - interface name to create + * + * This creates a netif interface structure with interface name, + * or if one already exists returns the existing one. + */ +static struct fcm_netif *fcm_netif_lookup_create(char *ifname) +{ + struct fcm_netif *ff; + + TAILQ_FOREACH(ff, &fcm_netif_head, ff_list) { + if (!strncmp(ifname, ff->ifname, IFNAMSIZ)) + return ff; + } + + ff = fcm_netif_alloc(ifname); + if (ff != NULL) { + sa_timer_init(&ff->dcbd_retry_timer, fcm_dcbd_retry_timeout, + (void *)ff); + FCM_LOG_DEV_DBG(ff, "Monitoring port %s\n", ifname); + } + + return ff; +} + +/* + * Find an FCoE interface by name. + */ +static struct fcm_netif *fcm_netif_lookup(char *ifname) +{ + struct fcm_netif *curr, *ff = NULL; + + TAILQ_FOREACH(curr, &fcm_netif_head, ff_list) { + if (strcmp(curr->ifname, ifname) == 0) { + ff = curr; + break; + } + } + + return ff; +} + +static void fcm_dcbd_init() +{ + fcm_clif->cl_fd = -1; /* not connected */ + fcm_clif->cl_ping_pending = 0; + sa_timer_init(&fcm_dcbd_timer, fcm_dcbd_timeout, NULL); + fcm_dcbd_timeout(NULL); +} + +static int fcm_dcbd_connect(void) +{ + int rc; + int fd; + struct sockaddr_un dest; + struct sockaddr_un *lp; + socklen_t addrlen; + + ASSERT(fcm_clif->cl_fd < 0); + fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + FCM_LOG_ERR(errno, "clif socket open failed"); /* XXX */ + return 0; + } + + lp = &fcm_clif->cl_local; + memset(lp, 0, sizeof(*lp)); + lp->sun_family = AF_LOCAL; + lp->sun_path[0] = '\0'; + rc = bind(fd, (struct sockaddr *)lp, sizeof(sa_family_t)); + if (rc < 0) { + FCM_LOG_ERR(errno, "clif bind failed"); + close(fd); + return 0; + } + + memset(&dest, 0, sizeof(dest)); + dest.sun_family = AF_LOCAL; + dest.sun_path[0] = '\0'; + snprintf(&dest.sun_path[1], sizeof(dest.sun_path) - 1, + "%s", LLDP_CLIF_SOCK); + addrlen = sizeof(sa_family_t) + strlen(dest.sun_path + 1) + 1; + rc = connect(fd, (struct sockaddr *)&dest, addrlen); + if (rc < 0) { + FCM_LOG_ERR(errno, "Failed to connect to lldpad"); + close(fd); + return 0; + } + fcm_clif->cl_fd = fd; + sa_select_add_fd(fd, fcm_dcbd_rx, NULL, NULL, fcm_clif); + FCM_LOG_DBG("connected to lldpad"); + return 1; +} + +static void fcm_dcbd_timeout(UNUSED void *arg) +{ + if (fcm_clif->cl_ping_pending > 0) { + fcm_dcbd_request("D"); /* DETACH_CMD */ + fcm_dcbd_disconnect(); + } + if (fcm_clif->cl_fd < 0) { + if (fcm_dcbd_connect()) + fcm_dcbd_request("A"); /* ATTACH_CMD: for events */ + else + sa_timer_set(&fcm_dcbd_timer, DCBD_CONNECT_TIMEOUT); + } else { + fcm_clif->cl_ping_pending++; + fcm_dcbd_request("P"); /* ping to verify connection */ + } +} + +static void fcm_dcbd_retry_timeout(void *arg) +{ + struct fcm_netif *ff = (struct fcm_netif *)arg; + + ASSERT(ff); + FCM_LOG_DBG("%s: lldpad retry TIMEOUT occurred [%d]", + ff->ifname, ff->dcbd_retry_cnt); + + fcm_dcbd_state_set(ff, FCD_GET_DCB_STATE); + fcm_netif_advance(ff); +} + +static void fcm_dcbd_disconnect(void) +{ + if (fcm_clif) { + if (fcm_clif->cl_fd >= 0) { + sa_select_rem_fd(fcm_clif->cl_fd); + close(fcm_clif->cl_fd); + } + fcm_clif->cl_fd = -1; /* mark as disconnected */ + fcm_clif->cl_busy = 0; + fcm_clif->cl_ping_pending = 0; + FCM_LOG_DBG("Disconnected from lldpad"); + } +} + +static void fcm_dcbd_shutdown(void) +{ + FCM_LOG_DBG("Shutdown lldpad connection\n"); + fcm_dcbd_request("D"); /* DETACH_CMD */ + fcm_dcbd_disconnect(); + closelog(); +} + +static void fcm_cleanup(void) +{ + struct fcoe_port *curr, *next; + struct fcm_netif *ff, *head; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + for (curr = fcoe_config.port; curr; curr = next) { + FCM_LOG_DBG("OP: DESTROY %s\n", curr->ifname); + fcm_fcoe_if_action(FCOE_DESTROY, curr->ifname); + next = curr->next; + free(curr); + } + + for (head = TAILQ_FIRST(&fcm_netif_head); head; head = ff) { + ff = TAILQ_NEXT(head, ff_list); + TAILQ_REMOVE(&fcm_netif_head, head, ff_list); + free(head); + } + + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + + free(fcm_link_buf); +} + +static u_int32_t fcm_get_hex(char *cp, u_int32_t len, char **endptr) +{ + u_int32_t hex = 0; + + while (len > 0) { + len--; + if (*cp >= '0' && *cp <= '9') + hex = (hex << 4) | (*cp - '0'); + else if (*cp >= 'A' && *cp <= 'F') + hex = (hex << 4) | (*cp - 'A' + 10); + else if (*cp >= 'a' && *cp <= 'f') + hex = (hex << 4) | (*cp - 'a' + 10); + else + break; + cp++; + } + *endptr = (len == 0) ? NULL : cp; + return hex; +} + +static void fcm_dcbd_rx(void *arg) +{ + struct fcm_clif *clif = arg; + cmd_status st; + char buf[128]; + size_t len; + int rc; + char *ep; + + len = sizeof(buf); + rc = read(clif->cl_fd, buf, sizeof(buf) - 1); + if (rc < 0) + FCM_LOG_ERR(errno, "read"); + else if (rc > 0 && rc < (int)sizeof(buf)) { + buf[rc] = '\0'; + len = strlen(buf); + ASSERT(len <= rc); + + FCM_LOG_DBG("recv '%s', len=%d bytes succeeded", buf, (int)len); + switch (buf[CLIF_RSP_MSG_OFF]) { + case CMD_RESPONSE: + st = fcm_get_hex(buf + CLIF_STAT_OFF, CLIF_STAT_LEN, + &ep); + if (ep != NULL) + FCM_LOG("unexpected response code from lldpad: " + "len %zd buf %s rc %d", len, buf, rc); + else if (st != cmd_success && + st != cmd_not_applicable && + st != cmd_device_not_found) { + FCM_LOG("error response from lldpad: " + "error %d len %zd %s", + st, len, buf); + } + fcm_clif->cl_busy = 0; + + switch (buf[3]) { + case DCB_CMD: + fcm_dcbd_cmd_resp(buf, st); + break; + case ATTACH_CMD: + break; + case DETACH_CMD: + break; + case PING_CMD: + if (clif->cl_ping_pending > 0) + --clif->cl_ping_pending; + break; + case LEVEL_CMD: + break; + default: + FCM_LOG("Unexpected cmd in response " + "from lldpad: len %zd %s", + len, buf); + break; + } + break; + + case EVENT_MSG: + fcm_dcbd_event(buf, len); + break; + default: + FCM_LOG("Unexpected message from lldpad: len %zd buf %s", + len, buf); + break; + } + } +} + +/* + * returns: 1 if request was successfully written + * 0 if the write failed + */ +static int fcm_dcbd_request(char *req) +{ + size_t len; + int rc; + + if (fcm_clif->cl_fd < 0) + return 0; + len = strlen(req); + ASSERT(fcm_clif->cl_busy == 0); + sa_timer_set(&fcm_dcbd_timer, DCBD_CONNECT_TIMEOUT); + fcm_clif->cl_busy = 1; + rc = write(fcm_clif->cl_fd, req, len); + if (rc < 0) { + FCM_LOG_ERR(errno, "Failed write req %s len %zd", req, len); + fcm_clif->cl_busy = 0; + fcm_dcbd_disconnect(); + sa_timer_set(&fcm_dcbd_timer, DCBD_CONNECT_RETRY_TIMEOUT); + return 0; + } + + if (rc > FCM_PING_REQ_LEN) + FCM_LOG_DBG("sent '%s', rc=%d bytes succeeded", req, rc); + return 1; +} + +/* + * Find port for message. + * The port name length starts at len_off for len_len bytes. + * The entire message length is len. + * The pointer to the message pointer is passed in, and updated to point + * past the interface name. + */ +static struct fcm_netif *fcm_dcbd_get_port(char **msgp, size_t len_off, + size_t len_len, size_t len) +{ + struct fcm_netif *ff; + u_int32_t if_len; + char *ep; + char *msg; + char ifname[IFNAMSIZ]; + + msg = *msgp; + if (len_off + len_len >= len) + return NULL; + + if_len = fcm_get_hex(msg + len_off, len_len, &ep); + if (ep != NULL) { + FCM_LOG("Parse error on port len: msg %s", msg); + return NULL; + } + + if (len_off + len_len + if_len > len) { + FCM_LOG("Invalid port len %d msg %s", if_len, msg); + return NULL; + } + msg += len_off + len_len; + sa_strncpy_safe(ifname, sizeof(ifname), msg, if_len); + *msgp = msg + if_len; + ff = fcm_netif_lookup(ifname); + if (ff == NULL) { + FCM_LOG("ifname '%s' not found", ifname); + } + return ff; +} + +/* + * (XXX) Notes: + * This routine is here to help fcm_dcbd_cmd_resp() to pick up + * information of the response packet from the DCBD. + * Returns: 0 on success + * -1 on failure + */ +static int dcb_rsp_parser(struct fcm_netif *ff, char *rsp) +{ + int version; + int dcb_cmd; + int feature; + int subtype; + int plen; + int doff; + int i; + int n; + struct feature_info *f_info = NULL; + char buf[20]; + + feature = hex2int(rsp+DCB_FEATURE_OFF); + + dcb_cmd = hex2int(rsp+DCB_CMD_OFF); + + version = rsp[DCB_VER_OFF] & 0x0f; + if (version != CLIF_MSG_VERSION) { + FCM_LOG_DEV(ff, "WARNING: Unexpected rsp version %d\n", + version); + return -1; + } + + subtype = hex2int(rsp+DCB_SUBTYPE_OFF); + plen = hex2int(rsp+DCB_PORTLEN_OFF); + doff = DCB_PORT_OFF + plen; + + switch (feature) { + case FEATURE_DCB: + ff->ff_dcb_state = (*(rsp+doff+CFG_ENABLE) == '1'); + return 0; + case FEATURE_PFC: + f_info = &ff->ff_pfc_info; + break; + case FEATURE_APP: + f_info = &ff->ff_app_info; + f_info->subtype = subtype; + break; + default: + return -1; + } + + switch (dcb_cmd) { + case CMD_GET_CONFIG: + f_info->enable = (*(rsp+doff+CFG_ENABLE) == '1'); + f_info->advertise = (*(rsp+doff+CFG_ADVERTISE) == '1'); + f_info->willing = (*(rsp+doff+CFG_WILLING) == '1'); + doff += CFG_LEN; + break; + + case CMD_GET_OPER: + f_info->op_vers = hex2int(rsp+doff+OPER_OPER_VER); + f_info->op_error = hex2int(rsp+doff+OPER_ERROR); + f_info->op_mode = (*(rsp+doff+OPER_OPER_MODE) == '1'); + f_info->syncd = (*(rsp+doff+OPER_SYNCD) == '1'); + doff += OPER_LEN; + if (feature == FEATURE_PFC) { + f_info->u.pfcup = 0; + for (i = 0; i < MAX_USER_PRIORITIES; i++) { + if (*(rsp+doff+PFC_UP(i)) == '1') + f_info->u.pfcup |= 1<u.appcfg = hex2int(buf); + } + break; + } + + return 0; +} + +/* + * validate_ieee_info - Validation IEEE DCB status for FCoE + * + * Returns: FCP_ACTIVATE_IF - if the dcb netif qualifies for an fcoe interface + * FCP_DESTROY_IF - if the dcb netif should not support fcoe interface + * FCP_ERROR - if dcb configuration has errors + * FCP_WAIT - if dcb criteria is inconclusive + */ +static enum fcp_action validate_ieee_info(struct fcm_netif *ff) +{ + if (ff->ieee_pfc_info & ff->ieee_app_info) { + FCM_LOG_DBG("%s: %s: IEEE active and valid\n", + __func__, ff->ifname); + return FCP_ACTIVATE_IF; + } + FCM_LOG_DBG("%s: %s: IEEE active and invalid, pfc=0x%x, app=0x%x\n", + __func__, ff->ifname, ff->ieee_pfc_info, ff->ieee_app_info); + return FCP_WAIT; +} + +/* + * validate_dcbd_info - Validating DCBD configuration and status + * + * Returns: FCP_ACTIVATE_IF - if the dcb netif qualifies for an fcoe interface + * FCP_DESTROY_IF - if the dcb netif should not support fcoe interface + * FCP_ERROR - if dcb configuration has errors + * FCP_WAIT - if dcb criteria is inconclusive + */ +static enum fcp_action validate_dcbd_info(struct fcm_netif *ff) +{ + int errors = 0; + int dcbon; + + dcbon = ff->ff_dcb_state; + if (dcbon && (ff->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) + return validate_ieee_info(ff); + + /* check if dcb state qualifies to create the fcoe interface */ + if (dcbon && + ff->ff_app_info.enable && + ff->ff_pfc_info.enable && + ff->ff_app_info.op_mode && + ff->ff_pfc_info.op_mode && + ff->ff_pfc_info.u.pfcup & ff->ff_app_info.u.appcfg) { + + if (dcbon && !ff->ff_app_info.willing) { + FCM_LOG_DEV(ff, + "WARNING: FCoE willing mode is false\n"); + errors++; + } + if (dcbon && !ff->ff_app_info.advertise) { + FCM_LOG_DEV(ff, + "WARNING: FCoE advertise mode is false\n"); + errors++; + } + if (dcbon && !ff->ff_pfc_info.willing) { + FCM_LOG_DEV(ff, + "WARNING: PFC willing mode is false\n"); + errors++; + } + if (dcbon && !ff->ff_pfc_info.advertise) { + FCM_LOG_DEV(ff, + "WARNING: PFC advertise mode is false\n"); + errors++; + } + + if (errors) + FCM_LOG_DEV_DBG(ff, + "WARNING: DCB may not be configured correctly\n"); + else + FCM_LOG_DEV_DBG(ff, "DCB is configured correctly\n"); + + + if (ff->ff_enabled) + return FCP_ACTIVATE_IF; + else { + ff->ff_enabled = 1; + return FCP_ENABLE_IF; + } + } + + /* check if dcb state qualifies to destroy the fcoe interface */ + if (!dcbon || + !ff->ff_app_info.enable || + (ff->ff_app_info.op_mode && ff->ff_pfc_info.op_mode && + !(ff->ff_pfc_info.u.pfcup & ff->ff_app_info.u.appcfg))) { + + if (dcbon && !ff->ff_dcb_state) + FCM_LOG_DEV(ff, "WARNING: DCB is disabled\n"); + + if (dcbon && !ff->ff_app_info.enable) + FCM_LOG_DEV(ff, "WARNING: FCoE enable is off\n"); + + if (dcbon && + !(ff->ff_pfc_info.u.pfcup & ff->ff_app_info.u.appcfg)) + FCM_LOG_DEV(ff, + "WARNING: FCoE priority (0x%02x) doesn't " + "intersect with PFC priority (0x%02x)\n", + ff->ff_app_info.u.appcfg, + ff->ff_pfc_info.u.pfcup); + + ff->ff_enabled = 0; + return FCP_DISABLE_IF; + } + + /* The dcbd state does not match the create or destroy criteria. + * Log possible problems. + */ + if (dcbon && !ff->ff_app_info.willing) { + FCM_LOG_DEV(ff, "WARNING: FCoE willing mode is false\n"); + errors++; + } + if (dcbon && !ff->ff_app_info.advertise) { + FCM_LOG_DEV(ff, "WARNING: FCoE advertise mode is false\n"); + errors++; + } + if (dcbon && !ff->ff_app_info.op_mode) { + FCM_LOG_DEV(ff, "WARNING: FCoE operational mode is false\n"); + print_errors(ff->ff_app_info.op_error); + errors++; + } + if (dcbon && !ff->ff_pfc_info.enable) { + FCM_LOG_DEV(ff, "WARNING: PFC enable is off\n"); + errors++; + } + if (dcbon && !ff->ff_pfc_info.advertise) { + FCM_LOG_DEV(ff, "WARNING: PFC advertise mode is false\n"); + errors++; + } + if (dcbon && !ff->ff_app_info.op_mode) { + FCM_LOG_DEV(ff, "WARNING: APP:0 operational mode is false\n"); + print_errors(ff->ff_app_info.op_error); + errors++; + } + if (dcbon && !ff->ff_pfc_info.op_mode) { + FCM_LOG_DEV(ff, "WARNING: PFC operational mode is false\n"); + print_errors(ff->ff_pfc_info.op_error); + errors++; + } + if (dcbon && !(ff->ff_pfc_info.u.pfcup & ff->ff_app_info.u.appcfg)) { + FCM_LOG_DEV(ff, "WARNING: APP:0 priority (0x%02x) doesn't " + "intersect with PFC priority (0x%02x)\n", + ff->ff_app_info.u.appcfg, + ff->ff_pfc_info.u.pfcup); + errors++; + } + if (errors) { + FCM_LOG_DEV(ff, "WARNING: DCB may be configured incorrectly\n"); + return FCP_ERROR; + } + + return FCP_WAIT; +} + +/* + * clear_dcbd_info - clear dcbd info to unknown values + * + */ +static void clear_dcbd_info(struct fcm_netif *ff) +{ + memset(&ff->ff_pfc_info, 0, sizeof(struct feature_info)); + memset(&ff->ff_app_info, 0, sizeof(struct feature_info)); +} + + +/** + * fcm_dcbd_set_config() - Response handler for set config command + * @ff: fcoe port structure + * @st: status + */ +static void fcm_dcbd_set_config(struct fcm_netif *ff) +{ + if (ff->ff_dcbd_state == FCD_SEND_CONF) + fcm_dcbd_state_set(ff, FCD_GET_PFC_CONFIG); +} + +/** + * fcm_dcbd_get_config() - Response handler for get config command + * @ff: fcoe port structure + * @resp: response buffer + * @st: status + */ +static void fcm_dcbd_get_config(struct fcm_netif *ff, char *resp) +{ + switch (ff->ff_dcbd_state) { + case FCD_GET_DCB_STATE: + if (!dcb_rsp_parser(ff, resp)) { + if (ff->ff_dcb_state && + !(ff->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) + fcm_dcbd_state_set(ff, FCD_GET_PFC_CONFIG); + else + fcm_dcbd_state_set(ff, FCD_DONE); + } else + fcm_dcbd_state_set(ff, FCD_ERROR); + break; + case FCD_GET_PFC_CONFIG: + if (!dcb_rsp_parser(ff, resp)) + fcm_dcbd_state_set(ff, FCD_GET_APP_CONFIG); + else + fcm_dcbd_state_set(ff, FCD_ERROR); + break; + case FCD_GET_APP_CONFIG: + if (!dcb_rsp_parser(ff, resp)) + fcm_dcbd_state_set(ff, FCD_GET_PFC_OPER); + else + fcm_dcbd_state_set(ff, FCD_ERROR); + break; + default: + break; + } +} + + +/** + * fcm_dcbd_get_oper() - Response handler for get operational state command + * @ff: fcoe port structure + * @resp: response buffer + * @cp: response buffer pointer, points past the interface name + * @st: status + * + * Sample msg: R00C103050004eth8010100100208 + * opppssll vvmmeemsllpp + */ +static void fcm_dcbd_get_oper(struct fcm_netif *ff, char *resp, char *cp) +{ + u_int32_t val; + char *ep = NULL; + + val = fcm_get_hex(cp + OPER_ERROR, 2, &ep); + + if (ep) { + FCM_LOG_DEV(ff, "Invalid get oper response " + "parse error byte %td, resp %s", ep - cp, cp); + fcm_dcbd_state_set(ff, FCD_ERROR); + } else { + if (val && fcoe_config.debug) + print_errors(val); + + switch (ff->ff_dcbd_state) { + case FCD_GET_PFC_OPER: + if (dcb_rsp_parser(ff, resp) || !ff->ff_pfc_info.syncd) + fcm_dcbd_state_set(ff, FCD_ERROR); + else + fcm_dcbd_state_set(ff, FCD_GET_APP_OPER); + + FCM_LOG_DEV_DBG(ff, "PFC feature is %ssynced", + ff->ff_pfc_info.syncd ? "" : "not "); + FCM_LOG_DEV_DBG(ff, "PFC operating mode is %s", + ff->ff_pfc_info.op_mode ? "on" : + "off "); + break; + + case FCD_GET_APP_OPER: + if (dcb_rsp_parser(ff, resp) || !ff->ff_app_info.syncd) + fcm_dcbd_state_set(ff, FCD_ERROR); + else + fcm_dcbd_state_set(ff, FCD_DONE); + + FCM_LOG_DEV_DBG(ff, "FCoE feature is %ssynced", + ff->ff_app_info.syncd ? "" : + "not "); + FCM_LOG_DEV_DBG(ff, "FCoE operating mode is %s", + ff->ff_app_info.op_mode ? "on" : + "off "); + break; + + default: + break; + } + } +} + +/* + * Handle command response. + * Response buffer points past command code character in response. + */ +static void fcm_dcbd_cmd_resp(char *resp, cmd_status st) +{ + struct fcm_netif *ff; + u_int32_t ver; + u_int32_t cmd; + u_int32_t feature; + u_int32_t state; + char *ep; + char *cp; + size_t len; + + resp += CLIF_RSP_OFF; + len = strlen(resp); + ver = fcm_get_hex(resp + DCB_VER_OFF, DCB_VER_LEN, &ep); + if (ep != NULL) { + FCM_LOG("parse error: resp %s", resp); + return; + } else if (ver != CLIF_RSP_VERSION) { + FCM_LOG("unexpected version %d resp %s", ver, resp); + return; + } + cmd = fcm_get_hex(resp + DCB_CMD_OFF, DCB_CMD_LEN, &ep); + if (ep != NULL) { + FCM_LOG("parse error on resp cmd: resp %s", resp); + return; + } + feature = fcm_get_hex(resp + DCB_FEATURE_OFF, DCB_FEATURE_LEN, &ep); + if (ep != NULL) { + FCM_LOG("parse error on resp feature: resp %s", resp); + return; + } + fcm_get_hex(resp + DCB_SUBTYPE_OFF, DCB_SUBTYPE_LEN, &ep); + if (ep != NULL) { + FCM_LOG("parse error on resp subtype: resp %s", resp); + return; + } + cp = resp; + ff = fcm_dcbd_get_port(&cp, DCB_PORTLEN_OFF, DCB_PORTLEN_LEN, len); + if (ff == NULL) { + FCM_LOG("port not found. resp %s", resp); + return; + } + + /* + * check that dcbd response matches the current dcbd state. + */ + state = ff->ff_dcbd_state; + if (((cmd == CMD_GET_CONFIG) && + ((state == FCD_GET_DCB_STATE && feature == FEATURE_DCB) || + (state == FCD_GET_PFC_CONFIG && feature == FEATURE_PFC) || + (state == FCD_GET_APP_CONFIG && feature == FEATURE_APP))) + || + ((cmd == CMD_GET_OPER) && + ((state == FCD_GET_PFC_OPER && feature == FEATURE_PFC) || + (state == FCD_GET_APP_OPER && feature == FEATURE_APP)))) { + + /* the response matches the current pending query */ + ff->response_pending = 0; + if (st != cmd_success) { + if (st == cmd_not_applicable) + fcm_dcbd_state_set(ff, FCD_DONE); + else + fcm_dcbd_state_set(ff, FCD_ERROR); + return; + } + } + + switch (cmd) { + case CMD_SET_CONFIG: + fcm_dcbd_set_config(ff); + break; + + case CMD_GET_CONFIG: + fcm_dcbd_get_config(ff, resp); + break; + + case CMD_GET_OPER: + fcm_dcbd_get_oper(ff, resp, cp); + break; + + default: + FCM_LOG_DEV_DBG(ff, "Unknown cmd 0x%x in response: resp %s", + cmd, resp); + break; + } +} + +/* + * Handle incoming DCB event message. + * Example message: E5104eth8050001 + */ +static void fcm_dcbd_event(char *msg, size_t len) +{ + struct fcm_netif *ff; + struct fcoe_port *p; + u_int32_t feature; + u_int32_t subtype; + char *cp; + char *ep; + + if (msg[EV_LEVEL_OFF] != MSG_DCB + '0' || len <= EV_PORT_ID_OFF) + return; + if (msg[EV_VERSION_OFF] != CLIF_EV_VERSION + '0') { + FCM_LOG("Unexpected version in event msg %s", msg); + return; + } + cp = msg; + ff = fcm_dcbd_get_port(&cp, EV_PORT_LEN_OFF, EV_PORT_LEN_LEN, len); + if (ff == NULL) + return; + + feature = fcm_get_hex(cp + EV_FEATURE_OFF, 2, &ep); + if (ep != NULL) { + FCM_LOG_DEV_DBG(ff, "Invalid feature code in event msg %s", + msg); + return; + } + + /* + * Check if the FCoE ports which use the interface on which the + * dcbd event arrived are configured to require dcb. + */ + + p = fcm_find_fcoe_port(ff->ifname, FCP_REAL_IFNAME); + while (p) { + if (p->dcb_required && p->last_msg_type != RTM_DELLINK && + p->fcoe_enable) + break; + p = fcm_find_next_fcoe_port(p, ff->ifname); + } + + /* + * dcb is not required or link was removed, ignore dcbd event + */ + if (!p) + return; + + if (ff->ff_operstate != IF_OPER_UP) + return; + + switch (feature) { + case FEATURE_PG: /* 'E5204eth2020001' */ + FCM_LOG_DEV_DBG(ff, "\n"); + break; + case FEATURE_PFC: /* 'E5204eth2030011' */ + FCM_LOG_DEV_DBG(ff, "\n"); + fcm_dcbd_state_set(ff, FCD_GET_DCB_STATE); + break; + case FEATURE_APP: /* 'E5204eth2050011' */ + FCM_LOG_DEV_DBG(ff, "\n"); + subtype = fcm_get_hex(cp + EV_SUBTYPE_OFF, 2, &ep); + if (subtype != APP_FCOE_STYPE) { + FCM_LOG_DEV_DBG(ff, "Unknown application subtype " + "in msg %s", msg); + break; + } + fcm_dcbd_state_set(ff, FCD_GET_DCB_STATE); + break; + default: + FCM_LOG_DEV_DBG(ff, "Unknown feature 0x%x in msg %s", + feature, msg); + break; + } + + if (fcoe_config.debug) { + if (cp[EV_OP_MODE_CHG_OFF] == '1') + FCM_LOG_DEV_DBG(ff, + "Operational mode changed"); + if (cp[EV_OP_CFG_CHG_OFF] == '1') + FCM_LOG_DEV_DBG(ff, + "Operational config changed"); + } +} + +/* + * The status is interpreted by the client as an 'enum fcoe_status'. + */ +static void fcm_cli_reply(struct sock_info *r, enum fcoe_status status) +{ + char rbuf[MAX_MSGBUF]; + snprintf(rbuf, MSG_RBUF, "%d", status); + sendto(r->sock, rbuf, MSG_RBUF, 0, (struct sockaddr *)&(r->from), + r->fromlen); +} + +static enum fcoe_status fcm_fcoe_if_action(char *path, char *ifname) +{ + FILE *fp = NULL; + enum fcoe_status ret = EFAIL; + + fp = fopen(path, "w"); + if (!fp) { + FCM_LOG_ERR(errno, "%s: Failed to open path %s\n", + progname, path); + goto err_out; + } + + if (EOF == fputs(ifname, fp)) { + FCM_LOG_ERR(errno, "%s: Failed to write %s to path %s.\n", + progname, ifname, path); + goto out; + } + + ret = SUCCESS; +out: + fclose(fp); +err_out: + return ret; +} + +static void fcm_send_fip_request(const struct fcoe_port *p) +{ + int dest; + + if (p->mode == FCOE_MODE_VN2VN) + dest = FIP_ALL_VN2VN; + else + dest = FIP_ALL_FCF; + fip_send_vlan_request(p->fip_socket, p->ifindex, p->mac, dest); +} + +void fcm_vlan_disc_timeout(void *arg) +{ + struct fcoe_port *p = arg; + int s; + + FCM_LOG_DBG("%s: VLAN discovery TIMEOUT [%d]", + p->ifname, p->vlan_disc_count); + p->vlan_disc_count++; + if (p->fip_socket < 0) { + s = fcm_vlan_disc_socket(p); + if (s < 0) { + FCM_LOG_ERR(errno, "Could not acquire fip socket.\n"); + goto set_timeout; + } + p->fip_socket = s; + p->vlan_disc_count = 1; + } + fcm_send_fip_request(p); +set_timeout: + sa_timer_set(&p->vlan_disc_timer, FCM_VLAN_DISC_TIMEOUT); +} + +static int fcm_start_vlan_disc(struct fcoe_port *p) +{ + int s; + if (p->fip_socket < 0) { + s = fcm_vlan_disc_socket(p); + if (s < 0) { + /* + * If we can't open the socket set the timeout + * anyways so we will retry sending the fipvlan + * request. + */ + FCM_LOG_ERR(errno, "Failed to open socket, setting VLAN DISC timer.\n"); + sa_timer_set(&p->vlan_disc_timer, FCM_VLAN_DISC_TIMEOUT); + return s; + } + p->fip_socket = s; + } + p->vlan_disc_count = 1; + fcm_send_fip_request(p); + sa_timer_set(&p->vlan_disc_timer, FCM_VLAN_DISC_TIMEOUT); + return 0; +} + +/* + * + * Input: action = 1 Destroy the FCoE interface + * action = 2 Create the FCoE interface + * action = 3 Reset the interface + */ +static void fcm_fcoe_action(struct fcoe_port *p) +{ + struct fcoe_port *vp; + char path[MAX_PATH_LEN]; + enum fcoe_status rc = SUCCESS; + + switch (p->action) { + case FCP_CREATE_IF: + FCM_LOG_DBG("OP: CREATE %s\n", p->ifname); + rc = libfcoe_control->create(p); + if (rc) { + FCM_LOG_DBG("Failed to create FCoE interface " + "for %s, rc is %d\n", p->ifname, rc); + break; + } + + FCM_LOG_DBG("OP: created fchost:%s for %s\n", + p->fchost, p->ifname); + + break; + case FCP_DESTROY_IF: + FCM_LOG_DBG("OP: DESTROY %s\n", p->ifname); + if (p->auto_vlan) { + /* destroy all the VLANs */ + vp = fcm_find_fcoe_port(p->ifname, FCP_REAL_IFNAME); + while (vp) { + if (vp->auto_created) { + vp->ready = 0; + fcp_set_next_action(vp, FCP_DESTROY_IF); + } + vp = fcm_find_next_fcoe_port(vp, p->ifname); + } + rc = SUCCESS; + break; + } + rc = libfcoe_control->destroy(p); + p->fchost[0] = '\0'; + break; + case FCP_ENABLE_IF: + FCM_LOG_DBG("OP: ENABLE %s\n", p->ifname); + rc = libfcoe_control->enable(p); + break; + case FCP_DISABLE_IF: + FCM_LOG_DBG("OP: DISABLE %s\n", p->ifname); + if (p->auto_vlan) { + /* disable all the VLANs */ + vp = fcm_find_fcoe_port(p->ifname, FCP_REAL_IFNAME); + while (vp) { + if (vp->auto_created) { + vp->ready = 0; + fcp_set_next_action(vp, FCP_DISABLE_IF); + } + vp = fcm_find_next_fcoe_port(vp, p->ifname); + } + break; + } + rc = libfcoe_control->disable(p); + break; + case FCP_RESET_IF: + FCM_LOG_DBG("OP: RESET %s\n", p->ifname); + + if (strlen(p->fchost) <= 0) { + fcm_cli_reply(p->sock_reply, ENOFCHOST); + return; + } + + sprintf(path, "%s/%s/issue_lip", SYSFS_FCHOST, p->fchost); + FCM_LOG_DBG("OP: RESET %s\n", path); + rc = fcm_fcoe_if_action(path, "1"); + break; + case FCP_SCAN_IF: + FCM_LOG_DBG("OP: SCAN %s\n", p->ifname); + if (strlen(p->fchost) <= 0) { + fcm_cli_reply(p->sock_reply, ENOFCHOST); + return; + } + + sprintf(path, "%s/%s/device/scsi_host/%s/scan", + SYSFS_FCHOST, p->fchost, p->fchost); + FCM_LOG_DBG("OP: SCAN %s\n", path); + rc = fcm_fcoe_if_action(path, "- - -"); + break; + case FCP_VLAN_DISC: + FCM_LOG_DBG("OP: VLAN DISC %s\n", p->ifname); + rc = fcm_start_vlan_disc(p); + break; + default: + return; + break; + } + + if (p->sock_reply) { + fcm_cli_reply(p->sock_reply, rc); + free(p->sock_reply); + p->sock_reply = NULL; + } + + p->last_action = p->action; +} + +/* + * Called for all ports. For FCoE ports and candidates, + * get IEEE DCBX information and set the next action. + */ +static void fcm_netif_ieee_advance(struct fcm_netif *ff) +{ + enum fcp_action action; + + ASSERT(ff); + ASSERT(fcm_clif); + + if (fcm_clif->cl_busy) + return; + + if (ff->ieee_resp_pending) + return; + + switch (ff->ieee_state) { + case IEEE_INIT: + break; + case IEEE_GET_STATE: + ieee_get_req(ff); + break; + case IEEE_DONE: + action = validate_ieee_info(ff); + if (action == FCP_ACTIVATE_IF) { + fcp_action_set(ff->ifname, action); + ieee_state_set(ff, IEEE_ACTIVE); + } + break; + case IEEE_ACTIVE: + /* TBD enable and disable if needed in IEEE mode */ + break; + default: + break; + } +} + +/* + * Called for all ports. For FCoE ports and candidates, + * get information and send to dcbd. + */ +static void fcm_netif_advance(struct fcm_netif *ff) +{ + char buf[80], params[30]; + + ASSERT(ff); + ASSERT(fcm_clif); + + if (fcm_clif->cl_busy) + return; + + if (ff->response_pending) + return; + + if (sa_timer_active(&ff->dcbd_retry_timer)) + return; + + switch (ff->ff_dcbd_state) { + case FCD_INIT: + case FCD_ERROR: + break; + case FCD_GET_DCB_STATE: + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_GET_CONFIG, FEATURE_DCB, 0, + (u_int) strlen(ff->ifname), ff->ifname); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_SEND_CONF: + snprintf(params, sizeof(params), "%x1%x02", + ff->ff_app_info.enable, + ff->ff_app_info.willing); + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_SET_CONFIG, FEATURE_APP, APP_FCOE_STYPE, + (u_int) strlen(ff->ifname), ff->ifname, params); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_GET_PFC_CONFIG: + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_GET_CONFIG, FEATURE_PFC, 0, + (u_int) strlen(ff->ifname), ff->ifname, ""); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_GET_APP_CONFIG: + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_GET_CONFIG, FEATURE_APP, APP_FCOE_STYPE, + (u_int) strlen(ff->ifname), ff->ifname, ""); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_GET_PFC_OPER: + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_GET_OPER, FEATURE_PFC, 0, + (u_int) strlen(ff->ifname), ff->ifname, ""); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_GET_APP_OPER: + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_GET_OPER, FEATURE_APP, APP_FCOE_STYPE, + (u_int) strlen(ff->ifname), ff->ifname, ""); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_GET_PEER: + snprintf(buf, sizeof(buf), "%c%x%2.2x%2.2x%2.2x%2.2x%s%s", + DCB_CMD, CLIF_RSP_VERSION, + CMD_GET_PEER, FEATURE_APP, APP_FCOE_STYPE, + (u_int) strlen(ff->ifname), ff->ifname, ""); + ff->response_pending = fcm_dcbd_request(buf); + break; + case FCD_DONE: + switch (validate_dcbd_info(ff)) { + case FCP_DESTROY_IF: + fcp_action_set(ff->ifname, FCP_DESTROY_IF); + fcm_dcbd_state_set(ff, FCD_INIT); + break; + case FCP_ENABLE_IF: + fcp_action_set(ff->ifname, FCP_ENABLE_IF); + fcm_dcbd_state_set(ff, FCD_INIT); + break; + case FCP_DISABLE_IF: + fcp_action_set(ff->ifname, FCP_DISABLE_IF); + fcm_dcbd_state_set(ff, FCD_INIT); + break; + case FCP_ACTIVATE_IF: + fcp_action_set(ff->ifname, FCP_ACTIVATE_IF); + fcm_dcbd_state_set(ff, FCD_INIT); + break; + case FCP_ERROR: + fcp_action_set(ff->ifname, FCP_DISABLE_IF); + if (ff->dcbd_retry_cnt < DCBD_MAX_REQ_RETRIES) + fcm_dcbd_state_set(ff, FCD_ERROR); + else + fcm_dcbd_state_set(ff, FCD_INIT); + break; + case FCP_WAIT: + default: + break; + } + + break; + default: + break; + } +} + +/* + * Run through these steps at the end of each select loop. + * 1. Process list of network interfaces + * - issue next dcbd query action + * - if query sequence is complete - update FCoE port objects + * as necessary with a CREATE or DESTROY next action. + * 2. Process FCoE port list - handle next actions, update states, clean up + */ +static void fcm_handle_changes(void) +{ + struct fcm_netif *ff; + struct fcoe_port *p; + + /* + * Perform pending actions (dcbd queries) on network interfaces. + */ + TAILQ_FOREACH(ff, &fcm_netif_head, ff_list) { + fcm_netif_advance(ff); + fcm_netif_ieee_advance(ff); + } + + /* + * Perform actions on FCoE ports + */ + p = fcoe_config.port; + while (p) { + ff = fcm_netif_lookup(p->real_ifname); + if (!ff) { + if (p->sock_reply) { + fcm_cli_reply(p->sock_reply, ENOETHDEV); + free(p->sock_reply); + p->sock_reply = NULL; + p->action = FCP_WAIT; + } + goto next_port; + } + + fcm_fcoe_action(p); + + fcp_set_next_action(p, FCP_WAIT); +next_port: + p = p->next; + } +} + +static void fcm_usage(void) +{ + printf("Usage: %s\n" + "\t [-f|--foreground]\n" + "\t [-d|--debug]\n" + "\t [-l|--legacy]\n" + "\t [-s|--syslog]\n" + "\t [-v|--version]\n" + "\t [-h|--help]\n\n", progname); + exit(1); +} + +static void fcm_dump(void) +{ + struct fcoe_port *curr, *next; + struct fcm_netif *ff, *head; + + FCM_LOG("*** !!! fcoemon dump begin !!! ***\n"); + + FCM_LOG("*** Listing of fcoe_port instances ***\n"); + for (curr = fcoe_config.port; curr; curr = next) { + FCM_LOG("** ifname: %s\n", curr->ifname); + FCM_LOG("ifindex: %d\n", curr->ifindex); + FCM_LOG("real_ifname: %s\n", curr->real_ifname); + FCM_LOG("fcoe_enable: %d\n", curr->fcoe_enable); + FCM_LOG("dcb_required: %d\n", curr->dcb_required); + FCM_LOG("auto_vlan: %d\n", curr->auto_vlan); + FCM_LOG("auto_created: %d\n", curr->auto_created); + FCM_LOG("ready: %d\n", curr->ready); + FCM_LOG("action: %d\n", curr->action); + FCM_LOG("last_action: %d\n", curr->last_action); + FCM_LOG("last_msg_type: %d\n", curr->last_msg_type); + FCM_LOG("mac: %s\n", curr->mac); //TODO + FCM_LOG("vlan_disc_count: %d\n", curr->vlan_disc_count); + FCM_LOG("fip_socket: %d\n", curr->fip_socket); //TODO + FCM_LOG("fchost: %s\n", curr->fchost); + FCM_LOG("ctlr: %s\n", curr->ctlr); + FCM_LOG("last_fc_event_num: %d\n", curr->last_fc_event_num); + + next = curr->next; + } + + FCM_LOG("*** Listing of fcm_netif instances ***\n"); + for (head = TAILQ_FIRST(&fcm_netif_head); head; head = ff) { + FCM_LOG("** ifname: %s", head->ifname); + FCM_LOG("ff_enabled: %d\n", head->ff_enabled); + FCM_LOG("ff_dcb_state: %d\n", head->ff_dcb_state); + FCM_LOG("dcbx_cap: %d\n", head->dcbx_cap); + FCM_LOG("ieee_state: %d\n", head->ieee_state); + FCM_LOG("ieee_resp_pending: %d\n", head->ieee_resp_pending); + FCM_LOG("ieee_pfc_info: %d\n", head->ieee_pfc_info); + FCM_LOG("ieee_app_info: %d\n", head->ieee_app_info); + FCM_LOG("ff_pfc_info: %d\n", head->ff_pfc_info.op_mode); + FCM_LOG("ff_app_info: %d\n", head->ff_app_info.op_mode); + FCM_LOG("ff_operstate: %d\n", head->ff_operstate); + FCM_LOG("ff_dcb_state: %d\n", head->ff_dcb_state); + FCM_LOG("response_pending: %d\n", head->response_pending); + FCM_LOG("dcbd_retry_cnt: %d\n", head->dcbd_retry_cnt); + + ff = TAILQ_NEXT(head, ff_list); + } + + FCM_LOG("*** !!! fcoemon dump end !!! ***\n"); +} + +static void fcm_sig(int sig) +{ + switch (sig) { + case SIGUSR1: + fcm_dump(); + break; + default: + sa_select_exit(sig); + break; + } +} + +/* + * TODO: This routine does too much. It executes a 'cmd' + * and allocates a fcoe_port if one doesn't exist. The + * function name implies that it only does the latter. + */ +static struct fcoe_port * +fcm_port_create(char *ifname, enum clif_flags flags, int cmd) +{ + struct fcoe_port *p; + struct fcoe_port *curr; + struct fcm_netif *ff; + + p = fcm_find_fcoe_port(ifname, FCP_CFG_IFNAME); + if (p) { + p->ready = 1; + if (!p->fcoe_enable) { + p->fcoe_enable = 1; + fcp_set_next_action(p, cmd); + if (p->dcb_required) { + ff = fcm_netif_lookup(p->real_ifname); + if (!ff) + return p; + fcm_dcbd_state_set(ff, FCD_GET_DCB_STATE); + if (ff->ff_dcbd_state == FCD_GET_DCB_STATE) + fcp_set_next_action(p, FCP_WAIT); + } + } else { + p->fcoe_enable = 1; + fcp_set_next_action(p, cmd); + } + return p; + } + + p = alloc_fcoe_port(ifname); + if (!p) { + FCM_LOG_ERR(errno, "fail to allocate fcoe_port %s", ifname); + return NULL; + } + + fcm_vlan_dev_real_dev(ifname, p->real_ifname); + if (!strlen(p->real_ifname)) + snprintf(p->real_ifname, sizeof(p->real_ifname), "%s", ifname); + p->fcoe_enable = 1; + p->dcb_required = 0; + p->mode = flags & CLIF_FLAGS_MODE_MASK; + fcp_set_next_action(p, cmd); + p->next = NULL; + + sigprocmask(SIG_BLOCK, &block_sigset, NULL); + + if (!fcoe_config.port) + fcoe_config.port = p; + else { + curr = fcoe_config.port; + while (curr->next) + curr = curr->next; + curr->next = p; + } + + sigprocmask(SIG_UNBLOCK, &block_sigset, NULL); + + /* check and add the real_ifname to the network interface list */ + ff = fcm_netif_lookup_create(p->real_ifname); + if (!ff) { + FCM_LOG_ERR(errno, "fail to allocate fcm_netif %s", ifname); + return NULL; + } + return p; +} + +static enum fcoe_status fcm_cli_create(char *ifname, enum clif_flags flags, + struct sock_info **r) +{ + struct fcoe_port *p, *vp; + enum fcoe_status rc = EFAIL; + char fchost[FCHOSTBUFLEN]; + + /* existing fchost already? e.g., from fipvlan -cs */ + if (!fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN)) { + FCM_LOG_DBG("Existing fchost %s found for %s\n", + fchost, ifname); + rc = EFCOECONN; + goto out; + } + + p = fcm_find_fcoe_port(ifname, FCP_CFG_IFNAME); + if (p && p->fcoe_enable) { + /* no action needed */ + rc = EFCOECONN; + goto out; + } + /* re-enable previous VLANs */ + if (p && p->auto_vlan) { + vp = fcm_find_fcoe_port(p->ifname, FCP_REAL_IFNAME); + while (vp) { + if (vp->auto_created) + vp->fcoe_enable = 1; + vp = fcm_find_next_fcoe_port(vp, p->ifname); + } + } + + /* + * This looks odd, and could use some improvement. We may + * or may not have found a valid port. fcm_port_create + * will execute the 'cmd' even if it doesn't allocate a + * new port. fcm_port_create should probably be split + * into two routines, one that allocs a new port and one + * that executes the command. + */ + p = fcm_port_create(ifname, flags, FCP_CREATE_IF); + if (!p) + goto out; + + rc = SUCCESS; + p->sock_reply = *r; + +out: + return rc; +} + +static enum fcoe_status fcm_cli_destroy(char *ifname, + struct sock_info **r) +{ + struct fcoe_port *p; + + p = fcm_find_fcoe_port(ifname, FCP_CFG_IFNAME); + if (p) { + if (p->fcoe_enable) { + p->fcoe_enable = 0; + + if (p->last_action == FCP_DESTROY_IF) + return ENOACTION; + + fcp_set_next_action(p, FCP_DESTROY_IF); + p->sock_reply = *r; + return SUCCESS; + } else { + /* no action needed */ + return ENOACTION; + } + } + + FCM_LOG_ERR(errno, "%s is not in port list.\n", ifname); + return EFAIL; +} + +static enum fcoe_status fcm_cli_action(char *ifname, int cmd, + struct sock_info **r) +{ + struct fcoe_port *p; + + p = fcm_find_fcoe_port(ifname, FCP_CFG_IFNAME); + if (p) { + fcp_set_next_action(p, cmd); + p->sock_reply = *r; + return SUCCESS; + } + + FCM_LOG_ERR(errno, "%s is not in port list.\n", ifname); + return EFAIL; +} + +static struct sock_info *fcm_alloc_reply(struct sockaddr_un *f, + socklen_t flen, int s) +{ + static struct sock_info *r; + + if (flen > sizeof(*f)) + return NULL; + + r = (struct sock_info *)malloc(sizeof(struct sock_info)); + if (!r) { + FCM_LOG_ERR(errno, "Failed in alloc reply sock info.\n"); + return NULL; + } + r->sock = s; + memcpy(&r->from, f, flen); + r->fromlen = flen; + return r; +} + +/* + * receive function registered in sa_select_loop + */ +static void fcm_srv_receive(void *arg) +{ + struct fcm_srv_info *srv_info = arg; + struct clif_data *data; + struct sockaddr_un from; + socklen_t fromlen = sizeof(struct sockaddr_un); + struct sock_info *reply = NULL; + char buf[MAX_MSGBUF], rbuf[MAX_MSGBUF]; + char ifname[sizeof(data->ifname) + 1]; + enum fcoe_status rc = EFAIL; + int res, cmd, snum; + size_t size; + + snum = srv_info->srv_sock; + res = recvfrom(snum, buf, sizeof(buf) - 1, + MSG_DONTWAIT, (struct sockaddr *)&from, &fromlen); + if (res < 0) { + FCM_LOG_ERR(errno, "Failed to receive data from socket %d", + snum); + return; + } + + data = (struct clif_data *)buf; + size = res; + if (size < sizeof(*data)) { + if (size < sizeof(*data) - sizeof(data->flags)) { + FCM_LOG_ERR(EMSGSIZE, + "Message too short from socket %d", snum); + rc = EBADCLIFMSG; + goto err; + } + data->flags = 0; + } else if (size > sizeof(*data)) { + FCM_LOG_ERR(EMSGSIZE, "Message too long from socket %d", snum); + rc = EBADCLIFMSG; + goto err; + } + + cmd = data->cmd; + strncpy(ifname, data->ifname, sizeof(data->ifname)); + ifname[sizeof(data->ifname)] = 0; + + if (cmd != CLIF_PID_CMD) { + rc = fcoe_validate_interface(ifname); + if (rc) + goto err; + } + reply = fcm_alloc_reply(&from, fromlen, snum); + if (!reply) + goto err_out; + + switch (cmd) { + case CLIF_CREATE_CMD: + FCM_LOG_DBG("Received command to create %s\n", ifname); + rc = fcm_cli_create(ifname, data->flags, &reply); + if (rc) + goto err_out; + break; + case CLIF_DESTROY_CMD: + FCM_LOG_DBG("Received command to destroy %s\n", ifname); + rc = fcm_cli_destroy(ifname, &reply); + if (rc) + goto err_out; + break; + case CLIF_RESET_CMD: + FCM_LOG_DBG("Received command to reset %s\n", ifname); + rc = fcm_cli_action(ifname, FCP_RESET_IF, &reply); + if (rc) + goto err_out; + break; + case CLIF_SCAN_CMD: + FCM_LOG_DBG("Received command to scan %s\n", ifname); + rc = fcm_cli_action(ifname, FCP_SCAN_IF, &reply); + if (rc) + goto err_out; + break; + case CLIF_PID_CMD: + FCM_LOG_DBG("FCMON PID\n"); + snprintf(rbuf, MAX_MSGBUF, "%lu", (long unsigned int)getpid()); + sendto(snum, rbuf, strlen(rbuf), 0, (struct sockaddr *)&from, + fromlen); + break; + default: + FCM_LOG_DBG("Received invalid command %d for %s\n", + cmd, ifname); + goto err_out; + } + + return; + +err_out: + free(reply); +err: + snprintf(rbuf, MSG_RBUF, "%d", rc); + sendto(snum, rbuf, MSG_RBUF, 0, (struct sockaddr *)&from, fromlen); +} + +static int fcm_systemd_socket(void) +{ + char *env, *ptr; + unsigned int p, l; + + env = getenv("LISTEN_PID"); + if (!env) + return -1; + + p = strtoul(env, &ptr, 10); + if (ptr && ptr == env) { + FCM_LOG_DBG("Invalid value '%s' for LISTEN_PID\n", env); + return -1; + } + if ((pid_t)p != getpid()) { + FCM_LOG_DBG("Invalid PID '%d' from LISTEN_PID\n", p); + return -1; + } + env = getenv("LISTEN_FDS"); + if (!env) { + FCM_LOG_DBG("LISTEN_FDS is not set\n"); + return -1; + } + l = strtoul(env, &ptr, 10); + if (ptr && ptr == env) { + FCM_LOG_DBG("Invalid value '%s' for LISTEN_FDS\n", env); + return -1; + } + if (l != 1) { + FCM_LOG_DBG("LISTEN_FDS specified %d fds\n", l); + return -1; + } + /* systemd returns fds with an offset of '3' */ + return 3; +} + +static int fcm_srv_create(struct fcm_srv_info *srv_info) +{ + socklen_t addrlen; + struct sockaddr_un addr; + int rc = 0; + + srv_info->srv_sock = fcm_systemd_socket(); + if (srv_info->srv_sock > 0) { + FCM_LOG_DBG("Using systemd socket\n"); + goto out_done; + } + + srv_info->srv_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (srv_info->srv_sock < 0) { + FCM_LOG_ERR(errno, "Failed to create socket\n"); + rc = errno; + goto err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + addr.sun_path[0] = '\0'; + snprintf(&addr.sun_path[1], sizeof(addr.sun_path) -1, + "%s", CLIF_IFNAME); + addrlen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1; + if (bind(srv_info->srv_sock, (struct sockaddr *)&addr, addrlen) < 0) { + FCM_LOG_ERR(errno, "Failed to bind socket\n"); + rc = errno; + goto err_close; + } +out_done: + sa_select_add_fd(srv_info->srv_sock, fcm_srv_receive, + NULL, NULL, srv_info); + + FCM_LOG_DBG("Successfully created socket, socket file and binding\n"); + + return rc; + +err_close: + close(srv_info->srv_sock); + +err: + return rc; +} + +static void fcm_srv_destroy(struct fcm_srv_info *srv_info) +{ + FCM_LOG_DBG("Shutdown fcmon server"); + close(srv_info->srv_sock); +} + +int main(int argc, char **argv) +{ + struct fcm_srv_info srv_info; + struct sigaction sig; + int fcm_fg = 0; + int rc; + int c; + + memset(&fcoe_config, 0, sizeof(fcoe_config)); + + strncpy(progname, basename(argv[0]), sizeof(progname)); + sa_log_prefix = progname; + sa_log_flags = 0; + openlog(sa_log_prefix, LOG_CONS, LOG_DAEMON); + + while ((c = getopt_long(argc, argv, "fdhlsv", + fcm_options, NULL)) != -1) { + switch (c) { + case 'f': + fcm_fg = 1; + break; + case 'd': + fcoe_config.debug = 1; + enable_debug_log(1); + break; + case 'l': + force_legacy = true; + break; + case 's': + fcoe_config.use_syslog = 1; + enable_syslog(1); + break; + case 'v': + printf("%s\n", FCOE_UTILS_VERSION); + return 0; + case 'h': + default: + fcm_usage(); + break; + } + } + if (argc != optind) + fcm_usage(); + + umask(0); + + /* + * Set up for signals. + */ + memset(&sig, 0, sizeof(sig)); + sig.sa_handler = fcm_sig; + + /* + * SIGUSR1 will walk the fcoe_port and fcm_netif lists, + * therefore we need to disable this interrupt when we + * do any list manipulations. The 'block_sigset' is the + * set of signals that will be blocked before list + * manipulations on either of the aforementioned lists. + */ + sigemptyset(&block_sigset); + sigaddset(&block_sigset, SIGUSR1); + + rc = sigaction(SIGINT, &sig, NULL); + if (rc < 0) { + FCM_LOG_ERR(errno, "Failed to register handler for SIGINT"); + exit(1); + } + rc = sigaction(SIGTERM, &sig, NULL); + if (rc < 0) { + FCM_LOG_ERR(errno, "Failed to register handler for SIGTERM"); + exit(1); + } + rc = sigaction(SIGHUP, &sig, NULL); + if (rc < 0) { + FCM_LOG_ERR(errno, "Failed to register handler for SIGHUP"); + exit(1); + } + rc = sigaction(SIGUSR1, &sig, NULL); + if (rc < 0) { + FCM_LOG_ERR(errno, "Failed to register handler for SIGUSR1"); + exit(1); + } + + /* check fcoe module */ + if (fcoe_checkdir(SYSFS_FCOE)) { + FCM_LOG_ERR(errno, "make sure FCoE driver module is loaded!"); + exit(1); + } + + fcm_fcoe_init(); + rc = fcm_fc_events_init(); + if (rc != 0) + exit(1); + + rc = fcm_link_init(); /* NETLINK_ROUTE protocol */ + if (rc != 0) + goto err_cleanup; + + if (fcoe_config.dcb_init) + fcm_dcbd_init(); + + rc = fcm_srv_create(&srv_info); + if (rc != 0) + goto err_cleanup; + + if (!fcm_fg && daemon(0, !fcoe_config.use_syslog)) { + FCM_LOG("Starting daemon failed"); + goto err_cleanup; + } + + sa_select_set_callback(fcm_handle_changes); + + rc = sa_select_loop(); + if (rc < 0) { + FCM_LOG_ERR(rc, "select error\n"); + goto err_cleanup; + } + fcm_dcbd_shutdown(); + fcm_srv_destroy(&srv_info); + if (rc == SIGHUP) + fcm_cleanup(); + return 0; + +err_cleanup: + fcm_cleanup(); + exit(1); +} + +/******************************************************* + * The following are debug routines * + *******************************************************/ +static void add_msg_to_buf(char *buf, size_t maxlen, char *msg, char *prefix) +{ + size_t len = strlen(buf); + + if (len + strlen(msg) + strlen(prefix) < maxlen) + sprintf(buf+len, "%s%s", prefix, msg); +} + +static void print_errors(int errors) +{ + char msg[256]; + int cnt = 0; + + memset(msg, 0, sizeof(msg)); + sprintf(msg, "0x%02x - ", errors); + + if (errors & 0x01) + add_msg_to_buf(msg, sizeof(msg), "mismatch with peer", + (cnt++) ? ", " : ""); + + if (errors & 0x02) + add_msg_to_buf(msg, sizeof(msg), "local configuration error", + (cnt++) ? ", " : ""); + + if (errors & 0x04) + add_msg_to_buf(msg, sizeof(msg), "multiple TLV's received", + (cnt++) ? ", " : ""); + + if (errors & 0x08) + add_msg_to_buf(msg, sizeof(msg), "peer error", + (cnt++) ? ", " : ""); + + if (errors & 0x10) + add_msg_to_buf(msg, sizeof(msg), "multiple LLDP neighbors", + (cnt++) ? ", " : ""); + + if (errors & 0x20) + add_msg_to_buf(msg, sizeof(msg), "peer feature not present", + (cnt++) ? ", " : ""); + + if (!errors) + add_msg_to_buf(msg, sizeof(msg), "none", ""); + + FCM_LOG("%s\n", msg); +} diff --git a/fcoemon.h b/fcoemon.h new file mode 100644 index 0000000..86858ae --- /dev/null +++ b/fcoemon.h @@ -0,0 +1,181 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOEMON_H_ +#define _FCOEMON_H_ + +#include "fcoe_utils.h" + +struct fcoe_config { + int debug; + int use_syslog; + int dcb_init; + struct fcoe_port *port; +} fcoe_config; + +/* + * Log message. + */ +#define FCM_LOG(...) \ + do { \ + sa_log(__VA_ARGS__); \ + } while (0) + +#define FCM_LOG_ERR(error, ...) \ + do { \ + sa_log_err(error, NULL, __VA_ARGS__); \ + } while (0) + +#define FCM_LOG_DBG(fmt, args...) \ + do { \ + sa_log_debug(fmt, ##args); \ + } while (0) + +#define FCM_LOG_DEV_DBG(fcm, fmt, args...) \ + do { \ + sa_log_debug("%s, " fmt, fcm->ifname, ##args); \ + } while (0) + +#define FCM_LOG_DEV(fcm, fmt, args...) \ + do { \ + sa_log("%s, " fmt, fcm->ifname, ##args); \ + } while (0) + +/* + * States for HBAs relative to the DCB daemon. + * States advance sequentially if conditions are right. + */ +enum fcm_dcbd_state { + FCD_INIT = 0, /* starting state */ + FCD_GET_DCB_STATE, /* getting DCB state */ + FCD_SEND_CONF, /* set proposed configuration */ + FCD_GET_PFC_CONFIG, /* getting PFC configuration */ + FCD_GET_APP_CONFIG, /* getting APP configuration */ + FCD_GET_PFC_OPER, /* getting PFC operational mode */ + FCD_GET_APP_OPER, /* getting AAP operational mode */ + FCD_GET_PEER, /* getting peer configuration */ + FCD_DONE, /* DCB exchanges complete */ + FCD_ERROR, /* DCB error or port unknown by DCB */ +}; + +/* + * States for IEEE DCB devices. + */ +enum ieee_state { + IEEE_INIT = 0, /* Starting state */ + IEEE_GET_STATE, /* Getting IEEE DCB state */ + IEEE_DONE, /* Received IEEE DCB state */ + IEEE_ACTIVE, /* IEEE is in ACTIVE state */ + IEEE_ERROR, /* Error receiving IEEE DCB state */ +}; + +#define MSG_RBUF sizeof(int) +struct sock_info { + int sock; + struct sockaddr_un from; + socklen_t fromlen; +}; + +/* + * Action codes for FCoE ports +*/ +enum fcp_action { + FCP_WAIT = 0, /* waiting for something to happen */ + FCP_CREATE_IF, /* create FCoE interface */ + FCP_DESTROY_IF, /* destroy FCoE interface */ + FCP_RESET_IF, /* reset FCoE interface */ + FCP_SCAN_IF, /* scan FCoE interface */ + FCP_ENABLE_IF, /* enable FCoE interface */ + FCP_DISABLE_IF, /* disable FCoE interface */ + FCP_ACTIVATE_IF, /* create or enable FCoE interface */ + FCP_ERROR, /* error condition */ + FCP_VLAN_DISC, /* start VLAN discovery */ +}; + +#define FCM_DCBD_STATES { \ + { "INIT", FCD_INIT }, \ + { "GET_DCB_STATE", FCD_GET_DCB_STATE }, \ + { "SEND_CONF", FCD_SEND_CONF }, \ + { "GET_PFC_CONFIG", FCD_GET_PFC_CONFIG }, \ + { "GET_APP_CONFIG", FCD_GET_APP_CONFIG }, \ + { "GET_PFC_OPER", FCD_GET_PFC_OPER }, \ + { "GET_APP_OPER", FCD_GET_APP_OPER }, \ + { "GET_PEER", FCD_GET_PEER }, \ + { "DONE", FCD_DONE }, \ + { "ERROR", FCD_ERROR }, \ + { NULL, 0 } \ +} + +struct feature_info { + u_int32_t enable; /* enable/disable feature */ + u_int32_t advertise; /* enable/disable advertise */ + u_int32_t willing; /* enable/disable willing mode */ + u_int32_t syncd; /* synchronized with switch */ + u_int32_t op_mode; /* operational mode */ + u_int32_t op_vers; /* feature operational version */ + u_int32_t op_error; /* operational error */ + u_int32_t subtype; /* subtype */ + union { + u_int32_t pfcup; + u_int32_t appcfg; + } u; +}; + +/* + * Description of potential FCoE network interface. + */ +struct fcm_netif { + TAILQ_ENTRY(fcm_netif) ff_list; /* list linkage */ + u_int32_t ff_enabled:1; /* operational status */ + u_int32_t ff_dcb_state; /* DCB feature state */ + u_int32_t dcbx_cap; /* DCBX capability */ + enum ieee_state ieee_state; /* IEEE state */ + u_int32_t ieee_resp_pending;/* IEEE response pending */ + u_int32_t ieee_pfc_info; /* IEEE pfc info */ + u_int32_t ieee_app_info; /* IEEE app info */ + struct feature_info ff_pfc_info; /* PFC feature info */ + struct feature_info ff_app_info; /* App feature info */ + u_int8_t ff_operstate; /* RFC 2863 operational status */ + enum fcm_dcbd_state ff_dcbd_state; /* DCB daemon state */ + char ifname[IFNAMSIZ]; /* Ethernet interface name */ + int response_pending; /* dcbd query in progress */ + int dcbd_retry_cnt; /* Number of query attempts */ + struct sa_timer dcbd_retry_timer; /* dcbd retry timer */ +}; + +/* + * Description of fcoe socket server interface + */ +struct fcm_srv_info { + int srv_sock; +}; + +TAILQ_HEAD(fcm_netif_head, fcm_netif); + +struct fcm_netif_head fcm_netif_head; + +static void fcm_dcbd_init(void); +static void fcm_dcbd_shutdown(void); +static void fcm_fcoe_init(void); +static struct fcm_netif *fcm_netif_lookup(char *); +static struct fcm_netif *fcm_netif_lookup_create(char *); +static int fcm_link_init(void); +static void fcm_dcbd_state_set(struct fcm_netif *, enum fcm_dcbd_state); + +#endif /* _FCOEMON_H_ */ diff --git a/fcping.c b/fcping.c new file mode 100644 index 0000000..bf2bc0f --- /dev/null +++ b/fcping.c @@ -0,0 +1,1008 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +/* + * FCPing - FC fabric diagnostic. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_types.h" +#include "fc_types.h" +#include "fcoe_utils.h" +typedef uint8_t u8; +#include +#include "fc_ns.h" +#include "fc_gs.h" +#include "fc_els.h" +#include "scsi_bsg_fc.h" + +#include "sysfs_hba.h" + +static const char *cmdname; + +#define FC_MAX_PAYLOAD 2112UL /* Max FC payload */ +#define MAX_SENSE_LEN 96 /* SCSI_SENSE_BUFFERSIZE */ +#define MAX_HBA_COUNT 128 +/* FC ELS ECHO Command takes 4 bytes */ +#define FP_LEN_ECHO sizeof(net32_t) +/* default max ping data length, excluding 4 bytes of ELS ECHO command */ +#define FP_LEN_MAX (FC_MAX_PAYLOAD - FP_LEN_ECHO) +#define FP_LEN_MIN 4 /* fcping needs 4 bytes as sequence number */ +#define FP_LEN_DEF 32 /* default ping payload length */ +#define FP_LEN_PAD 32 /* extra length for response */ +#define FP_MIN_INTVL 0.001 /* minimum interval in seconds */ +#define FP_DEF_INTVL 1.000 /* default sending interval in seconds */ +#define SYSFS_HBA_DIR "/sys/class/net" + +/* Check if it is WKA accoriding to FC-FS-3 Rev 1.00 Clause 11 Table 30 */ +#define FCID_IS_WKA(i) ((((i) >= 0xfffc01) && ((i) <= 0xfffcfe)) || \ + (((i) >= 0xfffff0) && ((i) <= 0xffffff))) + +#define FC_WKA_FABRIC_CONTROLLER ((fc_fid_t)0xfffffd) +#define FC_WKA_DIRECTORY_SERVICE ((fc_fid_t)0xfffffc) + +static void fp_usage(void) +{ + fprintf(stderr, + "Usage: %s [ -fqx ] [ -i ] [ -c ] -h " + "[ -s ] { -F | -P | -N }\n" + " flags:\n" + " -f: Flood ping\n" + " -q: Quiet! just print summary\n" + " -x: Hex dump of responses\n" + " -i : Wait seconds between each ping\n" + " -c : Stop after sending pings\n" + " -h : eth, MAC address, WWPN, or FC-ID of the HBA\n" + " -s : Byte-length of ping request payload (max %lu)\n" + " -F : Destination port ID\n" + " -P : Destination world-wide port name\n" + " -N : Destination world-wide node name\n", + cmdname, FP_LEN_MAX); + + fprintf(stderr, "\nNote that the default maximum FC payload allowed " + "is %lu bytes and the default maximum fcping payload, " + "i.e., the FC ELS ECHO data, allowed is %lu " + "bytes.\n", + FC_MAX_PAYLOAD, FP_LEN_MAX); + + exit(1); +} + +static fc_fid_t fp_did; +static fc_wwn_t fp_port_wwn; +static fc_wwn_t fp_node_wwn; +static int fp_count = -1; /* send indefinitely by default */ +static uint32_t fp_len = FP_LEN_DEF + FP_LEN_ECHO; +static int fp_flood; /* send as fast as possible */ +static uint32_t fp_interval = FP_DEF_INTVL * 1000; /* in milliseconds */ +static int fp_quiet; +static int fp_hex; +static char *fp_hba; /* name of interface to be used */ +static char fp_dev[64]; +static int fp_fd; /* file descriptor for openfc ioctls */ +static void *fp_buf; /* sending buffer */ +static int fp_debug; +static char *host; +static struct port_attributes *port_attrs; + +struct fp_stats { + uint32_t fp_tx_frames; + uint32_t fp_rx_frames; + uint32_t fp_rx_errors; + uint64_t fp_transit_time_us; /* total transit time in microseconds */ + uint32_t fp_rx_times; /* valid times on receive */ +}; +static struct fp_stats fp_stats; + +#define hton24(p, v) \ + do { \ + p[0] = (((v) >> 16) & 0xFF); \ + p[1] = (((v) >> 8) & 0xFF); \ + p[2] = ((v) & 0xFF); \ + } while (0) + +#define hton64(p, v) \ + do { \ + p[0] = (u_char) ((v) >> 56) & 0xFF; \ + p[1] = (u_char) ((v) >> 48) & 0xFF; \ + p[2] = (u_char) ((v) >> 40) & 0xFF; \ + p[3] = (u_char) ((v) >> 32) & 0xFF; \ + p[4] = (u_char) ((v) >> 24) & 0xFF; \ + p[5] = (u_char) ((v) >> 16) & 0xFF; \ + p[6] = (u_char) ((v) >> 8) & 0xFF; \ + p[7] = (u_char) (v) & 0xFF; \ + } while (0) + +__attribute__((__format__(__printf__, 2, 3))) +static void sa_log_func(const char *func, const char *format, ...); +__attribute__((__format__(__printf__, 3, 4))) +static void sa_log_err(int, const char *func, const char *format, ...); +static void sa_log_output(const char *buf); + +/* + * Log message. + */ +#define SA_LOG(...) \ + do { sa_log_func(__func__, __VA_ARGS__); } while (0) + +#define SA_LOG_ERR(error, ...) \ + do { sa_log_err(error, NULL, __VA_ARGS__); } while (0) + +/* + * Logging exits. + */ +#define SA_LOG_EXIT(...) \ + do { sa_log_func(__func__, __VA_ARGS__); \ + if (fp_debug) \ + sa_log_func(__func__, \ + "Exiting at %s:%d", __FILE__, __LINE__); \ + exit(1); \ + } while (0) + +#define SA_LOG_ERR_EXIT(error, ...) \ + do { sa_log_func(__func__, __VA_ARGS__); \ + if (fp_debug) \ + sa_log_err(error, __func__, \ + "Exiting at %s:%d", __FILE__, __LINE__); \ + else \ + sa_log_err(error, NULL, NULL); \ + exit(1); \ + } while (0) + +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +/* + * log with a variable argument list. + */ +static void +sa_log_va(const char *func, const char *format, va_list arg) +{ + size_t len; + size_t flen; + int add_newline; + char sa_buf[SA_LOG_BUF_LEN]; + char *bp; + + /* + * If the caller didn't provide a newline at the end, we will. + */ + len = strlen(format); + add_newline = 0; + if (!len || format[len - 1] != '\n') + add_newline = 1; + bp = sa_buf; + len = sizeof(sa_buf); + if (func) { + flen = snprintf(bp, len, "%s: ", func); + len -= flen; + bp += flen; + } + flen = vsnprintf(bp, len, format, arg); + if (add_newline && flen < len) { + bp += flen; + *bp++ = '\n'; + *bp = '\0'; + } + sa_log_output(sa_buf); +} + +/* + * log with function name. + */ +static void +sa_log_func(const char *func, const char *format, ...) +{ + va_list arg; + + va_start(arg, format); + if (fp_debug) + sa_log_va(func, format, arg); + else + sa_log_va(NULL, format, arg); + va_end(arg); +} + +/* + * log with error number. + */ +static void +sa_log_err(int error, const char *func, const char *format, ...) +{ + va_list arg; + char buf[SA_LOG_BUF_LEN]; + + strerror_r(error, buf, sizeof(buf)); + sa_log_func(func, "errno=%d %s", error, buf); + if (format) { + va_start(arg, format); + sa_log_va(func, format, arg); + va_end(arg); + } +} + +static void +sa_log_output(const char *buf) +{ + fprintf(stderr, "%s", buf); + fflush(stderr); +} + +static char * +sa_hex_format(char *buf, size_t buflen, + const unsigned char *data, size_t data_len, + unsigned int group_len, char *inter_group_sep) +{ + size_t rlen, tlen; + char *bp, *sep; + unsigned int i; + + rlen = buflen; + bp = buf; + sep = ""; + for (i = 0; rlen > 0 && i < data_len; ) { + tlen = snprintf(bp, rlen, "%s%2.2x", sep, data[i]); + rlen -= tlen; + bp += tlen; + i++; + sep = (i % group_len) ? "" : inter_group_sep; + } + return buf; +} + +/* + * Hex dump buffer to file. + */ +static void sa_hex_dump(unsigned char *bp, size_t len, FILE *fp) +{ + char lbuf[120]; + size_t tlen; + uint32_t offset = 0; + + while (len > 0) { + tlen = 16; /* bytes per line */ + if (tlen > len) + tlen = len; + sa_hex_format(lbuf, sizeof(lbuf), bp, tlen, 4, " "); + fprintf(fp, "%6x %s\n", offset, lbuf); + offset += tlen; + len -= tlen; + bp += tlen; + } +} + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +fc_wwn_t +fc_wwn_from_mac(uint64_t mac, uint32_t scheme, uint32_t port) +{ + fc_wwn_t wwn; + + assert(mac < (1ULL << 48)); + wwn = mac | ((fc_wwn_t) scheme << 60); + switch (scheme) { + case 1: + assert(port == 0); + break; + case 2: + assert(port < 0xfff); + wwn |= (fc_wwn_t) port << 48; + break; + default: + assert(1); + break; + } + return wwn; +} + +/* + * Handle WWN/MAC arguments + */ +static fc_wwn_t +fp_parse_wwn(const char *arg, char *msg, uint32_t scheme, uint32_t port) +{ + char *endptr; + fc_wwn_t wwn; + fc_wwn_t oui; + struct ether_addr mac; + + wwn = strtoull(arg, &endptr, 16); + if (*endptr != '\0') { + if (ether_aton_r(arg, &mac) == NULL && + ether_hostton(arg, &mac) != 0) { + SA_LOG_EXIT("invalid %s WWN or MAC addr %s", msg, arg); + } + oui = net48_get((net48_t *)mac.ether_addr_octet); + wwn = fc_wwn_from_mac(oui, scheme, port); + } + return wwn; +} + +/* + * Handle options. + */ +static void +fp_options(int argc, char *argv[]) +{ + int opt; + char *endptr; + float sec; + int targ_spec = 0; + + cmdname = basename(argv[0]); + if (argc <= 1) + fp_usage(); + + while ((opt = getopt(argc, argv, "?c:fi:h:qs:xF:P:N:")) != -1) { + switch (opt) { + case 'c': + fp_count = (int) strtoul(optarg, &endptr, 10); + if (*endptr != '\0') + SA_LOG_EXIT("bad count %s\n", optarg); + break; + case 'f': + fp_flood = 1; + break; + case 'i': + if (sscanf(optarg, "%f", &sec) != 1 || + sec < FP_MIN_INTVL) + SA_LOG_EXIT("bad interval %s\n", optarg); + fp_interval = sec * 1000; + break; + case 'h': + fp_hba = optarg; + break; + case 'q': + fp_quiet = 1; + break; + case 's': + /* maximum ECHO data allowed is 2108 */ + fp_len = strtoul(optarg, &endptr, 0); + if (*endptr != '\0' || fp_len > FP_LEN_MAX) + SA_LOG_EXIT("Bad size %s for FC ELS ECHO " + "data, max %lu bytes allowed.\n", + optarg, FP_LEN_MAX); + if (fp_len < FP_LEN_MIN) + SA_LOG_EXIT("Bad size %s for FC ELS ECHO " + "data, min %d bytes allowed.\n", + optarg, FP_LEN_MIN); + /* add 4 bytes for the ECHO command */ + fp_len += FP_LEN_ECHO; + break; + case 'x': + fp_hex = 1; + break; + + /* + * -F specifies the target FC_ID. + */ + case 'F': + fp_did = strtoull(optarg, &endptr, 16); + if (*endptr != '\0') + SA_LOG_EXIT("bad target FC_ID %s\n", optarg); + targ_spec++; + break; + + /* + * The -P and -N flags take a world-wide name + * in hex, or an ethernet addr, or an etherhost + * entry from /etc/ethers. + */ + case 'N': + fp_node_wwn = fp_parse_wwn(optarg, "Node", 1, 0); + targ_spec++; + break; + + case 'P': + fp_port_wwn = fp_parse_wwn(optarg, "Port", 2, 0); + targ_spec++; + break; + + case '?': + default: + fp_usage(); /* exits */ + break; + } + } + argc -= optind; + argv += optind; + + if (fp_hba == NULL) + SA_LOG_EXIT("FCoE interface not specified"); + + if (targ_spec == 0) + SA_LOG_EXIT("no target specified"); + + if (targ_spec > 1) + SA_LOG_EXIT("too many targets specified;" + " only one is allowed."); + + return; +} + +/* + * Lookup specified adapter using HBAAPI. + */ +static int +fp_find_hba(void) +{ + struct stat statbuf; + char hba_dir[256]; + fc_wwn_t wwn = 0; + struct hba_wwn wwpn; + char *endptr; + uint32_t fcid; + int hba_cnt; + + hba_cnt = get_number_of_adapters(); + if (!hba_cnt) + SA_LOG_EXIT("No FCoE interfaces created"); + + /* + * Parse HBA spec. if there is one. + * These formats are tried: + * If pass in an interface name, it does not need + * to be validated here. The interface name can be + * anything. It will have to be found via HBAAPI + * library. It fails if not found. + * host = match the index . + * mac address xx:xx:xx:xx:xx:xx + * otherwise, try parsing as a wwn and match that. + */ + snprintf(hba_dir, sizeof(hba_dir), SYSFS_HBA_DIR "/%s", fp_hba); + if (!stat(hba_dir, &statbuf)) { + host = get_host_from_netdev(fp_hba); + } else if (strstr(fp_hba, "host") == fp_hba) { + (void) strtoul(fp_hba + strlen("host"), &endptr, 10); + if (*endptr != '\0') + SA_LOG_EXIT("invalid hba name %s", fp_hba); + host = strdup(fp_hba); + } else if (strstr(fp_hba, ":")) { + if (strlen(fp_hba) == strlen("xx:yy:aa:bb:cc:dd:ee:ff")) { + fc_wwn_t wwn1; + + wwn1 = fp_parse_wwn(fp_hba, "HBA", 2, 0); + wwn1 &= 0xffff000000000000; + wwn = fp_parse_wwn(&fp_hba[6], "HBA", 2, 0); + wwn &= 0x0000ffffffffffff; + wwn |= wwn1; + } else if (strlen(fp_hba) == strlen("aa:bb:cc:dd:ee:ff")) { + wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0); + } else { + SA_LOG_EXIT("invalid WWPN or MAC address %s", fp_hba); + } + hton64(wwpn.wwn, wwn); + host = get_host_by_wwpn(wwpn); + } else { + wwn = strtoull(fp_hba, &endptr, 16); + if (wwn < 0x1000000) { + fcid = wwn; + host = get_host_by_fcid(fcid); + } else { + if (*endptr != '\0') + SA_LOG_EXIT("unsupported hba name"); + wwn = fp_parse_wwn(fp_hba, "HBA", 2, 0); + hton64(wwpn.wwn, wwn); + host = get_host_by_wwpn(wwpn); + } + } + + if (!host) { + SA_LOG("FCoE interface %s not found", fp_hba); + return 0; + } + + snprintf(fp_dev, sizeof(fp_dev), "fc_%s", host); + + port_attrs = get_port_attribs(host); + if (!port_attrs) { + free(host); + return 0; + } + + return 1; +} + +static void +fp_report(void) +{ + double loss; + struct fp_stats *sp = &fp_stats; + + loss = 100.0 * (sp->fp_tx_frames - sp->fp_rx_frames) / sp->fp_tx_frames; + printf("%d frames sent, %d received %d errors, %.3f%% loss, " + "avg. rt time %.3f ms\n", + sp->fp_tx_frames, sp->fp_rx_frames, sp->fp_rx_errors, loss, + sp->fp_rx_times ? sp->fp_transit_time_us * 1.0 / + (1000.0 * sp->fp_rx_times) : 0.0); +} + +/* + * Lookup ID from port name or node name. + */ +static int +fp_ns_get_id(uint32_t op, fc_wwn_t wwn, char *response, size_t *resp_len) +{ + struct ct_get_id { + struct fc_ct_hdr hdr; + net64_t wwn; + } ct; + struct fc_bsg_request cdb; + struct fc_bsg_reply reply; + struct sg_io_v4 sg_io; + size_t actual_len; + int cmd, rc = 0; + + memset((char *)&cdb, 0, sizeof(cdb)); + memset(&ct, 0, sizeof(ct)); + ct.hdr.ct_rev = FC_CT_REV; + hton24(ct.hdr.ct_in_id, 0xfffffc); + ct.hdr.ct_fs_type = FC_FST_DIR; + ct.hdr.ct_fs_subtype = FC_NS_SUBTYPE; + ct.hdr.ct_options = 0; + ct.hdr.ct_cmd = htons(op); + ct.hdr.ct_mr_size = *resp_len; + net64_put(&ct.wwn, wwn); + + cdb.msgcode = FC_BSG_HST_CT; + hton24(cdb.rqst_data.h_ct.port_id, 0xfffffc); + memcpy(&cdb.rqst_data.h_ct.preamble_word0, &ct.hdr, + 3 * sizeof(uint32_t)); + + sg_io.guard = 'Q'; + sg_io.protocol = BSG_PROTOCOL_SCSI; + sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; + sg_io.request_len = sizeof(cdb); + sg_io.request = (uintptr_t)&cdb; + sg_io.dout_xfer_len = sizeof(ct); + sg_io.dout_xferp = (uintptr_t)&ct; + sg_io.din_xfer_len = *resp_len; + sg_io.din_xferp = (uintptr_t)response; + sg_io.max_response_len = sizeof(reply); + sg_io.response = (uintptr_t)&reply; + sg_io.timeout = 1000; /* millisecond */ + memset(&reply, 0, sizeof(reply)); + memset(response, 0, *resp_len); + + rc = ioctl(fp_fd, SG_IO, &sg_io); + if (rc < 0) { + if (op == FC_NS_GID_PN) + printf("GID_PN error: %s\n", strerror(errno)); + if (op == FC_NS_GID_NN) + printf("GID_NN error: %s\n", strerror(errno)); + return rc; + } + + cmd = ((response[8]<<8) | response[9]) & 0xffff; + if (cmd != FC_FS_ACC) + return -1; + + actual_len = reply.reply_payload_rcv_len; + if (actual_len < *resp_len) + *resp_len = actual_len; + + return 0; +} + +static int fp_lookup_target(void) +{ + char response[32]; + size_t resp_len; + int rc; + + if (fp_did != 0) + return 0; + + if (fp_port_wwn != 0) { + resp_len = sizeof(response); + memset(&response, 0, sizeof(response)); + rc = fp_ns_get_id(FC_NS_GID_PN, fp_port_wwn, + response, &resp_len); + if (rc == 0) { + fp_did = ((response[17] << 16) & 0xff0000) | + ((response[18] << 8) & 0x00ff00) | + (response[19] & 0x0000ff); + return 0; + } + SA_LOG("cannot find fcid of destination @ wwpn 0x%llX", + fp_port_wwn); + } + if (fp_node_wwn != 0) { + resp_len = sizeof(response); + memset(&response, 0, sizeof(response)); + rc = fp_ns_get_id(FC_NS_GID_NN, fp_node_wwn, + response, &resp_len); + if (rc == 0) { + fp_did = ((response[17] << 16) & 0xff0000) | + ((response[18] << 8) & 0x00ff00) | + (response[19] & 0x0000ff); + return 0; + } + SA_LOG("cannot find fcid of destination @ wwnn 0x%llX", + fp_node_wwn); + } + return 1; +} + +/* + * fp_get_max_data_len - get the maximum ECHO data size by FCID + * @fcid: the fcid + * + * Returns the maximum allowed ECHO data size. The ECHO data plus the 4 bytes + * ECHO ELS command is the maximum payload allowed. + */ +static uint32_t fp_get_max_data_len(fc_fid_t fcid) +{ + struct port_attributes *rport_attrs; + uint32_t dlen = FP_LEN_DEF; + uint32_t maxframe_size; + char *rport; + + /* not found from disovered ports, if it's one of the + * WKA from FC-LS Table 30, use FC_MAX_PAYLOAD */ + if (FCID_IS_WKA(fcid)) { + dlen = FP_LEN_MAX; + goto out; + } + + rport = get_rport_by_fcid(fcid); + if (!rport) + goto out; + + rport_attrs = get_rport_attribs(rport); + if (!rport_attrs) + goto free_rport; + + maxframe_size = strtoul(rport_attrs->maxframe_size, NULL, 16); + dlen = maxframe_size - FP_LEN_ECHO; + + free(rport_attrs); +free_rport: + free(rport); + +out: + /* returns maximum allowed ECHO data length, excluding the 4 + * bytes ECHO command in the payload */ + return dlen; +} + +/* + * fp_check_data_len - figure out maximum allowed ECHO data size + * + * From FC-LS 4.2.4, for maximum allowed payload when Login exists + * + * "If a Login with the destination Nx_Port exists, the ECHO data field size + * is limited by 4 less than the smallest Receive Data_Field Size supported by + * the destination Nx_Port, the Fabric, and the source Nx_Port for the class + * of service being use." + * + * So, here we figure out the minimum of the source PortMaxFrameSize, the target + * PortMaxFraemSize, and the Domain Controller (Fabric) PortMaxFrameSize + * (default to be FC_MAX_PAYLOAD). For any FCID that is in FC-LS Table 30 WKA, + * use FP_LEN_MAX for ECHO data, i.e., FC_MAX_PAYLOAD - 4. + */ +static void fp_check_data_len(void) +{ + fc_fid_t sid; + uint32_t slen = 0; + uint32_t dlen = 0; + uint32_t flen = 0; + uint32_t plen = FP_LEN_DEF; + uint32_t maxframe_size; + + /* find out maximum payload supported by the fabric */ + flen = fp_get_max_data_len(FC_WKA_FABRIC_CONTROLLER); + if (!flen) { + flen = fp_get_max_data_len(FC_WKA_DIRECTORY_SERVICE); + if (!flen) + flen = FP_LEN_MAX; + } + + /* find out maximum payload supported by the target */ + dlen = fp_get_max_data_len(fp_did); + if (!dlen) + dlen = FP_LEN_DEF; + + + maxframe_size = strtoul(port_attrs->maxframe_size, NULL, 16); + sid = strtoul(port_attrs->port_id, NULL, 16); + + slen = maxframe_size - FP_LEN_ECHO; + plen = MIN(flen, MIN(slen, dlen)); + + printf("Maximum ECHO data allowed by the Fabric (0x%06x) : %d bytes.\n" + "Maximum ECHO data allowed by the Source (0x%06x) : %d bytes.\n" + "Maximum ECHO data allowed by the Target (0x%06x) : %d bytes.\n" + "Maximum ECHO data requested from user input (-s) : %" PRIu32 " " + "(default %d) bytes.\n", + FC_WKA_FABRIC_CONTROLLER, flen, sid, slen, fp_did, dlen, + (uint32_t)(fp_len - FP_LEN_ECHO), FP_LEN_DEF); + + /* fp_len is the total payload, including 4 bytes for ECHO command */ + fp_len = MIN(fp_len, plen + FP_LEN_ECHO); + printf("Actual FC ELS ECHO data size used : %" PRIu32 " bytes.\n" + "Actual FC ELS ECHO payload size used : %d bytes " + "(including %zu bytes ECHO command).\n", + (uint32_t)(fp_len - FP_LEN_ECHO), fp_len, FP_LEN_ECHO); +} + +/* + * ELS_ECHO request format being used. + * Put a sequence number in the payload, followed by the pattern. + */ +struct fcping_echo { + net8_t fe_op; /* opcode */ + net24_t fe_resvd; /* reserved, must be zero */ + net32_t fe_seq; /* sequence number */ +}; + +/* + * Setup buffer to be sent. + */ +static void +fp_buf_setup(void) +{ + struct fcping_echo *ep; + net8_t *pp; + int len; + int i; + + /* + * Alloc extra in case of odd len or shorter than minimum. + */ + len = fp_len + sizeof(*ep) + sizeof(net32_t); + ep = calloc(1, len); + if (ep == NULL) + SA_LOG_ERR_EXIT(errno, "calloc %d bytes failed", len); + ep->fe_op = ELS_ECHO; + net32_put(&ep->fe_seq, 1); /* starting sequence number */ + i = 0; + for (pp = (net8_t *) (ep + 1); pp < (net8_t *) ep + fp_len; pp++) + *pp = i++; + fp_buf = ep; +} + +static unsigned long long +fp_get_time_usec(void) +{ +#ifdef _POSIX_TIMERS + struct timespec ts; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rc) + SA_LOG_ERR_EXIT(errno, "clock_gettime error"); + return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; +#else +#warning no _POSIX_TIMERS + struct timeval ts; + + gettimeofday(&ts, NULL); + return ts.tv_sec * 1000000ULL + ts.tv_usec; +#endif /* _POSIX_TIMERS */ +} + +static int +send_els_echo(int fp_fd, void *fp_buf, uint32_t fp_len, + unsigned char *resp, uint32_t *resp_len, fc_fid_t fp_did) +{ + struct fc_bsg_request cdb; + char sense[MAX_SENSE_LEN]; + struct sg_io_v4 sg_io; + int rc; + + cdb.msgcode = FC_BSG_HST_ELS_NOLOGIN; + cdb.rqst_data.h_els.command_code = ELS_ECHO; + hton24(cdb.rqst_data.h_els.port_id, fp_did); + + sg_io.guard = 'Q'; + sg_io.protocol = BSG_PROTOCOL_SCSI; + sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; + sg_io.request_len = sizeof(cdb); + sg_io.request = (unsigned long)&cdb; + sg_io.dout_xfer_len = fp_len; + sg_io.dout_xferp = (unsigned long)fp_buf; + sg_io.din_xfer_len = *resp_len; + sg_io.din_xferp = (unsigned long)resp; + sg_io.max_response_len = sizeof(sense); + sg_io.response = (unsigned long)sense; + sg_io.timeout = 20000; + memset(sense, 0, sizeof(sense)); + + rc = ioctl(fp_fd, SG_IO, &sg_io); + if (rc < 0) + return 1; + + *resp_len = sg_io.din_xfer_len - sg_io.din_resid; + return 0; +} + +/* + * Send ELS ECHO. + */ +static int fp_send_ping(void) +{ + struct fp_stats *sp = &fp_stats; + struct fcping_echo *ep; + int rc; + uint32_t resp_len; + unsigned char *resp; + unsigned long long tx_time; + unsigned long long usec; + char msg[80]; + char time_msg[80]; + + resp_len = fp_len + FP_LEN_PAD; /* for odd-byte padding and then some */ + resp = calloc(1, resp_len); + if (resp == NULL) + SA_LOG_EXIT("calloc %d bytes failed", resp_len); + + sp->fp_tx_frames++; + if (fp_len >= sizeof(*ep)) { + ep = (struct fcping_echo *) fp_buf; + net32_put(&ep->fe_seq, sp->fp_tx_frames); + } + tx_time = fp_get_time_usec(); + + /* send ELS ECHO frame and receive */ + rc = send_els_echo(fp_fd, fp_buf, fp_len, resp, &resp_len, fp_did); + if (rc) { + sp->fp_rx_errors++; + printf("echo %4d error: %s\n", + sp->fp_tx_frames, strerror(errno)); + } else { + usec = fp_get_time_usec(); + sp->fp_rx_frames++; + ep = (struct fcping_echo *) resp; + if (usec < tx_time) { + snprintf(time_msg, sizeof(time_msg), + "time unknown now %llx old %llx", + usec, tx_time); + usec = 0; /* as if time went backwards */ + } else { + usec = usec - tx_time; + snprintf(time_msg, sizeof(time_msg), + "%6.3f ms", usec / 1000.0); + sp->fp_transit_time_us += usec; + sp->fp_rx_times++; + } + if (ep->fe_op == ELS_LS_ACC) { + if (memcmp((char *) ep + 1, + (char *) fp_buf + 1, fp_len - 1) == 0) + snprintf(msg, sizeof(msg), "accepted"); + else { + sp->fp_rx_errors++; + snprintf(msg, sizeof(msg), + "accept data mismatches"); + } + } else if (ep->fe_op == ELS_LS_RJT) { + sp->fp_rx_errors++; + snprintf(msg, sizeof(msg), "REJECT received"); + } else { + sp->fp_rx_errors++; + snprintf(msg, sizeof(msg), + "op %x received", ep->fe_op); + } + if (fp_quiet == 0) + printf("echo %4d %-30s %s\n", + sp->fp_tx_frames, msg, time_msg); + } + if (fp_hex) { + printf("response length %u\n", resp_len); + sa_hex_dump(resp, resp_len, stdout); + printf("\n"); + } + free(resp); + return rc; +} + +static void fp_signal_handler(UNUSED int sig) +{ + /* + * Allow graceful termination of the + * for loop in fp_start. + */ + fp_count = 0; +} + +/* + * Main loop. + */ +static void fp_start(void) +{ + struct sigaction act; + int i; + int rc; + + memset(&act, 0, sizeof(act)); + act.sa_handler = fp_signal_handler; + act.sa_flags = 0; + + sigaction(SIGTERM, &act, NULL); /* Signal 15: kill */ + sigaction(SIGQUIT, &act, NULL); /* Signal 3: Ctrl-\ */ + sigaction(SIGINT, &act, NULL); /* Signal 2: Ctrl-C */ + + printf("Sending FC ELS ECHO from %s (%s) to 0x%X:\n", + port_attrs->port_id, fp_dev, fp_did); + + for (i = 0; fp_count == -1 || i < fp_count; i++) { + rc = fp_send_ping(); + if (rc != 0 && errno == EMSGSIZE) + break; + if (rc != 0 && errno == ECONNABORTED) + break; + if (fp_flood == 0) + usleep(fp_interval * 1000); + if (!fp_count) + break; + } +} + +/* + * Main. + */ +int main(int argc, char *argv[]) +{ + char bsg_dev[80]; + int rc = 1; + + fp_options(argc, argv); + if (fp_find_hba()) { + sprintf(bsg_dev, "/dev/bsg/%s", fp_dev); + fp_fd = open(bsg_dev, O_RDWR); + if (fp_fd < 0) + SA_LOG_ERR_EXIT(errno, + "open of %s failed", bsg_dev); + + if (!fp_lookup_target()) { + fp_check_data_len(); + fp_buf_setup(); + fp_start(); + fp_report(); + rc = 0; + } + free(port_attrs); + free(host); + close(fp_fd); + } + + return rc; +} diff --git a/fcrls.c b/fcrls.c new file mode 100644 index 0000000..df19410 --- /dev/null +++ b/fcrls.c @@ -0,0 +1,509 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef __u8 u8; +typedef __u16 u16; +typedef __u32 u32; +typedef __u64 u64; +#include +#include +#include "fc_els.h" +#include "scsi_bsg_fc.h" + +#define ntoh24(n) (u32) ((n)[0] << 16 | (n)[1] << 8 | (n)[2]) +#define hton24(h) { (h) >> 16 & 0xff, (h) >> 8 & 0xff, (h) & 0xff } + +#define SYSFS_FC_RPORTS "/sys/class/fc_remote_ports" + +struct rport_info { + int host_no; + int channel; + u32 number; + u32 port_id; + bool found; + bool online; +}; + +struct fcoe_fc_els_lesb { + __be32 lesb_link_fail; /* link failure count */ + __be32 lesb_vlink_fail; /* virtual link failure count */ + __be32 lesb_miss_fka; /* missing FIP keep-alive count */ + __be32 lesb_symb_err; /* symbol error during carrier count */ + __be32 lesb_err_block; /* errored block count */ + __be32 lesb_fcs_error; /* frame check sequence error count */ +} __attribute__((__packed__)); + +union rls_acc { + struct fc_els_lesb fs3; + struct fcoe_fc_els_lesb bb5; +} __attribute__((__packed__)); + +struct rls_rjt { + u8 er_resv; + u8 er_reason; + u8 er_explan; + u8 er_vendor; +} __attribute__((__packed__)); + +struct rls_rsp { + u8 rls_cmd; + u8 rls_resv[3]; + union rls_acc acc; + struct rls_rjt rjt; +} __attribute__((__packed__)); + +static char *rjt_reason[] = { + [1] = "Invalid command code", + [2] = "Invalid version level", + [3] = "Logical error", + [4] = "Invalid CT_IU size", + [5] = "Logical busy", + [7] = "Protocol error", + [9] = "Unable to perform command request", + [0xB] = "Command not supported", + [0xD] = "Server not available", + [0xE] = "Session could not be established", + [0xFF] = "Vendor specific", + [0x100] = "N/A", +}; + +static char *rjt_explan[] = { + [0] = "No additional explanation", + [1] = "Port Identifier not registered", + [2] = "Port Name not registered", + [3] = "Node Name not registered", + [4] = "Class of Service not registered", + [6] = "Initial Process Associator not registered", + [7] = "FC-4 Types not registered", + [8] = "Symbolic Port Name not registered", + [9] = "Symbolic Node Name not registered", + [0xA] = "Port Type not registered", + [0xC] = "Fabric Port Name not registered", + [0xD] = "Hard Address not registered", + [0xF] = "FC-4 Features not registered", + [0x10] = "Access denied", + [0x11] = "Unacceptable Port Identifier", + [0x12] = "Data base empty", + [0x13] = "No object registered in the specified scope", + [0x14] = "Domain ID not present", + [0x15] = "Port number not present", + [0x16] = "No device attached", + [0x17] = "invalid OX_ID-RX_ID combination", + [0x19] = "Request already in progress", + [0x1e] = "N_Port login required", + [0x29] = "insufficient resources", + [0x2a] = "unable to supply requested data", + [0x2c] = "Request not supported", + [0x2d] = "Invalid payload length", + [0x44] = "Invalid Port/Node_Name", + [0x46] = "Login Extension not supported", + [0x48] = "Authentication required", + [0x50] = "Periodic Scan Value not allowed", + [0x51] = "Periodic Scanning not supported", + [0x60] = "MAC addressing mode not supported", + [0x61] = "Proposed MAC address incorrectly formed", + [0xf0] = "Authorization Exception", + [0xf1] = "Authentication Exception", + [0xf2] = "Data base full", + [0xf3] = "Data base empty", + [0xf4] = "Processing request", + [0xf5] = "Unable to verify connection", + [0xf6] = "Devices not in a common zone", + [0x100] = "N/A", + +}; + +enum commands { + NONE = 0, + RLS_PORT, + RLS_FCID, + RLS_QUIET, + RLS_HELP, +}; + +/* RLS_QUIET */ +static bool quiet; + +/* : - has arg + * :: - has optional arg + * ; - arg is long opt + */ +static const struct option lopt[] = { + { "port", required_argument, NULL, RLS_PORT }, + { "fcid", required_argument, NULL, RLS_FCID }, + { "quiet", no_argument, NULL, RLS_QUIET }, + { "help", no_argument, NULL, RLS_HELP }, + { NULL, 0, NULL, 0 }, +}; + +static const char *lopt_usage[] = { + "rport bsg name, e.g., rport-7:0-1.", + "rport port FC_ID, e.g., 0xce000d.", + "disable verbose output.", + "print useage information.", + NULL, +}; + +#define bsg_error(format...) \ + ({ \ + fprintf(stderr, "ERROR: " format); \ + }) + +#define bsg_debug(format...) \ + ({ \ + if (!quiet) \ + printf("DEBUG: " format); \ + }) + +static char *els_rjt2str(int type, int code) +{ + char **str; + + str = (type == 0) ? rjt_reason : rjt_explan; + + if (code > 0xff) + code = 0x100; + + if (!str[code]) + code = 0x100; + + return str[code]; +} + +static int els_print_lesb(struct fcoe_fc_els_lesb *lesb) +{ + printf("RLS request accepted (LS_ACC), dumping status counters:\n" + "\tLink Failure Count = %u\n" + "\tVirtual Link Failure Count = %u\n" + "\tMissed Discovery Advertisement Count = %u\n" + "\tSymbol Error During Carrier Count = %u\n" + "\tErrored Block Count = %u\n" + "\tFrame Check Sequence Error Count = %u\n", + ntohl(lesb->lesb_link_fail), + ntohl(lesb->lesb_vlink_fail), + ntohl(lesb->lesb_miss_fka), + ntohl(lesb->lesb_symb_err), + ntohl(lesb->lesb_err_block), + ntohl(lesb->lesb_fcs_error)); + + return 0; +} + +static int els_print_rjt(struct rls_rjt *rjt) +{ + printf("RLS request rejected (LS_RJT), check reason code below:\n" + "\tReason Code = 0x%02x, %s.\n" + "\tExplain Code = 0x%02x, %s.\n", + rjt->er_reason, els_rjt2str(0, rjt->er_reason), + rjt->er_explan, els_rjt2str(1, rjt->er_explan)); + if (rjt->er_reason == ELS_RJT_VENDOR) + printf("\tVendor Code = 0x%02x (check with your vendor).\n", + rjt->er_vendor); + return 0; +} + +static int bsg_rport_els(int bsg, u8 els_code, void *req, int req_len, + void *rsp, int rsp_len) +{ + int rc; + char sense[96]; + struct fc_bsg_reply *reply = (struct fc_bsg_reply *)sense; + struct fc_bsg_request cdb = { + .msgcode = FC_BSG_RPT_ELS, + .rqst_data.r_els = { + .els_code = els_code, + } + }; + + struct sg_io_v4 sgio = { + .guard = 'Q', + .protocol = BSG_PROTOCOL_SCSI, + .subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT, + .request_len = sizeof(cdb), + .request = (uintptr_t)&cdb, + .dout_xfer_len = req_len, + .dout_xferp = (uintptr_t)req, + .din_xfer_len = rsp_len, + .din_xferp = (uintptr_t)rsp, + .max_response_len = sizeof(sense), + .response = (uintptr_t)&sense, + .timeout = 1000, + }; + memset(sense, 0, sizeof(sense)); + rc = ioctl(bsg, SG_IO, &sgio); + bsg_debug("ioctl returned %d: bsg_reply result=%d\n", + rc, reply->result); + return rc; +} + +static int bsg_rport_els_rls(int bsg, struct rport_info *rpi) +{ + int rc = EOPNOTSUPP; + struct fc_els_rls rls = { + .rls_cmd = ELS_RLS, + .rls_port_id = hton24(rpi->port_id), + }; + struct rls_rsp rsp; + + memset(&rsp, 0, sizeof(rsp)); + rc = bsg_rport_els(bsg, ELS_RLS, &rls, sizeof(rls), &rsp, sizeof(rsp)); + if (rc) { + bsg_error("bsg_rport_els(ELS_RLS) failed\n"); + return rc; + } + if (rsp.rls_cmd == ELS_LS_ACC) + return els_print_lesb(&rsp.acc.bb5); + + if (rsp.rls_cmd == ELS_LS_RJT) + return els_print_rjt(&rsp.rjt); + + bsg_error("Unknow response!\n"); + return EIO; +} + +static int rport_getid(struct rport_info *rpi) +{ + FILE *f; + char rp_sysfs[256]; + + if (rpi->found) + return 0; + snprintf(rp_sysfs, sizeof(rp_sysfs), "%s/rport-%d:%d-%d/port_id", + SYSFS_FC_RPORTS, rpi->host_no, rpi->channel, rpi->number); + f = fopen(rp_sysfs, "ro"); + if (!f) { + bsg_error("failed to fopen(%s)!\n", rp_sysfs); + return ENODEV; + } + if (1 != fscanf(f, "0x%6x", &rpi->port_id)) { + bsg_error("failed to fscanf(%s)\n", rp_sysfs); + fclose(f); + return ENODEV; + } + if (rpi->port_id & 0xff000000) { + bsg_error("rport %s:invalid fcid 0x%x\n", rp_sysfs, + rpi->port_id); + rpi->port_id = 0; + fclose(f); + return ENODEV; + } + fclose(f); + return 0; +} + +/* + * parse a string in format of rport-%d:%d-%d, and get the + * corresponding rport info. + * rport-%d:%d-%d + */ +static int rport_parse(const char *s, struct rport_info *rpi) +{ + if (!s) + return EINVAL; + memset(rpi, 0, sizeof(*rpi)); + if (3 != sscanf(s, "rport-%d:%d-%d", &rpi->host_no, &rpi->channel, + &rpi->number)) + return ENODEV; + if (rport_getid(rpi)) + return ENODEV; + return 0; +} + +#define RPORT_ONLINE "Online" +static int rport_check_state(struct rport_info *rpi) +{ + FILE *f; + char rp_sysfs[256]; + char rp_state[256]; + + rpi->online = false; + if (!rpi->found) + return EINVAL; + + snprintf(rp_sysfs, sizeof(rp_sysfs), "%s/rport-%d:%d-%d/port_state", + SYSFS_FC_RPORTS, rpi->host_no, rpi->channel, rpi->number); + + f = fopen(rp_sysfs, "ro"); + if (!f) { + bsg_error("failed to fopen(%s)!\n", rp_sysfs); + return ENODEV; + } + if (!fgets(rp_state, sizeof(rp_state), f)) { + bsg_error("failed to fgets(%s)!\n", rp_sysfs); + fclose(f); + return ENODEV; + } + if (strncmp(rp_state, RPORT_ONLINE, strlen(RPORT_ONLINE))) { + bsg_error("rport 0x%x %s:must be %s\n", rpi->port_id, + rp_state, RPORT_ONLINE); + fclose(f); + return ENODEV; + } + rpi->online = true; + fclose(f); + return 0; +} +/* locate rport by fcid */ +static int rport_find(struct rport_info *rpi) +{ + int n; + struct dirent **namelist; + struct rport_info rpii; + + if (rpi->found) + return 0; + + if (!rpi->port_id) + return ENODEV; + + n = scandir(SYSFS_FC_RPORTS, &namelist, 0, alphasort); + if (n < 0) { + bsg_error("failed to scandir %s\n", SYSFS_FC_RPORTS); + return ENODEV; + } + while (n--) { + if ((namelist[n]->d_type != DT_DIR) && + (namelist[n]->d_type != DT_LNK)) + goto free_name; + if (rport_parse(namelist[n]->d_name, &rpii)) + goto free_name; + if (rpi->port_id != rpii.port_id) + goto free_name; + rpii.found = true; + memcpy(rpi, &rpii, sizeof(rpii)); + bsg_debug("found rport 0x%06x as rport-%d:%d-%d\n", + rpi->port_id, rpi->host_no, rpi->channel, + rpi->number); +free_name: + free(namelist[n]); + } + free(namelist); + return 0; +} + +static void bsg_usage(int status) +{ + int i, n; + + if (status) + bsg_error("Failed! %s (Errno %d)!\n", strerror(status), status); + + n = sizeof(lopt)/sizeof(struct option) - 1; + printf("Usage: fcrls\n"); + for (i = 0; i < n; i++) + printf("\t--%s: %s\n", lopt[i].name, lopt_usage[i]); + exit(status); +} + + +int main(int argc, char *argv[]) +{ + int rc = ENODEV; + int opt; + int bsg_dev; + char *endptr; + char *bsg_name = NULL; + struct rport_info rpi; + + rpi.found = false; + while ((opt = getopt_long(argc, argv, "", lopt, NULL)) != -1) { + switch (opt) { + case RLS_PORT: + if (rport_parse(optarg, &rpi)) { + bsg_error("%s format incorrect, must be:" + "rport-host:channel-number\n", + optarg); + bsg_usage(EINVAL); + } + rpi.found = true; + goto out_rls; + case RLS_FCID: + rpi.found = false; + rpi.port_id = strtoull(optarg, &endptr, 16); + if (*endptr != '\0') { + bsg_error("%s has no valid FCID\n", optarg); + bsg_usage(EINVAL); + } + if (rport_find(&rpi)) { + bsg_error("%s is not a rport\n", optarg); + bsg_usage(ENODEV); + } + goto out_rls; + case RLS_QUIET: + quiet = true; + break; + case RLS_HELP: + bsg_usage(0); + break; + } + } +out_rls: + /* bsg device name */ + if (!rpi.found) + bsg_usage(ENODEV); + + if (asprintf(&bsg_name, "/dev/bsg/rport-%d:%d-%d", + rpi.host_no, rpi.channel, rpi.number) < 0) { + rc = ENOMEM; + bsg_error("not enough memory!\n"); + goto out_error; + } + /* open bsg device */ + bsg_dev = open(bsg_name, O_RDWR); + if (bsg_dev < 0) { + bsg_error("failed to open %s!\n", bsg_name); + goto out_free; + } + /* check port state */ + if (rport_check_state(&rpi) || (!rpi.online)) { + bsg_error("rport 0x%x is not online!\n", rpi.port_id); + goto out_close; + } + /* send rls */ + rc = bsg_rport_els_rls(bsg_dev, &rpi); + if (rc) { + bsg_error("Faild to bsg_rport_els_rls\n"); + goto out_close; + } + rc = 0; + +out_close: + close(bsg_dev); +out_free: + free(bsg_name); +out_error: + return rc; +} diff --git a/fipvlan.c b/fipvlan.c new file mode 100644 index 0000000..7c00c7c --- /dev/null +++ b/fipvlan.c @@ -0,0 +1,1019 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "fcoe_utils_version.h" +#include "fip.h" +#include "fcoemon_utils.h" +#include "fcoe_utils.h" +#include "rtnetlink.h" + +#define FIP_LOG(...) sa_log(__VA_ARGS__) +#define FIP_LOG_ERR(error, ...) sa_log_err(error, __func__, __VA_ARGS__) +#define FIP_LOG_ERRNO(...) sa_log_err(errno, __func__, __VA_ARGS__) +#define FIP_LOG_DBG(...) sa_log_debug(__VA_ARGS__) + +#define MAX_VLAN_RETRIES 50 + +/* global configuration */ + +struct { + char **namev; + int namec; + bool automode; + bool create; + bool start; + bool vn2vn; + bool debug; + bool link_up; + int link_retry; + char suffix[256]; +} config = { + .namev = NULL, + .namec = 0, + .automode = false, + .create = false, + .vn2vn = false, + .debug = false, + .link_up = false, + .link_retry = 20, + .suffix = "", +}; + +int (*fcoe_instance_start)(const char *ifname); + +char *exe; + +static struct pollfd *pfd = NULL; +static int pfd_len = 0; + +static void pfd_add(int fd) +{ + struct pollfd *npfd; + int i; + + for (i = 0; i < pfd_len; i++) + if (pfd[i].fd == fd) + return; + + npfd = realloc(pfd, (pfd_len + 1) * sizeof(struct pollfd)); + if (!npfd) { + perror("realloc fail"); + return; + } + pfd = npfd; + pfd[pfd_len].fd = fd; + pfd[pfd_len].events = POLLIN; + pfd_len++; +} + +static void pfd_remove(int fd) +{ + struct pollfd *npfd; + int i; + + for (i = 0; i < pfd_len; i++) { + if (pfd[i].fd == fd) + break; + } + if (i == pfd_len) + return; + memmove(&pfd[i], &pfd[i+1], (--pfd_len - i) * sizeof(struct pollfd)); + npfd = realloc(pfd, pfd_len * sizeof(struct pollfd)); + if (npfd) + pfd = npfd; + else + perror("realloc failed"); +} + +TAILQ_HEAD(iff_list_head, iff); + +struct iff { + int ps; /* packet socket file descriptor */ + int ifindex; + int iflink; + char ifname[IFNAMSIZ]; + unsigned char mac_addr[ETHER_ADDR_LEN]; + bool running; + bool is_vlan; + short int vid; + bool linkup_sent; + bool req_sent; + bool resp_recv; + bool fip_ready; + bool fcoe_started; + TAILQ_ENTRY(iff) list_node; + struct iff_list_head vlans; +}; + +struct iff_list_head interfaces = TAILQ_HEAD_INITIALIZER(interfaces); + +TAILQ_HEAD(fcf_list_head, fcf); + +struct fcf { + int ifindex; + uint16_t vlan; + unsigned char mac_addr[ETHER_ADDR_LEN]; + TAILQ_ENTRY(fcf) list_node; +}; + +struct fcf_list_head fcfs = TAILQ_HEAD_INITIALIZER(fcfs); +static struct fcf_list_head vn2vns = TAILQ_HEAD_INITIALIZER(vn2vns); + +static int create_and_start_vlan(struct fcf *fcf, bool vn2vn); + +static struct fcf *lookup_fcf(struct fcf_list_head *head, int ifindex, + uint16_t vlan, unsigned char *mac) +{ + struct fcf *fcf; + + TAILQ_FOREACH(fcf, head, list_node) + if ((ifindex == fcf->ifindex) && (vlan == fcf->vlan)) { + if ((!mac) || (memcmp(mac, fcf->mac_addr, ETHER_ADDR_LEN) == 0)) + return fcf; + } + return NULL; +} + +static struct iff *lookup_iff(int ifindex, const char *ifname) +{ + struct iff *iff; + struct iff *vlan; + + if (!ifindex && !ifname) + return NULL; + + TAILQ_FOREACH(iff, &interfaces, list_node) { + if ((!ifindex || ifindex == iff->ifindex) && + (!ifname || strcmp(ifname, iff->ifname) == 0)) + return iff; + + TAILQ_FOREACH(vlan, &iff->vlans, list_node) + if ((!ifindex || ifindex == vlan->ifindex) && + (!ifname || strcmp(ifname, vlan->ifname) == 0)) + return vlan; + } + return NULL; +} + +static struct iff *lookup_vlan(int ifindex, short int vid) +{ + struct iff *real_dev, *vlan; + TAILQ_FOREACH(real_dev, &interfaces, list_node) + if (real_dev->ifindex == ifindex) + TAILQ_FOREACH(vlan, &real_dev->vlans, list_node) + if (vlan->vid == vid) + return vlan; + return NULL; +} + +static struct iff *find_vlan_real_dev(struct iff *vlan) +{ + struct iff *real_dev; + TAILQ_FOREACH(real_dev, &interfaces, list_node) { + if (real_dev->ifindex == vlan->iflink) + return real_dev; + } + return NULL; +} + +struct fip_tlv_ptrs { + struct fip_tlv_mac_addr *mac; + struct fip_tlv_vlan *vlan[370]; + unsigned int vlanc; +}; + +#define SET_BIT(b, n) ((b) |= (1 << (n))) + +#define TLV_LEN_CHECK(t, l) ({ \ + int _tlc = ((t)->tlv_len != (l)) ? 1 : 0; \ + if (_tlc) \ + FIP_LOG("bad length for TLV of type %d", (t)->tlv_type); \ + _tlc; \ + }) + +/** + * fip_parse_tlvs - parse type/length/value encoded FIP descriptors + * @ptr: pointer to beginning of FIP TLV payload, the first descriptor + * @len: total length of all TLVs, in double words + * @tlv_ptrs: pointers to type specific structures to fill out + */ +static unsigned int +fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs) +{ + struct fip_tlv_hdr *tlv = ptr; + unsigned int bitmap = 0; + + tlv_ptrs->vlanc = 0; + while (len > 0) { + switch (tlv->tlv_type) { + case FIP_TLV_MAC_ADDR: + if (TLV_LEN_CHECK(tlv, 2)) + break; + SET_BIT(bitmap, FIP_TLV_MAC_ADDR); + tlv_ptrs->mac = (struct fip_tlv_mac_addr *) tlv; + break; + case FIP_TLV_VLAN: + if (TLV_LEN_CHECK(tlv, 1)) + break; + SET_BIT(bitmap, FIP_TLV_VLAN); + tlv_ptrs->vlan[tlv_ptrs->vlanc++] = (void *) tlv; + break; + default: + /* unexpected or unrecognized descriptor */ + FIP_LOG("unrecognized TLV type %d", tlv->tlv_type); + break; + } + len -= tlv->tlv_len; + tlv = ((void *) tlv) + (tlv->tlv_len << 2); + }; + return bitmap; +} + +/** + * fip_recv_vlan_note - parse a FIP VLAN Notification + * @fh: FIP header, the beginning of the received FIP frame + * @ifindex: index of interface this was received on + * @vn2vn: true if vn2vn notification + */ +static int fip_recv_vlan_note(struct fiphdr *fh, int ifindex, bool vn2vn) +{ + struct fip_tlv_ptrs tlvs; + struct fcf_list_head *head; + struct fcf *fcf; + struct iff *iff; + uint16_t vlan; + unsigned int bitmap, required_tlvs; + int len; + unsigned int i; + + FIP_LOG_DBG("received FIP VLAN Notification"); + + len = ntohs(fh->fip_desc_len); + + required_tlvs = (1 << FIP_TLV_MAC_ADDR) | (1 << FIP_TLV_VLAN); + + tlvs.mac = NULL; /* Silence incorrect GCC warning */ + bitmap = fip_parse_tlvs((fh + 1), len, &tlvs); + if ((bitmap & required_tlvs) != required_tlvs) + return -1; + + if (vn2vn) + head = &vn2vns; + else + head = &fcfs; + + iff = lookup_iff(ifindex, NULL); + if (iff) + iff->resp_recv = true; + + for (i = 0; i < tlvs.vlanc; i++) { + vlan = ntohs(tlvs.vlan[i]->vlan); + if (lookup_fcf(head, ifindex, vlan, tlvs.mac->mac_addr)) + continue; + + fcf = malloc(sizeof(*fcf)); + if (!fcf) { + FIP_LOG_ERRNO("malloc failed"); + break; + } + memset(fcf, 0, sizeof(*fcf)); + fcf->ifindex = ifindex; + fcf->vlan = vlan; + memcpy(fcf->mac_addr, tlvs.mac->mac_addr, ETHER_ADDR_LEN); + TAILQ_INSERT_TAIL(head, fcf, list_node); + if (!config.create) + continue; + create_and_start_vlan(fcf, vn2vn); + } + + return 0; +} + +static int fip_vlan_handler(struct fiphdr *fh, struct sockaddr_ll *sa, + UNUSED void *arg) +{ + /* We only care about VLAN Notifications */ + if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) { + FIP_LOG_DBG("ignoring FIP packet, protocol %d", + ntohs(fh->fip_proto)); + return -1; + } + + switch (fh->fip_subcode) { + case FIP_VLAN_NOTE: + if (config.vn2vn) { + FIP_LOG_DBG("ignoring FCF response in vn2vn mode\n"); + return -1; + } + return fip_recv_vlan_note(fh, sa->sll_ifindex, false); + case FIP_VLAN_NOTE_VN2VN: + if (!config.vn2vn) { + FIP_LOG_DBG("ignoring VN2VN response in fabric mode\n"); + return -1; + } + return fip_recv_vlan_note(fh, sa->sll_ifindex, true); + default: + FIP_LOG_DBG("ignored FIP VLAN packet with subcode %d", + fh->fip_subcode); + return -1; + } +} + +/** + * rtnl_recv_newlink - parse response to RTM_GETLINK, or an RTM_NEWLINK event + * @nh: netlink message header, beginning of received netlink frame + */ +static void rtnl_recv_newlink(struct nlmsghdr *nh) +{ + struct ifinfomsg *ifm = NLMSG_DATA(nh); + struct rtattr *ifla[__IFLA_MAX]; + struct rtattr *linkinfo[__IFLA_INFO_MAX]; + struct rtattr *vlan[__IFLA_VLAN_MAX]; + struct iff *iff, *real_dev; + struct fcf_list_head *head; + bool running; + + if (config.vn2vn) + head = &vn2vns; + else + head = &fcfs; + + FIP_LOG_DBG("RTM_NEWLINK: ifindex %d, type %d, flags %x", + ifm->ifi_index, ifm->ifi_type, ifm->ifi_flags); + + /* We only deal with Ethernet interfaces */ + if (ifm->ifi_type != ARPHRD_ETHER) + return; + + /* not on bond master, but rather allow FIP on the slaves below */ + if (ifm->ifi_flags & IFF_MASTER) + return; + + running = !!(ifm->ifi_flags & (IFF_RUNNING | IFF_SLAVE)); + iff = lookup_iff(ifm->ifi_index, NULL); + if (iff) { + int ifindex; + + /* already tracking, update operstate and return */ + iff->running = running; + if (!iff->running) { + pfd_remove(iff->ps); + return; + } + pfd_add(iff->ps); + if (!config.start) + return; + + FIP_LOG_DBG("Checking for FCoE on %sif %d", + iff->is_vlan ? "VLAN " : "", iff->ifindex); + if (iff->is_vlan) { + real_dev = find_vlan_real_dev(iff); + if (!real_dev) { + FIP_LOG_ERR(ENODEV, "VLAN %d without a parent", + iff->ifindex); + return; + } + ifindex = real_dev->ifindex; + } else + ifindex = iff->ifindex; + + if (!iff->fcoe_started && + lookup_fcf(head, ifindex, iff->vid, NULL)) { + printf("Starting FCoE on interface %s\n", + iff->ifname); + fcoe_instance_start(iff->ifname); + iff->fcoe_started = true; + } + return; + } + + iff = malloc(sizeof(*iff)); + if (!iff) { + FIP_LOG_ERRNO("malloc failed"); + return; + } + memset(iff, 0, sizeof(*iff)); + TAILQ_INIT(&iff->vlans); + + parse_ifinfo(ifla, nh); + + iff->ifindex = ifm->ifi_index; + iff->running = running; + iff->fip_ready = false; + if (ifla[IFLA_LINK]) + iff->iflink = *(int *)RTA_DATA(ifla[IFLA_LINK]); + else + iff->iflink = iff->ifindex; + memcpy(iff->mac_addr, RTA_DATA(ifla[IFLA_ADDRESS]), ETHER_ADDR_LEN); + strncpy(iff->ifname, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ); + + if (ifla[IFLA_LINKINFO]) { + parse_linkinfo(linkinfo, ifla[IFLA_LINKINFO]); + /* Track VLAN devices separately */ + if (linkinfo[IFLA_INFO_KIND] && + !strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vlan")) { + iff->is_vlan = true; + parse_vlaninfo(vlan, linkinfo[IFLA_INFO_DATA]); + iff->vid = *(int *)RTA_DATA(vlan[IFLA_VLAN_ID]); + real_dev = find_vlan_real_dev(iff); + if (!real_dev) { + free(iff); + return; + } + TAILQ_INSERT_TAIL(&real_dev->vlans, iff, list_node); + if (!iff->running) { + FIP_LOG_DBG("vlan if %d not running, " + "starting", iff->ifindex); + rtnl_set_iff_up(iff->ifindex, NULL); + } + return; + } + /* ignore bonding interfaces */ + if (linkinfo[IFLA_INFO_KIND] && + !strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "bond")) { + free(iff); + return; + } + } + TAILQ_INSERT_TAIL(&interfaces, iff, list_node); +} + +/* command line arguments */ + +#define GETOPT_STR "acdf:l:m:suhv" + +static const struct option long_options[] = { + { "auto", no_argument, NULL, 'a' }, + { "create", no_argument, NULL, 'c' }, + { "start", no_argument, NULL, 's' }, + { "debug", no_argument, NULL, 'd' }, + { "suffix", required_argument, NULL, 'f' }, + { "link-retry", required_argument, NULL, 'l' }, + { "mode", required_argument, NULL, 'm' }, + { "link-up", required_argument, NULL, 'u' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +static void help(int status) +{ + printf( + "Usage: %s [ options ] [ network interfaces ]\n" + "Options:\n" + " -a, --auto Auto select Ethernet interfaces\n" + " -c, --create Create system VLAN devices\n" + " -d, --debug Enable debugging output\n" + " -s, --start Start FCoE login automatically\n" + " -f, --suffix Append the suffix to VLAN interface name\n" + " -l, --link-retry Number of retries for link up\n" + " -m, --mode Link mode, either fabric or vn2vn\n" + " -u, --link-up Leave link up after FIP response\n" + " -h, --help Display this help and exit\n" + " -v, --version Display version information and exit\n", + exe); + + exit(status); +} + +static void parse_cmdline(int argc, char **argv) +{ + signed char c; + + while (1) { + c = getopt_long(argc, argv, GETOPT_STR, long_options, NULL); + if (c < 0) + break; + switch (c) { + case 'a': + config.automode = true; + break; + case 'c': + config.create = true; + break; + case 'd': + config.debug = true; + break; + case 's': + config.start = true; + break; + case 'f': + if (optarg && strlen(optarg)) + strncpy(config.suffix, optarg, 256); + break; + case 'l': + config.link_retry = strtoul(optarg, NULL, 10); + break; + case 'm': + if (strcasecmp(optarg, "vn2vn") == 0) + config.vn2vn = true; + else if (strcasecmp(optarg, "fabric") == 0) + config.vn2vn = false; + else { + fprintf(stderr, "%s: Unknown value for mode: %s\n", + exe, optarg); + exit(1); + } + break; + case 'u': + config.link_up = true; + break; + case 'h': + help(0); + break; + case 'v': + printf("%s version %s\n", exe, FCOE_UTILS_VERSION); + exit(0); + break; + default: + fprintf(stderr, "Try '%s --help' " + "for more information\n", exe); + exit(1); + } + } + + if ((optind == argc) && (!config.automode)) + help(1); + + config.namev = &argv[optind]; + config.namec = argc - optind; +} + +static int rtnl_listener_handler(struct nlmsghdr *nh, UNUSED void *arg) +{ + switch (nh->nlmsg_type) { + case RTM_NEWLINK: + rtnl_recv_newlink(nh); + return 0; + } + return -1; +} + +static int +create_and_start_vlan(struct fcf *fcf, bool vn2vn) +{ + struct iff *real_dev, *vlan; + char vlan_name[IFNAMSIZ]; + int rc; + + real_dev = lookup_iff(fcf->ifindex, NULL); + if (!real_dev) { + FIP_LOG_ERR(ENODEV, + "lost device %d with discovered %s?\n", + fcf->ifindex, vn2vn ? "VN2VN" : "FCF"); + return -ENXIO; + } + if (!fcf->vlan) { + /* + * If the vlan notification has VLAN id 0, + * skip creating vlan interface, and FCoE is + * started on the physical interface itself. + */ + FIP_LOG_DBG("VLAN id is 0 for %s\n", real_dev->ifname); + vlan = real_dev; + } else { + vlan = lookup_vlan(fcf->ifindex, fcf->vlan); + if (vlan) { + FIP_LOG_DBG("VLAN %s.%d already exists as %s\n", + real_dev->ifname, fcf->vlan, vlan->ifname); + rc = 0; + } else { + snprintf(vlan_name, IFNAMSIZ, "%s.%d%s", + real_dev->ifname, fcf->vlan, config.suffix); + rc = vlan_create(fcf->ifindex, fcf->vlan, vlan_name); + if (rc < 0) + printf("Failed to create VLAN device %s\n\t%s\n", + vlan_name, strerror(-rc)); + else + printf("Created VLAN device %s\n", vlan_name); + return rc; + } + } + if (!config.start) + return rc; + + if (!vlan->running) { + FIP_LOG_DBG("%s if %d not running, starting", + vlan == real_dev ? "real" : "vlan", + vlan->ifindex); + rtnl_set_iff_up(vlan->ifindex, NULL); + } else if (!vlan->fcoe_started) { + printf("Starting FCoE on interface %s\n", + vlan->ifname); + fcoe_instance_start(vlan->ifname); + vlan->fcoe_started = true; + } + return rc; +} + +static int fcoe_mod_instance_start(const char *ifname) +{ + enum fcoe_status ret; + const char *path; + + if (config.vn2vn) + path = FCOE_CREATE_VN2VN; + else + path = FCOE_CREATE; + + ret = fcm_write_str_to_sysfs_file(path, ifname); + if (ret) { + FIP_LOG_ERRNO("Failed to open file: %s", FCOE_CREATE); + FIP_LOG_ERRNO("May be fcoe stack not loaded, starting" + " fcoe service will fix that"); + + return EFAIL; + } + + return 0; +} + +static int fcoe_bus_instance_start(const char *ifname) +{ + enum fcoe_status ret; + char fchost[FCHOSTBUFLEN]; + char ctlr[FCHOSTBUFLEN]; + + ret = fcm_write_str_to_sysfs_file(FCOE_BUS_CREATE, ifname); + if (ret) { + FIP_LOG_ERRNO("Failed to open file: %s", FCOE_BUS_CREATE); + FIP_LOG_ERRNO("May be fcoe stack not loaded, starting" + " fcoe service will fix that"); + return ret; + } + + if (fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN)) { + FIP_LOG_DBG("Failed to find fc_host for %s\n", ifname); + return ENOSYSFS; + } + + if (fcoe_find_ctlr(fchost, ctlr, FCHOSTBUFLEN)) { + FIP_LOG_DBG("Failed to get ctlr for %s\n", ifname); + return ENOSYSFS; + } + + if (config.vn2vn) { + ret = fcm_write_str_to_ctlr_attr(ctlr, FCOE_CTLR_ATTR_MODE, + "vn2vn"); + if (ret) + FIP_LOG_DBG("Failed to set mode interface %s\n", + ifname); + } + + ret = fcm_write_str_to_ctlr_attr(ctlr, FCOE_CTLR_ATTR_ENABLED, "1"); + if (ret) + FIP_LOG_DBG("Failed to enable interface %s\n", ifname); + + return 0; +} + +static void determine_libfcoe_interface(void) +{ + if (!access(FCOE_BUS_CREATE, F_OK)) { + FIP_LOG_DBG("Using /sys/bus/fcoe interfaces\n"); + fcoe_instance_start = &fcoe_bus_instance_start; + } else { + FIP_LOG_DBG("Using libfcoe module parameter interfaces\n"); + fcoe_instance_start = &fcoe_mod_instance_start; + } +} + +static void print_list(struct fcf_list_head *list, const char *label) +{ + struct iff *iff; + struct fcf *fcf; + + printf("%-16.16s| %-5.5s| %-17.17s\n", "interface", "VLAN", label); + printf("------------------------------------------\n"); + TAILQ_FOREACH(fcf, list, list_node) { + iff = lookup_iff(fcf->ifindex, NULL); + printf("%-16.16s| %-5d| %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + iff->ifname, fcf->vlan, + fcf->mac_addr[0], fcf->mac_addr[1], fcf->mac_addr[2], + fcf->mac_addr[3], fcf->mac_addr[4], fcf->mac_addr[5]); + } + printf("\n"); +} + +static int print_results(void) +{ + if (TAILQ_EMPTY(&fcfs) && TAILQ_EMPTY(&vn2vns)) { + printf("No Fibre Channel Forwarders or VN2VN Responders Found\n"); + return ENODEV; + } + + if (!TAILQ_EMPTY(&fcfs)) { + printf("Fibre Channel Forwarders Discovered\n"); + print_list(&fcfs, "FCF MAC"); + } + if (!TAILQ_EMPTY(&vn2vns)) { + printf("VN2VN Responders Discovered\n"); + print_list(&vn2vns, "VN2VN MAC"); + } + + return 0; +} + +static void recv_loop(int timeout) +{ + int i; + int rc; + + while (1) { + rc = poll(pfd, pfd_len, timeout); + FIP_LOG_DBG("return from poll %d", rc); + if (rc == 0) /* timeout */ + break; + if (rc == -1) { + FIP_LOG_ERRNO("poll error"); + break; + } + /* pfd[0] must be the netlink socket */ + if (pfd[0].revents & POLLIN) + rtnl_recv(pfd[0].fd, rtnl_listener_handler, NULL); + /* everything else should be FIP packet sockets */ + for (i = 1; i < pfd_len; i++) { + if (pfd[i].revents & POLLIN) { + rc = fip_recv(pfd[i].fd, fip_vlan_handler, + NULL); + if (rc < 0) + break; + } + } + if (i < pfd_len) + break; + } +} + +static void find_interfaces(int ns) +{ + send_getlink_dump(ns); + rtnl_recv(ns, rtnl_listener_handler, NULL); +} + +static int probe_fip_interface(struct iff *iff) +{ + int origdev = 1, rc; + + if (iff->resp_recv) + return 0; + if (!iff->running) { + if (iff->linkup_sent) { + FIP_LOG_DBG("if %d not running, waiting for link up", + iff->ifindex); + } else { + FIP_LOG_DBG("if %d not running, starting", + iff->ifindex); + rtnl_set_iff_up(iff->ifindex, NULL); + iff->linkup_sent = true; + } + iff->req_sent = false; + return 1; + } + if (iff->req_sent) + return 0; + + if (!iff->fip_ready) { + iff->ps = fip_socket(iff->ifindex, iff->mac_addr, FIP_NONE); + if (iff->ps < 0) { + FIP_LOG_DBG("if %d not ready\n", iff->ifindex); + return 0; + } + setsockopt(iff->ps, SOL_PACKET, PACKET_ORIGDEV, + &origdev, sizeof(origdev)); + pfd_add(iff->ps); + iff->fip_ready = true; + } + + if (config.vn2vn) + rc = fip_send_vlan_request(iff->ps, iff->ifindex, + iff->mac_addr, FIP_ALL_VN2VN); + else + rc = fip_send_vlan_request(iff->ps, iff->ifindex, + iff->mac_addr, FIP_ALL_FCF); + if (rc == 0) + iff->req_sent = true; + return rc == 0 ? 0 : 1; +} + +static int send_vlan_requests(void) +{ + struct iff *iff; + int skipped = 0; + int i; + + if (config.automode) { + TAILQ_FOREACH(iff, &interfaces, list_node) { + skipped += probe_fip_interface(iff); + } + } else { + for (i = 0; i < config.namec; i++) { + iff = lookup_iff(0, config.namev[i]); + if (!iff) { + skipped++; + continue; + } + skipped += probe_fip_interface(iff); + } + } + return skipped; +} + +static void do_vlan_discovery(void) +{ + struct iff *iff; + int retry_count = 0; + int skip_retry_count = 0; + int skipped = 0, retry_iff = 0; +retry: + skipped += send_vlan_requests(); + if (skipped && skip_retry_count++ < config.link_retry) { + FIP_LOG_DBG("waiting for IFF_RUNNING [%d/%d]\n", + skip_retry_count, config.link_retry); + recv_loop(500); + skipped = 0; + retry_count = 0; + goto retry; + } + recv_loop(200); + TAILQ_FOREACH(iff, &interfaces, list_node) { + if (!iff->fip_ready) { + FIP_LOG_DBG("if %d: skipping, FIP not ready\n", + iff->ifindex); + continue; + } + if (!iff->running && iff->linkup_sent) { + FIP_LOG_DBG("if %d: waiting for IFF_RUNNING [%d]\n", + iff->ifindex, retry_count); + retry_iff++; + continue; + } + /* if we did not receive a response, retry */ + if (iff->req_sent && !iff->resp_recv) { + FIP_LOG_DBG("if %d: VLAN discovery RETRY [%d]", + iff->ifindex, retry_count); + iff->req_sent = false; + retry_iff++; + continue; + } + if (config.create) { + struct iff *vlan; + + TAILQ_FOREACH(vlan, &iff->vlans, list_node) { + if (!vlan->running) { + FIP_LOG_DBG("vlan %d: waiting for " + "IFF_RUNNING [%d]", + vlan->ifindex, retry_count); + retry_iff++; + continue; + } + } + } + } + if (retry_iff && retry_count++ < config.link_retry) { + recv_loop(1000); + retry_iff = 0; + goto retry; + } +} + +static void cleanup_interfaces(void) +{ + struct iff *iff; + + TAILQ_FOREACH(iff, &interfaces, list_node) { + if (iff->linkup_sent) { + if (config.link_up && iff->resp_recv) + continue; + if (iff->fcoe_started) + continue; + if (TAILQ_EMPTY(&iff->vlans)) { + FIP_LOG_DBG("shutdown if %d", + iff->ifindex); + rtnl_set_iff_down(iff->ifindex, NULL); + iff->linkup_sent = false; + } + } + } +} + +/* this is to not require headers from libcap */ +static inline int capget(cap_user_header_t hdrp, cap_user_data_t datap) +{ + return syscall(__NR_capget, hdrp, datap); +} + +static int checkcaps(void) +{ + struct __user_cap_header_struct caphdr = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = 0, + }; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3]; + + capget(&caphdr, caps); + return !(caps[CAP_TO_INDEX(CAP_NET_RAW)].effective & + CAP_TO_MASK(CAP_NET_RAW)); +} + +int main(int argc, char **argv) +{ + int ns; + int rc = 0; + int find_cnt = 0; + + exe = strrchr(argv[0], '/'); + if (exe) + exe++; + else + exe = argv[0]; + + parse_cmdline(argc, argv); + sa_log_prefix = exe; + sa_log_flags = 0; + enable_debug_log(config.debug); + + if (checkcaps()) { + FIP_LOG("must run as root or with the NET_RAW capability"); + exit(1); + } + + ns = rtnl_socket(RTMGRP_LINK); + if (ns < 0) { + rc = ns; + goto ns_err; + } + pfd_add(ns); + + determine_libfcoe_interface(); + + find_interfaces(ns); + if (config.automode) + while ((TAILQ_EMPTY(&interfaces)) && ++find_cnt < 5) { + FIP_LOG_DBG("no interfaces found, trying again"); + find_interfaces(ns); + } + + if (TAILQ_EMPTY(&interfaces)) { + if (config.automode) + FIP_LOG_ERR(ENODEV, + "no interfaces to perform discovery on"); + else + FIP_LOG("no interfaces to perform discovery on"); + exit(1); + } + + do_vlan_discovery(); + + rc = print_results(); + + cleanup_interfaces(); + + close(ns); +ns_err: + exit(rc); +} + diff --git a/include/fc_els.h b/include/fc_els.h new file mode 100644 index 0000000..481abbd --- /dev/null +++ b/include/fc_els.h @@ -0,0 +1,831 @@ +/* + * Copyright(c) 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FC_ELS_H_ +#define _FC_ELS_H_ + +#include + +/* + * Fibre Channel Switch - Enhanced Link Services definitions. + * From T11 FC-LS Rev 1.2 June 7, 2005. + */ + +/* + * ELS Command codes - byte 0 of the frame payload + */ +enum fc_els_cmd { + ELS_LS_RJT = 0x01, /* ESL reject */ + ELS_LS_ACC = 0x02, /* ESL Accept */ + ELS_PLOGI = 0x03, /* N_Port login */ + ELS_FLOGI = 0x04, /* F_Port login */ + ELS_LOGO = 0x05, /* Logout */ + ELS_ABTX = 0x06, /* Abort exchange - obsolete */ + ELS_RCS = 0x07, /* read connection status */ + ELS_RES = 0x08, /* read exchange status block */ + ELS_RSS = 0x09, /* read sequence status block */ + ELS_RSI = 0x0a, /* read sequence initiative */ + ELS_ESTS = 0x0b, /* establish streaming */ + ELS_ESTC = 0x0c, /* estimate credit */ + ELS_ADVC = 0x0d, /* advise credit */ + ELS_RTV = 0x0e, /* read timeout value */ + ELS_RLS = 0x0f, /* read link error status block */ + ELS_ECHO = 0x10, /* echo */ + ELS_TEST = 0x11, /* test */ + ELS_RRQ = 0x12, /* reinstate recovery qualifier */ + ELS_REC = 0x13, /* read exchange concise */ + ELS_SRR = 0x14, /* sequence retransmission request */ + ELS_PRLI = 0x20, /* process login */ + ELS_PRLO = 0x21, /* process logout */ + ELS_SCN = 0x22, /* state change notification */ + ELS_TPLS = 0x23, /* test process login state */ + ELS_TPRLO = 0x24, /* third party process logout */ + ELS_LCLM = 0x25, /* login control list mgmt (obs) */ + ELS_GAID = 0x30, /* get alias_ID */ + ELS_FACT = 0x31, /* fabric activate alias_id */ + ELS_FDACDT = 0x32, /* fabric deactivate alias_id */ + ELS_NACT = 0x33, /* N-port activate alias_id */ + ELS_NDACT = 0x34, /* N-port deactivate alias_id */ + ELS_QOSR = 0x40, /* quality of service request */ + ELS_RVCS = 0x41, /* read virtual circuit status */ + ELS_PDISC = 0x50, /* discover N_port service params */ + ELS_FDISC = 0x51, /* discover F_port service params */ + ELS_ADISC = 0x52, /* discover address */ + ELS_RNC = 0x53, /* report node cap (obs) */ + ELS_FARP_REQ = 0x54, /* FC ARP request */ + ELS_FARP_REPL = 0x55, /* FC ARP reply */ + ELS_RPS = 0x56, /* read port status block */ + ELS_RPL = 0x57, /* read port list */ + ELS_RPBC = 0x58, /* read port buffer condition */ + ELS_FAN = 0x60, /* fabric address notification */ + ELS_RSCN = 0x61, /* registered state change notification */ + ELS_SCR = 0x62, /* state change registration */ + ELS_RNFT = 0x63, /* report node FC-4 types */ + ELS_CSR = 0x68, /* clock synch. request */ + ELS_CSU = 0x69, /* clock synch. update */ + ELS_LINIT = 0x70, /* loop initialize */ + ELS_LSTS = 0x72, /* loop status */ + ELS_RNID = 0x78, /* request node ID data */ + ELS_RLIR = 0x79, /* registered link incident report */ + ELS_LIRR = 0x7a, /* link incident record registration */ + ELS_SRL = 0x7b, /* scan remote loop */ + ELS_SBRP = 0x7c, /* set bit-error reporting params */ + ELS_RPSC = 0x7d, /* report speed capabilities */ + ELS_QSA = 0x7e, /* query security attributes */ + ELS_EVFP = 0x7f, /* exchange virt. fabrics params */ + ELS_LKA = 0x80, /* link keep-alive */ + ELS_AUTH_ELS = 0x90, /* authentication ELS */ +}; + +/* + * Initializer useful for decoding table. + * Please keep this in sync with the above definitions. + */ +#define FC_ELS_CMDS_INIT { \ + [ELS_LS_RJT] = "LS_RJT", \ + [ELS_LS_ACC] = "LS_ACC", \ + [ELS_PLOGI] = "PLOGI", \ + [ELS_FLOGI] = "FLOGI", \ + [ELS_LOGO] = "LOGO", \ + [ELS_ABTX] = "ABTX", \ + [ELS_RCS] = "RCS", \ + [ELS_RES] = "RES", \ + [ELS_RSS] = "RSS", \ + [ELS_RSI] = "RSI", \ + [ELS_ESTS] = "ESTS", \ + [ELS_ESTC] = "ESTC", \ + [ELS_ADVC] = "ADVC", \ + [ELS_RTV] = "RTV", \ + [ELS_RLS] = "RLS", \ + [ELS_ECHO] = "ECHO", \ + [ELS_TEST] = "TEST", \ + [ELS_RRQ] = "RRQ", \ + [ELS_REC] = "REC", \ + [ELS_SRR] = "SRR", \ + [ELS_PRLI] = "PRLI", \ + [ELS_PRLO] = "PRLO", \ + [ELS_SCN] = "SCN", \ + [ELS_TPLS] = "TPLS", \ + [ELS_TPRLO] = "TPRLO", \ + [ELS_LCLM] = "LCLM", \ + [ELS_GAID] = "GAID", \ + [ELS_FACT] = "FACT", \ + [ELS_FDACDT] = "FDACDT", \ + [ELS_NACT] = "NACT", \ + [ELS_NDACT] = "NDACT", \ + [ELS_QOSR] = "QOSR", \ + [ELS_RVCS] = "RVCS", \ + [ELS_PDISC] = "PDISC", \ + [ELS_FDISC] = "FDISC", \ + [ELS_ADISC] = "ADISC", \ + [ELS_RNC] = "RNC", \ + [ELS_FARP_REQ] = "FARP_REQ", \ + [ELS_FARP_REPL] = "FARP_REPL", \ + [ELS_RPS] = "RPS", \ + [ELS_RPL] = "RPL", \ + [ELS_RPBC] = "RPBC", \ + [ELS_FAN] = "FAN", \ + [ELS_RSCN] = "RSCN", \ + [ELS_SCR] = "SCR", \ + [ELS_RNFT] = "RNFT", \ + [ELS_CSR] = "CSR", \ + [ELS_CSU] = "CSU", \ + [ELS_LINIT] = "LINIT", \ + [ELS_LSTS] = "LSTS", \ + [ELS_RNID] = "RNID", \ + [ELS_RLIR] = "RLIR", \ + [ELS_LIRR] = "LIRR", \ + [ELS_SRL] = "SRL", \ + [ELS_SBRP] = "SBRP", \ + [ELS_RPSC] = "RPSC", \ + [ELS_QSA] = "QSA", \ + [ELS_EVFP] = "EVFP", \ + [ELS_LKA] = "LKA", \ + [ELS_AUTH_ELS] = "AUTH_ELS", \ +} + +/* + * LS_ACC payload. + */ +struct fc_els_ls_acc { + __u8 la_cmd; /* command code ELS_LS_ACC */ + __u8 la_resv[3]; /* reserved */ +}; + +/* + * ELS reject payload. + */ +struct fc_els_ls_rjt { + __u8 er_cmd; /* command code ELS_LS_RJT */ + __u8 er_resv[4]; /* reserved must be zero */ + __u8 er_reason; /* reason (enum fc_els_rjt_reason below) */ + __u8 er_explan; /* explanation (enum fc_els_rjt_explan below) */ + __u8 er_vendor; /* vendor specific code */ +}; + +/* + * ELS reject reason codes (er_reason). + */ +enum fc_els_rjt_reason { + ELS_RJT_NONE = 0, /* no reject - not to be sent */ + ELS_RJT_INVAL = 0x01, /* invalid ELS command code */ + ELS_RJT_LOGIC = 0x03, /* logical error */ + ELS_RJT_BUSY = 0x05, /* logical busy */ + ELS_RJT_PROT = 0x07, /* protocol error */ + ELS_RJT_UNAB = 0x09, /* unable to perform command request */ + ELS_RJT_UNSUP = 0x0b, /* command not supported */ + ELS_RJT_INPROG = 0x0e, /* command already in progress */ + ELS_RJT_FIP = 0x20, /* FIP error */ + ELS_RJT_VENDOR = 0xff, /* vendor specific error */ +}; + + +/* + * reason code explanation (er_explan). + */ +enum fc_els_rjt_explan { + ELS_EXPL_NONE = 0x00, /* No additional explanation */ + ELS_EXPL_SPP_OPT_ERR = 0x01, /* service parameter error - options */ + ELS_EXPL_SPP_ICTL_ERR = 0x03, /* service parm error - initiator ctl */ + ELS_EXPL_AH = 0x11, /* invalid association header */ + ELS_EXPL_AH_REQ = 0x13, /* association_header required */ + ELS_EXPL_SID = 0x15, /* invalid originator S_ID */ + ELS_EXPL_OXID_RXID = 0x17, /* invalid OX_ID-RX_ID combination */ + ELS_EXPL_INPROG = 0x19, /* Request already in progress */ + ELS_EXPL_PLOGI_REQD = 0x1e, /* N_Port login required */ + ELS_EXPL_INSUF_RES = 0x29, /* insufficient resources */ + ELS_EXPL_UNAB_DATA = 0x2a, /* unable to supply requested data */ + ELS_EXPL_UNSUPR = 0x2c, /* Request not supported */ + ELS_EXPL_INV_LEN = 0x2d, /* Invalid payload length */ + ELS_EXPL_NOT_NEIGHBOR = 0x62, /* VN2VN_Port not in neighbor set */ + /* TBD - above definitions incomplete */ +}; + +/* + * Common service parameters (N ports). + */ +struct fc_els_csp { + __u8 sp_hi_ver; /* highest version supported (obs.) */ + __u8 sp_lo_ver; /* highest version supported (obs.) */ + __be16 sp_bb_cred; /* buffer-to-buffer credits */ + __be16 sp_features; /* common feature flags */ + __be16 sp_bb_data; /* b-b state number and data field sz */ + union { + struct { + __be16 _sp_tot_seq; /* total concurrent sequences */ + __be16 _sp_rel_off; /* rel. offset by info cat */ + } sp_plogi; + struct { + __be32 _sp_r_a_tov; /* resource alloc. timeout msec */ + } sp_flogi_acc; + } sp_u; + __be32 sp_e_d_tov; /* error detect timeout value */ +}; +#define sp_tot_seq sp_u.sp_plogi._sp_tot_seq +#define sp_rel_off sp_u.sp_plogi._sp_rel_off +#define sp_r_a_tov sp_u.sp_flogi_acc._sp_r_a_tov + +#define FC_SP_BB_DATA_MASK 0xfff /* mask for data field size in sp_bb_data */ + +/* + * Minimum and maximum values for max data field size in service parameters. + */ +#define FC_SP_MIN_MAX_PAYLOAD FC_MIN_MAX_PAYLOAD +#define FC_SP_MAX_MAX_PAYLOAD FC_MAX_PAYLOAD + +/* + * sp_features + */ +#define FC_SP_FT_NPIV 0x8000 /* multiple N_Port_ID support (FLOGI) */ +#define FC_SP_FT_CIRO 0x8000 /* continuously increasing rel off (PLOGI) */ +#define FC_SP_FT_CLAD 0x8000 /* clean address (in FLOGI LS_ACC) */ +#define FC_SP_FT_RAND 0x4000 /* random relative offset */ +#define FC_SP_FT_VAL 0x2000 /* valid vendor version level */ +#define FC_SP_FT_NPIV_ACC 0x2000 /* NPIV assignment (FLOGI LS_ACC) */ +#define FC_SP_FT_FPORT 0x1000 /* F port (1) vs. N port (0) */ +#define FC_SP_FT_ABB 0x0800 /* alternate BB_credit management */ +#define FC_SP_FT_EDTR 0x0400 /* E_D_TOV Resolution is nanoseconds */ +#define FC_SP_FT_MCAST 0x0200 /* multicast */ +#define FC_SP_FT_BCAST 0x0100 /* broadcast */ +#define FC_SP_FT_HUNT 0x0080 /* hunt group */ +#define FC_SP_FT_SIMP 0x0040 /* dedicated simplex */ +#define FC_SP_FT_SEC 0x0020 /* reserved for security */ +#define FC_SP_FT_CSYN 0x0010 /* clock synch. supported */ +#define FC_SP_FT_RTTOV 0x0008 /* R_T_TOV value 100 uS, else 100 mS */ +#define FC_SP_FT_HALF 0x0004 /* dynamic half duplex */ +#define FC_SP_FT_SEQC 0x0002 /* SEQ_CNT */ +#define FC_SP_FT_PAYL 0x0001 /* FLOGI payload length 256, else 116 */ + +/* + * Class-specific service parameters. + */ +struct fc_els_cssp { + __be16 cp_class; /* class flags */ + __be16 cp_init; /* initiator flags */ + __be16 cp_recip; /* recipient flags */ + __be16 cp_rdfs; /* receive data field size */ + __be16 cp_con_seq; /* concurrent sequences */ + __be16 cp_ee_cred; /* N-port end-to-end credit */ + __u8 cp_resv1; /* reserved */ + __u8 cp_open_seq; /* open sequences per exchange */ + __u8 _cp_resv2[2]; /* reserved */ +}; + +/* + * cp_class flags. + */ +#define FC_CPC_VALID 0x8000 /* class valid */ +#define FC_CPC_IMIX 0x4000 /* intermix mode */ +#define FC_CPC_SEQ 0x0800 /* sequential delivery */ +#define FC_CPC_CAMP 0x0200 /* camp-on */ +#define FC_CPC_PRI 0x0080 /* priority */ + +/* + * cp_init flags. + * (TBD: not all flags defined here). + */ +#define FC_CPI_CSYN 0x0010 /* clock synch. capable */ + +/* + * cp_recip flags. + */ +#define FC_CPR_CSYN 0x0008 /* clock synch. capable */ + +/* + * NFC_ELS_FLOGI: Fabric login request. + * NFC_ELS_PLOGI: Port login request (same format). + */ +struct fc_els_flogi { + __u8 fl_cmd; /* command */ + __u8 _fl_resvd[3]; /* must be zero */ + struct fc_els_csp fl_csp; /* common service parameters */ + __be64 fl_wwpn; /* port name */ + __be64 fl_wwnn; /* node name */ + struct fc_els_cssp fl_cssp[4]; /* class 1-4 service parameters */ + __u8 fl_vend[16]; /* vendor version level */ +} __attribute__((__packed__)); + +/* + * Process login service parameter page. + */ +struct fc_els_spp { + __u8 spp_type; /* type code or common service params */ + __u8 spp_type_ext; /* type code extension */ + __u8 spp_flags; + __u8 _spp_resvd; + __be32 spp_orig_pa; /* originator process associator */ + __be32 spp_resp_pa; /* responder process associator */ + __be32 spp_params; /* service parameters */ +}; + +/* + * spp_flags. + */ +#define FC_SPP_OPA_VAL 0x80 /* originator proc. assoc. valid */ +#define FC_SPP_RPA_VAL 0x40 /* responder proc. assoc. valid */ +#define FC_SPP_EST_IMG_PAIR 0x20 /* establish image pair */ +#define FC_SPP_RESP_MASK 0x0f /* mask for response code (below) */ + +/* + * SPP response code in spp_flags - lower 4 bits. + */ +enum fc_els_spp_resp { + FC_SPP_RESP_ACK = 1, /* request executed */ + FC_SPP_RESP_RES = 2, /* unable due to lack of resources */ + FC_SPP_RESP_INIT = 3, /* initialization not complete */ + FC_SPP_RESP_NO_PA = 4, /* unknown process associator */ + FC_SPP_RESP_CONF = 5, /* configuration precludes image pair */ + FC_SPP_RESP_COND = 6, /* request completed conditionally */ + FC_SPP_RESP_MULT = 7, /* unable to handle multiple SPPs */ + FC_SPP_RESP_INVL = 8, /* SPP is invalid */ +}; + +/* + * ELS_RRQ - Reinstate Recovery Qualifier + */ +struct fc_els_rrq { + __u8 rrq_cmd; /* command (0x12) */ + __u8 rrq_zero[3]; /* specified as zero - part of cmd */ + __u8 rrq_resvd; /* reserved */ + __u8 rrq_s_id[3]; /* originator FID */ + __be16 rrq_ox_id; /* originator exchange ID */ + __be16 rrq_rx_id; /* responders exchange ID */ +}; + +/* + * ELS_REC - Read exchange concise. + */ +struct fc_els_rec { + __u8 rec_cmd; /* command (0x13) */ + __u8 rec_zero[3]; /* specified as zero - part of cmd */ + __u8 rec_resvd; /* reserved */ + __u8 rec_s_id[3]; /* originator FID */ + __be16 rec_ox_id; /* originator exchange ID */ + __be16 rec_rx_id; /* responders exchange ID */ +}; + +/* + * ELS_REC LS_ACC payload. + */ +struct fc_els_rec_acc { + __u8 reca_cmd; /* accept (0x02) */ + __u8 reca_zero[3]; /* specified as zero - part of cmd */ + __be16 reca_ox_id; /* originator exchange ID */ + __be16 reca_rx_id; /* responders exchange ID */ + __u8 reca_resvd1; /* reserved */ + __u8 reca_ofid[3]; /* originator FID */ + __u8 reca_resvd2; /* reserved */ + __u8 reca_rfid[3]; /* responder FID */ + __be32 reca_fc4value; /* FC4 value */ + __be32 reca_e_stat; /* ESB (exchange status block) status */ +}; + +/* + * ELS_PRLI - Process login request and response. + */ +struct fc_els_prli { + __u8 prli_cmd; /* command */ + __u8 prli_spp_len; /* length of each serv. parm. page */ + __be16 prli_len; /* length of entire payload */ + /* service parameter pages follow */ +}; + +/* + * ELS_PRLO - Process logout request and response. + */ +struct fc_els_prlo { + __u8 prlo_cmd; /* command */ + __u8 prlo_obs; /* obsolete, but shall be set to 10h */ + __be16 prlo_len; /* payload length */ +}; + +/* + * ELS_ADISC payload + */ +struct fc_els_adisc { + __u8 adisc_cmd; + __u8 adisc_resv[3]; + __u8 adisc_resv1; + __u8 adisc_hard_addr[3]; + __be64 adisc_wwpn; + __be64 adisc_wwnn; + __u8 adisc_resv2; + __u8 adisc_port_id[3]; +} __attribute__((__packed__)); + +/* + * ELS_LOGO - process or fabric logout. + */ +struct fc_els_logo { + __u8 fl_cmd; /* command code */ + __u8 fl_zero[3]; /* specified as zero - part of cmd */ + __u8 fl_resvd; /* reserved */ + __u8 fl_n_port_id[3];/* N port ID */ + __be64 fl_n_port_wwn; /* port name */ +}; + +/* + * ELS_RTV - read timeout value. + */ +struct fc_els_rtv { + __u8 rtv_cmd; /* command code 0x0e */ + __u8 rtv_zero[3]; /* specified as zero - part of cmd */ +}; + +/* + * LS_ACC for ELS_RTV - read timeout value. + */ +struct fc_els_rtv_acc { + __u8 rtv_cmd; /* command code 0x02 */ + __u8 rtv_zero[3]; /* specified as zero - part of cmd */ + __be32 rtv_r_a_tov; /* resource allocation timeout value */ + __be32 rtv_e_d_tov; /* error detection timeout value */ + __be32 rtv_toq; /* timeout qualifier (see below) */ +}; + +/* + * rtv_toq bits. + */ +#define FC_ELS_RTV_EDRES (1 << 26) /* E_D_TOV resolution is nS else mS */ +#define FC_ELS_RTV_RTTOV (1 << 19) /* R_T_TOV is 100 uS else 100 mS */ + +/* + * ELS_SCR - state change registration payload. + */ +struct fc_els_scr { + __u8 scr_cmd; /* command code */ + __u8 scr_resv[6]; /* reserved */ + __u8 scr_reg_func; /* registration function (see below) */ +}; + +enum fc_els_scr_func { + ELS_SCRF_FAB = 1, /* fabric-detected registration */ + ELS_SCRF_NPORT = 2, /* Nx_Port-detected registration */ + ELS_SCRF_FULL = 3, /* full registration */ + ELS_SCRF_CLEAR = 255, /* remove any current registrations */ +}; + +/* + * ELS_RSCN - registered state change notification payload. + */ +struct fc_els_rscn { + __u8 rscn_cmd; /* RSCN opcode (0x61) */ + __u8 rscn_page_len; /* page length (4) */ + __be16 rscn_plen; /* payload length including this word */ + + /* followed by 4-byte generic affected Port_ID pages */ +}; + +struct fc_els_rscn_page { + __u8 rscn_page_flags; /* event and address format */ + __u8 rscn_fid[3]; /* fabric ID */ +}; + +#define ELS_RSCN_EV_QUAL_BIT 2 /* shift count for event qualifier */ +#define ELS_RSCN_EV_QUAL_MASK 0xf /* mask for event qualifier */ +#define ELS_RSCN_ADDR_FMT_BIT 0 /* shift count for address format */ +#define ELS_RSCN_ADDR_FMT_MASK 0x3 /* mask for address format */ + +enum fc_els_rscn_ev_qual { + ELS_EV_QUAL_NONE = 0, /* unspecified */ + ELS_EV_QUAL_NS_OBJ = 1, /* changed name server object */ + ELS_EV_QUAL_PORT_ATTR = 2, /* changed port attribute */ + ELS_EV_QUAL_SERV_OBJ = 3, /* changed service object */ + ELS_EV_QUAL_SW_CONFIG = 4, /* changed switch configuration */ + ELS_EV_QUAL_REM_OBJ = 5, /* removed object */ +}; + +enum fc_els_rscn_addr_fmt { + ELS_ADDR_FMT_PORT = 0, /* rscn_fid is a port address */ + ELS_ADDR_FMT_AREA = 1, /* rscn_fid is a area address */ + ELS_ADDR_FMT_DOM = 2, /* rscn_fid is a domain address */ + ELS_ADDR_FMT_FAB = 3, /* anything on fabric may have changed */ +}; + +/* + * ELS_RNID - request Node ID. + */ +struct fc_els_rnid { + __u8 rnid_cmd; /* RNID opcode (0x78) */ + __u8 rnid_resv[3]; /* reserved */ + __u8 rnid_fmt; /* data format */ + __u8 rnid_resv2[3]; /* reserved */ +}; + +/* + * Node Identification Data formats (rnid_fmt) + */ +enum fc_els_rnid_fmt { + ELS_RNIDF_NONE = 0, /* no specific identification data */ + ELS_RNIDF_GEN = 0xdf, /* general topology discovery format */ +}; + +/* + * ELS_RNID response. + */ +struct fc_els_rnid_resp { + __u8 rnid_cmd; /* response code (LS_ACC) */ + __u8 rnid_resv[3]; /* reserved */ + __u8 rnid_fmt; /* data format */ + __u8 rnid_cid_len; /* common ID data length */ + __u8 rnid_resv2; /* reserved */ + __u8 rnid_sid_len; /* specific ID data length */ +}; + +struct fc_els_rnid_cid { + __be64 rnid_wwpn; /* N port name */ + __be64 rnid_wwnn; /* node name */ +}; + +struct fc_els_rnid_gen { + __u8 rnid_vend_id[16]; /* vendor-unique ID */ + __be32 rnid_atype; /* associated type (see below) */ + __be32 rnid_phys_port; /* physical port number */ + __be32 rnid_att_nodes; /* number of attached nodes */ + __u8 rnid_node_mgmt; /* node management (see below) */ + __u8 rnid_ip_ver; /* IP version (see below) */ + __be16 rnid_prot_port; /* UDP / TCP port number */ + __be32 rnid_ip_addr[4]; /* IP address */ + __u8 rnid_resvd[2]; /* reserved */ + __be16 rnid_vend_spec; /* vendor-specific field */ +}; + +enum fc_els_rnid_atype { + ELS_RNIDA_UNK = 0x01, /* unknown */ + ELS_RNIDA_OTHER = 0x02, /* none of the following */ + ELS_RNIDA_HUB = 0x03, + ELS_RNIDA_SWITCH = 0x04, + ELS_RNIDA_GATEWAY = 0x05, + ELS_RNIDA_CONV = 0x06, /* Obsolete, do not use this value */ + ELS_RNIDA_HBA = 0x07, /* Obsolete, do not use this value */ + ELS_RNIDA_PROXY = 0x08, /* Obsolete, do not use this value */ + ELS_RNIDA_STORAGE = 0x09, + ELS_RNIDA_HOST = 0x0a, + ELS_RNIDA_SUBSYS = 0x0b, /* storage subsystem (e.g., RAID) */ + ELS_RNIDA_ACCESS = 0x0e, /* access device (e.g. media changer) */ + ELS_RNIDA_NAS = 0x11, /* NAS server */ + ELS_RNIDA_BRIDGE = 0x12, /* bridge */ + ELS_RNIDA_VIRT = 0x13, /* virtualization device */ + ELS_RNIDA_MF = 0xff, /* multifunction device (bits below) */ + ELS_RNIDA_MF_HUB = 1UL << 31, /* hub */ + ELS_RNIDA_MF_SW = 1UL << 30, /* switch */ + ELS_RNIDA_MF_GW = 1UL << 29, /* gateway */ + ELS_RNIDA_MF_ST = 1UL << 28, /* storage */ + ELS_RNIDA_MF_HOST = 1UL << 27, /* host */ + ELS_RNIDA_MF_SUB = 1UL << 26, /* storage subsystem */ + ELS_RNIDA_MF_ACC = 1UL << 25, /* storage access dev */ + ELS_RNIDA_MF_WDM = 1UL << 24, /* wavelength division mux */ + ELS_RNIDA_MF_NAS = 1UL << 23, /* NAS server */ + ELS_RNIDA_MF_BR = 1UL << 22, /* bridge */ + ELS_RNIDA_MF_VIRT = 1UL << 21, /* virtualization device */ +}; + +enum fc_els_rnid_mgmt { + ELS_RNIDM_SNMP = 0, + ELS_RNIDM_TELNET = 1, + ELS_RNIDM_HTTP = 2, + ELS_RNIDM_HTTPS = 3, + ELS_RNIDM_XML = 4, /* HTTP + XML */ +}; + +enum fc_els_rnid_ipver { + ELS_RNIDIP_NONE = 0, /* no IP support or node mgmt. */ + ELS_RNIDIP_V4 = 1, /* IPv4 */ + ELS_RNIDIP_V6 = 2, /* IPv6 */ +}; + +/* + * ELS RPL - Read Port List. + */ +struct fc_els_rpl { + __u8 rpl_cmd; /* command */ + __u8 rpl_resv[5]; /* reserved - must be zero */ + __be16 rpl_max_size; /* maximum response size or zero */ + __u8 rpl_resv1; /* reserved - must be zero */ + __u8 rpl_index[3]; /* starting index */ +}; + +/* + * Port number block in RPL response. + */ +struct fc_els_pnb { + __be32 pnb_phys_pn; /* physical port number */ + __u8 pnb_resv; /* reserved */ + __u8 pnb_port_id[3]; /* port ID */ + __be64 pnb_wwpn; /* port name */ +}; + +/* + * RPL LS_ACC response. + */ +struct fc_els_rpl_resp { + __u8 rpl_cmd; /* ELS_LS_ACC */ + __u8 rpl_resv1; /* reserved - must be zero */ + __be16 rpl_plen; /* payload length */ + __u8 rpl_resv2; /* reserved - must be zero */ + __u8 rpl_llen[3]; /* list length */ + __u8 rpl_resv3; /* reserved - must be zero */ + __u8 rpl_index[3]; /* starting index */ + struct fc_els_pnb rpl_pnb[1]; /* variable number of PNBs */ +}; + +/* + * Link Error Status Block. + */ +struct fc_els_lesb { + __be32 lesb_link_fail; /* link failure count */ + __be32 lesb_sync_loss; /* loss of synchronization count */ + __be32 lesb_sig_loss; /* loss of signal count */ + __be32 lesb_prim_err; /* primitive sequence error count */ + __be32 lesb_inv_word; /* invalid transmission word count */ + __be32 lesb_inv_crc; /* invalid CRC count */ +}; + +/* + * ELS RPS - Read Port Status Block request. + */ +struct fc_els_rps { + __u8 rps_cmd; /* command */ + __u8 rps_resv[2]; /* reserved - must be zero */ + __u8 rps_flag; /* flag - see below */ + __be64 rps_port_spec; /* port selection */ +}; + +enum fc_els_rps_flag { + FC_ELS_RPS_DID = 0x00, /* port identified by D_ID of req. */ + FC_ELS_RPS_PPN = 0x01, /* port_spec is physical port number */ + FC_ELS_RPS_WWPN = 0x02, /* port_spec is port WWN */ +}; + +/* + * ELS RPS LS_ACC response. + */ +struct fc_els_rps_resp { + __u8 rps_cmd; /* command - LS_ACC */ + __u8 rps_resv[2]; /* reserved - must be zero */ + __u8 rps_flag; /* flag - see below */ + __u8 rps_resv2[2]; /* reserved */ + __be16 rps_status; /* port status - see below */ + struct fc_els_lesb rps_lesb; /* link error status block */ +}; + +enum fc_els_rps_resp_flag { + FC_ELS_RPS_LPEV = 0x01, /* L_port extension valid */ +}; + +enum fc_els_rps_resp_status { + FC_ELS_RPS_PTP = 1 << 5, /* point-to-point connection */ + FC_ELS_RPS_LOOP = 1 << 4, /* loop mode */ + FC_ELS_RPS_FAB = 1 << 3, /* fabric present */ + FC_ELS_RPS_NO_SIG = 1 << 2, /* loss of signal */ + FC_ELS_RPS_NO_SYNC = 1 << 1, /* loss of synchronization */ + FC_ELS_RPS_RESET = 1 << 0, /* in link reset protocol */ +}; + +/* + * ELS LIRR - Link Incident Record Registration request. + */ +struct fc_els_lirr { + __u8 lirr_cmd; /* command */ + __u8 lirr_resv[3]; /* reserved - must be zero */ + __u8 lirr_func; /* registration function */ + __u8 lirr_fmt; /* FC-4 type of RLIR requested */ + __u8 lirr_resv2[2]; /* reserved - must be zero */ +}; + +enum fc_els_lirr_func { + ELS_LIRR_SET_COND = 0x01, /* set - conditionally receive */ + ELS_LIRR_SET_UNCOND = 0x02, /* set - unconditionally receive */ + ELS_LIRR_CLEAR = 0xff /* clear registration */ +}; + +/* + * ELS SRL - Scan Remote Loop request. + */ +struct fc_els_srl { + __u8 srl_cmd; /* command */ + __u8 srl_resv[3]; /* reserved - must be zero */ + __u8 srl_flag; /* flag - see below */ + __u8 srl_flag_param[3]; /* flag parameter */ +}; + +enum fc_els_srl_flag { + FC_ELS_SRL_ALL = 0x00, /* scan all FL ports */ + FC_ELS_SRL_ONE = 0x01, /* scan specified loop */ + FC_ELS_SRL_EN_PER = 0x02, /* enable periodic scanning (param) */ + FC_ELS_SRL_DIS_PER = 0x03, /* disable periodic scanning */ +}; + +/* + * ELS RLS - Read Link Error Status Block request. + */ +struct fc_els_rls { + __u8 rls_cmd; /* command */ + __u8 rls_resv[4]; /* reserved - must be zero */ + __u8 rls_port_id[3]; /* port ID */ +}; + +/* + * ELS RLS LS_ACC Response. + */ +struct fc_els_rls_resp { + __u8 rls_cmd; /* ELS_LS_ACC */ + __u8 rls_resv[3]; /* reserved - must be zero */ + struct fc_els_lesb rls_lesb; /* link error status block */ +}; + +/* + * ELS RLIR - Registered Link Incident Report. + * This is followed by the CLIR and the CLID, described below. + */ +struct fc_els_rlir { + __u8 rlir_cmd; /* command */ + __u8 rlir_resv[3]; /* reserved - must be zero */ + __u8 rlir_fmt; /* format (FC4-type if type specific) */ + __u8 rlir_clr_len; /* common link incident record length */ + __u8 rlir_cld_len; /* common link incident desc. length */ + __u8 rlir_slr_len; /* spec. link incident record length */ +}; + +/* + * CLIR - Common Link Incident Record Data. - Sent via RLIR. + */ +struct fc_els_clir { + __be64 clir_wwpn; /* incident port name */ + __be64 clir_wwnn; /* incident port node name */ + __u8 clir_port_type; /* incident port type */ + __u8 clir_port_id[3]; /* incident port ID */ + + __be64 clir_conn_wwpn; /* connected port name */ + __be64 clir_conn_wwnn; /* connected node name */ + __be64 clir_fab_name; /* fabric name */ + __be32 clir_phys_port; /* physical port number */ + __be32 clir_trans_id; /* transaction ID */ + __u8 clir_resv[3]; /* reserved */ + __u8 clir_ts_fmt; /* time stamp format */ + __be64 clir_timestamp; /* time stamp */ +}; + +/* + * CLIR clir_ts_fmt - time stamp format values. + */ +enum fc_els_clir_ts_fmt { + ELS_CLIR_TS_UNKNOWN = 0, /* time stamp field unknown */ + ELS_CLIR_TS_SEC_FRAC = 1, /* time in seconds and fractions */ + ELS_CLIR_TS_CSU = 2, /* time in clock synch update format */ +}; + +/* + * Common Link Incident Descriptor - sent via RLIR. + */ +struct fc_els_clid { + __u8 clid_iq; /* incident qualifier flags */ + __u8 clid_ic; /* incident code */ + __be16 clid_epai; /* domain/area of ISL */ +}; + +/* + * CLID incident qualifier flags. + */ +enum fc_els_clid_iq { + ELS_CLID_SWITCH = 0x20, /* incident port is a switch node */ + ELS_CLID_E_PORT = 0x10, /* incident is an ISL (E) port */ + ELS_CLID_SEV_MASK = 0x0c, /* severity 2-bit field mask */ + ELS_CLID_SEV_INFO = 0x00, /* report is informational */ + ELS_CLID_SEV_INOP = 0x08, /* link not operational */ + ELS_CLID_SEV_DEG = 0x04, /* link degraded but operational */ + ELS_CLID_LASER = 0x02, /* subassembly is a laser */ + ELS_CLID_FRU = 0x01, /* format can identify a FRU */ +}; + +/* + * CLID incident code. + */ +enum fc_els_clid_ic { + ELS_CLID_IC_IMPL = 1, /* implicit incident */ + ELS_CLID_IC_BER = 2, /* bit-error-rate threshold exceeded */ + ELS_CLID_IC_LOS = 3, /* loss of synch or signal */ + ELS_CLID_IC_NOS = 4, /* non-operational primitive sequence */ + ELS_CLID_IC_PST = 5, /* primitive sequence timeout */ + ELS_CLID_IC_INVAL = 6, /* invalid primitive sequence */ + ELS_CLID_IC_LOOP_TO = 7, /* loop initialization time out */ + ELS_CLID_IC_LIP = 8, /* receiving LIP */ +}; + +#endif /* _FC_ELS_H_ */ diff --git a/include/fc_gs.h b/include/fc_gs.h new file mode 100644 index 0000000..a37346d --- /dev/null +++ b/include/fc_gs.h @@ -0,0 +1,96 @@ +/* + * Copyright(c) 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FC_GS_H_ +#define _FC_GS_H_ + +#include + +/* + * Fibre Channel Services - Common Transport. + * From T11.org FC-GS-2 Rev 5.3 November 1998. + */ + +struct fc_ct_hdr { + __u8 ct_rev; /* revision */ + __u8 ct_in_id[3]; /* N_Port ID of original requestor */ + __u8 ct_fs_type; /* type of fibre channel service */ + __u8 ct_fs_subtype; /* subtype */ + __u8 ct_options; + __u8 _ct_resvd1; + __be16 ct_cmd; /* command / response code */ + __be16 ct_mr_size; /* maximum / residual size */ + __u8 _ct_resvd2; + __u8 ct_reason; /* reject reason */ + __u8 ct_explan; /* reason code explanation */ + __u8 ct_vendor; /* vendor unique data */ +}; + +#define FC_CT_HDR_LEN 16 /* expected sizeof (struct fc_ct_hdr) */ + +enum fc_ct_rev { + FC_CT_REV = 1 /* common transport revision */ +}; + +/* + * ct_fs_type values. + */ +enum fc_ct_fs_type { + FC_FST_ALIAS = 0xf8, /* alias service */ + FC_FST_MGMT = 0xfa, /* management service */ + FC_FST_TIME = 0xfb, /* time service */ + FC_FST_DIR = 0xfc, /* directory service */ +}; + +/* + * ct_cmd: Command / response codes + */ +enum fc_ct_cmd { + FC_FS_RJT = 0x8001, /* reject */ + FC_FS_ACC = 0x8002, /* accept */ +}; + +/* + * FS_RJT reason codes. + */ +enum fc_ct_reason { + FC_FS_RJT_CMD = 0x01, /* invalid command code */ + FC_FS_RJT_VER = 0x02, /* invalid version level */ + FC_FS_RJT_LOG = 0x03, /* logical error */ + FC_FS_RJT_IUSIZ = 0x04, /* invalid IU size */ + FC_FS_RJT_BSY = 0x05, /* logical busy */ + FC_FS_RJT_PROTO = 0x07, /* protocol error */ + FC_FS_RJT_UNABL = 0x09, /* unable to perform command request */ + FC_FS_RJT_UNSUP = 0x0b, /* command not supported */ +}; + +/* + * FS_RJT reason code explanations. + */ +enum fc_ct_explan { + FC_FS_EXP_NONE = 0x00, /* no additional explanation */ + FC_FS_EXP_PID = 0x01, /* port ID not registered */ + FC_FS_EXP_PNAM = 0x02, /* port name not registered */ + FC_FS_EXP_NNAM = 0x03, /* node name not registered */ + FC_FS_EXP_COS = 0x04, /* class of service not registered */ + FC_FS_EXP_FTNR = 0x07, /* FC-4 types not registered */ + /* definitions not complete */ +}; + +#endif /* _FC_GS_H_ */ diff --git a/include/fc_ns.h b/include/fc_ns.h new file mode 100644 index 0000000..f7751d5 --- /dev/null +++ b/include/fc_ns.h @@ -0,0 +1,208 @@ +/* + * Copyright(c) 2007 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FC_NS_H_ +#define _FC_NS_H_ + +#include + +/* + * Fibre Channel Services - Name Service (dNS) + * From T11.org FC-GS-2 Rev 5.3 November 1998. + */ + +/* + * Common-transport sub-type for Name Server. + */ +#define FC_NS_SUBTYPE 2 /* fs_ct_hdr.ct_fs_subtype */ + +/* + * Name server Requests. + * Note: this is an incomplete list, some unused requests are omitted. + */ +enum fc_ns_req { + FC_NS_GA_NXT = 0x0100, /* get all next */ + FC_NS_GI_A = 0x0101, /* get identifiers - scope */ + FC_NS_GPN_ID = 0x0112, /* get port name by ID */ + FC_NS_GNN_ID = 0x0113, /* get node name by ID */ + FC_NS_GSPN_ID = 0x0118, /* get symbolic port name */ + FC_NS_GID_PN = 0x0121, /* get ID for port name */ + FC_NS_GID_NN = 0x0131, /* get IDs for node name */ + FC_NS_GID_FT = 0x0171, /* get IDs by FC4 type */ + FC_NS_GPN_FT = 0x0172, /* get port names by FC4 type */ + FC_NS_GID_PT = 0x01a1, /* get IDs by port type */ + FC_NS_RPN_ID = 0x0212, /* reg port name for ID */ + FC_NS_RNN_ID = 0x0213, /* reg node name for ID */ + FC_NS_RFT_ID = 0x0217, /* reg FC4 type for ID */ + FC_NS_RSPN_ID = 0x0218, /* reg symbolic port name */ + FC_NS_RFF_ID = 0x021f, /* reg FC4 Features for ID */ + FC_NS_RSNN_NN = 0x0239, /* reg symbolic node name */ +}; + +/* + * Port type values. + */ +enum fc_ns_pt { + FC_NS_UNID_PORT = 0x00, /* unidentified */ + FC_NS_N_PORT = 0x01, /* N port */ + FC_NS_NL_PORT = 0x02, /* NL port */ + FC_NS_FNL_PORT = 0x03, /* F/NL port */ + FC_NS_NX_PORT = 0x7f, /* Nx port */ + FC_NS_F_PORT = 0x81, /* F port */ + FC_NS_FL_PORT = 0x82, /* FL port */ + FC_NS_E_PORT = 0x84, /* E port */ + FC_NS_B_PORT = 0x85, /* B port */ +}; + +/* + * Port type object. + */ +struct fc_ns_pt_obj { + __u8 pt_type; +}; + +/* + * Port ID object + */ +struct fc_ns_fid { + __u8 fp_flags; /* flags for responses only */ + __u8 fp_fid[3]; +}; + +/* + * fp_flags in port ID object, for responses only. + */ +#define FC_NS_FID_LAST 0x80 /* last object */ + +/* + * FC4-types object. + */ +#define FC_NS_TYPES 256 /* number of possible FC-4 types */ +#define FC_NS_BPW 32 /* bits per word in bitmap */ + +struct fc_ns_fts { + __be32 ff_type_map[FC_NS_TYPES / FC_NS_BPW]; /* bitmap of FC-4 types */ +}; + +/* + * FC4-features object. + */ +struct fc_ns_ff { + __be32 fd_feat[FC_NS_TYPES * 4 / FC_NS_BPW]; /* 4-bits per FC-type */ +}; + +/* + * GID_PT request. + */ +struct fc_ns_gid_pt { + __u8 fn_pt_type; + __u8 fn_domain_id_scope; + __u8 fn_area_id_scope; + __u8 fn_resvd; +}; + +/* + * GID_FT or GPN_FT request. + */ +struct fc_ns_gid_ft { + __u8 fn_resvd; + __u8 fn_domain_id_scope; + __u8 fn_area_id_scope; + __u8 fn_fc4_type; +}; + +/* + * GPN_FT response. + */ +struct fc_gpn_ft_resp { + __u8 fp_flags; /* see fp_flags definitions above */ + __u8 fp_fid[3]; /* port ID */ + __be32 fp_resvd; + __be64 fp_wwpn; /* port name */ +}; + +/* + * GID_PN request + */ +struct fc_ns_gid_pn { + __be64 fn_wwpn; /* port name */ +}; + +/* + * GID_PN response or GSPN_ID request + */ +struct fc_gid_pn_resp { + __u8 fp_resvd; + __u8 fp_fid[3]; /* port ID */ +}; + +/* + * GSPN_ID response + */ +struct fc_gspn_resp { + __u8 fp_name_len; + char fp_name[]; +}; + +/* + * RFT_ID request - register FC-4 types for ID. + */ +struct fc_ns_rft_id { + struct fc_ns_fid fr_fid; /* port ID object */ + struct fc_ns_fts fr_fts; /* FC-4 types object */ +}; + +/* + * RPN_ID request - register port name for ID. + * RNN_ID request - register node name for ID. + */ +struct fc_ns_rn_id { + struct fc_ns_fid fr_fid; /* port ID object */ + __be64 fr_wwn; /* node name or port name */ +} __attribute__((__packed__)); + +/* + * RSNN_NN request - register symbolic node name + */ +struct fc_ns_rsnn { + __be64 fr_wwn; /* node name */ + __u8 fr_name_len; + char fr_name[]; +} __attribute__((__packed__)); + +/* + * RSPN_ID request - register symbolic port name + */ +struct fc_ns_rspn { + struct fc_ns_fid fr_fid; /* port ID object */ + __u8 fr_name_len; + char fr_name[]; +} __attribute__((__packed__)); + +/* + * RFF_ID request - register FC-4 Features for ID. + */ +struct fc_ns_rff_id { + struct fc_ns_fid fr_fid; /* port ID object */ + __u8 fr_resvd[2]; + __u8 fr_feat; /* FC-4 Feature bits */ + __u8 fr_type; /* FC-4 type */ +} __attribute__((__packed__)); + +#endif /* _FC_NS_H_ */ diff --git a/include/fc_scsi.h b/include/fc_scsi.h new file mode 100644 index 0000000..541a6d7 --- /dev/null +++ b/include/fc_scsi.h @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _FC_SCSI_H_ +#define _FC_SCSI_H_ + +/* + * SCSI definitions. + * From T10 SBC-3. + */ + +/* + * Block size. + */ +#define SCSI_BSIZE 512 + +/* + * Operation codes. + */ +enum scsi_op { + SCSI_OP_TEST_UNIT_READY = 0, /* test unit ready */ + SCSI_OP_REQ_SENSE = 0x03, /* request sense */ + SCSI_OP_INQUIRY = 0x12, /* inquiry */ + SCSI_OP_START_STOP = 0x1b, /* start/stop unit command */ + SCSI_OP_READ_CAP10 = 0x25, /* read capacity (32-bit blk address) */ + SCSI_OP_READ10 = 0x28, /* read (32-bit block address) */ + SCSI_OP_WRITE10 = 0x2a, /* write (32-bit block address) */ + SCSI_OP_READ16 = 0x88, /* read (64-bit block address) */ + SCSI_OP_SA_IN_16 = 0x9e, /* serivice action in (16) */ + SCSI_OP_SA_OUT_16 = 0x9f, /* serivice action out (16) */ + SCSI_OP_REPORT_LUNS = 0xa0, /* report LUNs */ +}; + +/* + * Name table initializer for SCSI opcodes. + * Please keep this in sync with the enum above. + */ +#define SCSI_OP_NAME_INIT { \ + [SCSI_OP_TEST_UNIT_READY] = "test unit ready", \ + [SCSI_OP_REQ_SENSE] = "request sense", \ + [SCSI_OP_INQUIRY] = "inquiry", \ + [SCSI_OP_START_STOP] = "start/stop unit", \ + [SCSI_OP_READ_CAP10] = "read_cap(10)", \ + [SCSI_OP_READ10] = "read(10)", \ + [SCSI_OP_WRITE10] = "write(10)", \ + [SCSI_OP_READ16] = "read(16)", \ + [SCSI_OP_SA_IN_16] = "sa_in(16)", \ + [SCSI_OP_SA_OUT_16] = "sa_out(16)", \ + [SCSI_OP_REPORT_LUNS] = "report LUNs", \ +} + +/* + * Service action codes. + * Codes for SCSI_OP_SA_IN_16 and SCSI_OP_SA_OUT_16. + */ +enum scsi_sa_in_16 { + SCSI_SA_READ_CAP16 = 0x10, /* read capacity (16) (IN only) */ + SCSI_SA_RW_LONG = 0x11, /* read/write long (16) */ +}; + +/* + * Status codes. + */ +enum scsi_status { + SCSI_ST_GOOD = 0x00, /* good */ + SCSI_ST_CHECK = 0x02, /* check condition */ + SCSI_ST_COND_MET = 0x04, /* condition met */ + SCSI_ST_BUSY = 0x08, /* busy */ + SCSI_ST_INTMED = 0x10, /* intermediate */ + SCSI_ST_INTMED_MET = 0x14, /* intermediate, condition met */ + SCSI_ST_RESERVED = 0x18, /* reservation conflict */ + SCSI_ST_TS_FULL = 0x28, /* task set full */ + SCSI_ST_ACA_ACTV = 0x30, /* ACA active */ + SCSI_ST_ABORTED = 0x40, /* task aborted */ +}; + +/* + * Control byte. + */ +#define SCSI_CTL_LINK (1 << 0) /* Task is linked accross multiple commands */ +#define SCSI_CTL_NACA (1 << 2) /* Normal auto contingent allegiance (ACA) */ + +/* + * Test Unit Ready command. + */ +struct scsi_test_unit_ready { + net8_t tr_op; /* opcode (0) */ + net8_t _tr_resvd[4]; /* reserved */ + net8_t tr_control; /* control bits */ +}; + +/* + * Request Sense command. + */ +struct scsi_req_sense { + net8_t rs_op; /* opcode (0x88) */ + net8_t rs_flags; /* LSB is descriptor sense bit */ + net8_t _rs_resvd[2]; + net8_t rs_alloc_len; /* allocated reply length */ + net8_t rs_control; /* control bits */ +}; + +#define SCSI_REQ_SENSE_LEN 6 /* expected length of struct */ + +#define SCSI_SENSE_LEN_MAX 252 /* maximum rs_alloc_len */ + +/* + * Start / stop command. + */ +struct scsi_start { + net8_t ss_op; /* opcode (0x88) */ + net8_t ss_immed; /* LSB is respond-immediately bit */ + net8_t _ss_resvd[2]; + net8_t ss_flags; /* power condition, flags */ + net8_t ss_control; /* control bits */ +}; + +#define SCSI_START_LEN 6 /* expected length of struct */ + +/* + * ss_flags: + */ +#define SCSI_SSF_START 0x01 /* start */ +#define SCSI_SSF_LOEJ 0x02 /* load or eject depending on start */ + +#define SCSI_SSF_ACTIVE 0x10 /* set active power condition */ +#define SCSI_SSF_IDLE 0x20 /* set idle power condition */ +#define SCSI_SSF_STANDBY 0x30 /* set idle power condition */ +#define SCSI_SSF_LU_CONTROL 0x70 /* set local control of power */ +#define SCSI_SSF_IDLE_0 0xa0 /* force idle timer to zero */ +#define SCSI_SSF_STDBY_0 0xb0 /* force standby timer to zero */ + +/* + * Read Capacity (10) command. + */ +struct scsi_rcap10 { + net8_t rc_op; /* opcode */ + net8_t _rc_resvd; + ua_net32_t rc_lba; /* logical block address */ + net8_t _rc_resvd1[2]; + net8_t rc_flags; /* flags (see below) */ + net8_t rc_control; /* control */ +}; + +#define SCSI_RCAP10_LEN 10 /* expected length of struct */ + +#define SCSI_RCAPF_PMI (1 << 0) /* rc_flags: partial medium indicator */ + +struct scsi_rcap10_resp { + net32_t rc_lba; /* logical block address (size) */ + net32_t rc_block_len; /* block length in bytes */ +}; + +/* + * Read Capacity (16) command. + */ +struct scsi_rcap16 { + net8_t rc_op; /* opcode (0x9e) */ + net8_t rc_sa; /* serivce action sub-opcode (0x10) */ + ua_net64_t rc_lba; /* logical block address */ + ua_net32_t rc_alloc_len; /* allocation length */ + net8_t rc_flags; /* flags (see scsi_rcap10 rc_flags) */ + net8_t rc_control; /* control */ +}; + +#define SCSI_RCAP16_LEN 16 /* expected length of struct */ + +struct scsi_rcap16_resp { + net64_t rc_lba; /* logical block address (size) */ + net32_t rc_block_len; /* block length in bytes */ +}; + +/* + * Read(10) or write(10) command. + */ +struct scsi_rw10 { + net8_t rd_op; /* opcode */ + net8_t rd_flags; + ua_net32_t rd_lba; /* logical block address */ + net8_t rd_group; /* group number */ + ua_net16_t rd_len; /* transfer length */ + net8_t rd_control; /* control */ +}; + +#define SCSI_RW10_LEN 10 /* expected length of struct */ + +/* + * Read(16) or write(16) command. + */ +struct scsi_rw16 { + net8_t rd_op; /* opcode */ + net8_t rd_flags; + ua_net64_t rd_lba; /* logical block address */ + ua_net32_t rd_len; /* transfer length */ + net8_t rd_group; /* group number */ + net8_t rd_control; /* control */ +}; + +#define SCSI_RW16_LEN 16 /* expected length of struct */ + +/* + * Flags: + */ +#define RDF_RWPROT_BIT 5 /* shift for RD/WRPROTECT field */ +#define RDF_DPO 0x10 /* disable page out - cache advisory */ +#define RDF_FUA 0x08 /* force unit access */ +#define RDF_FUA_NV 0x02 /* force unit access non-volatile */ + +/* + * REPORT LUNS. + */ +struct scsi_report_luns { + net8_t rl_op; /* opcode (0x88) */ + net8_t _rl_resvd1; + net8_t rl_sel_report; /* select report field */ + net8_t _rl_resvd2[3]; + ua_net32_t rl_alloc_len; /* allocated length for reply */ + net8_t _rl_resvd3; + net8_t rl_control; /* control */ +}; + +#define SCSI_REPORT_LUNS_LEN 12 /* expected length of struct */ + +/* + * rl_sel_report. + */ +#define SCSI_RLS_WKL 1 /* req. well known LUNs only */ +#define SCSI_RLS_ITL 2 /* req. LUNs accessible to I_T nexus */ + +/* + * REPORT LUNS repsonse. + */ +struct scsi_report_luns_resp { + net32_t rl_len; /* list length in bytes */ + net8_t _rl_resvd[4]; + net64_t rl_lun[1]; /* list of LUNs */ +}; + +/* + * Inquiry. + */ +struct scsi_inquiry { + net8_t in_op; /* opcode (0x12) */ + net8_t in_flags; /* LSB is EVPD */ + net8_t in_page_code; /* page code */ + + /* + * Note that the in_alloc_len field was widened to 16-bits between + * SPC-2 and SPC-3, but some devices will ignore the upper 8 bits. + * It makes sense to use lengths less than 256 where possible. + */ + ua_net16_t in_alloc_len; /* allocated length for reply */ + net8_t in_control; /* control */ +}; + +#define SCSI_INQUIRY_LEN 6 /* expected length of struct */ + +/* + * Inquiry in_flags. + */ +#define SCSI_INQF_EVPD (1 << 0) /* request vital product data (VPD) page */ + +/* + * SCSI Inquiry VPD Page Codes. + */ +enum scsi_inq_page { + SCSI_INQP_SUPP_VPD = 0, /* supported VPD list */ + SCSI_INQP_UNIT_SN = 0x80, /* Unit serial number */ + SCSI_INQP_DEV_ID = 0x83, /* Device Identification */ + SCSI_INQP_SW_IF_ID = 0x84, /* Software Interface Identification */ + SCSI_INQP_MGMT_ADDR = 0x85, /* management network addresses */ + SCSI_INQP_EXT_DATA = 0x86, /* Extended Inquiry Data */ + SCSI_INQP_MD_PAGE_POL = 0x87, /* Mode Page Policy */ + SCSI_INQP_SCSI_PORTS = 0x88, /* SCSI Ports */ +}; + +/* + * Inquiry - standard data format. + */ +struct scsi_inquiry_std { + net8_t is_periph; /* peripheral qualifier and type */ + net8_t is_flags1; /* flags (see below) */ + net8_t is_version; + net8_t is_flags2; /* flags / response data format */ + + net8_t is_addl_len; /* additional length */ + net8_t is_flags3; /* flags (see below) */ + net8_t is_flags4; + net8_t is_flags5; + + char is_vendor_id[8]; /* ASCII T10 vendor identification */ + char is_product[16]; /* ASCII product identification */ + char is_rev_level[4]; /* ASCII revision level */ + char is_vendor_spec[56 - 36]; /* vendor specific data */ + + net8_t is_clock_flags; /* clocking, QAS, IUS flags */ + net8_t is_resvd; + net16_t is_vers_desc[8]; /* version descriptors */ + + /* followed by vendor-specific fields */ +}; + +#define SCSI_INQUIRY_STD_LEN 74 /* expected length of structure */ + +/* + * Peripheral qualifier in is_periph field. + */ +#define SCSI_INQ_PQUAL_MASK 0xe0 /* mask for peripheral qualifier */ +#define SCSI_INQ_PTYPE_MASK 0x1f /* mask for peripheral type */ + +enum scsi_inq_pqual { + SCSI_PQUAL_ATT = 0, /* peripheral attached */ + SCSI_PQUAL_DET = (1 << 5), /* peripheral detached */ + SCSI_PQUAL_NC = (3 << 5), /* not capable of attachment */ +}; + +enum scsi_inq_ptype { + SCSI_PTYPE_DIR = 0x00, /* direct acccess block device */ + SCSI_PTYPE_SEQ = 0x01, /* sequential acccess block device */ + SCSI_PTYPE_PRINT = 0x02, /* printer device (obsolete) */ + SCSI_PTYPE_PROC = 0x03, /* processor device */ + SCSI_PTYPE_WORM = 0x04, /* write-once device */ + SCSI_PTYPE_CDDVD = 0x05, /* CD/DVD device */ + SCSI_PTYPE_SCANNER = 0x06, /* scanner device (obsolete) */ + SCSI_PTYPE_OPTMEM = 0x07, /* optical memory device */ + SCSI_PTYPE_CHANGER = 0x08, /* medium changer device */ + SCSI_PTYPE_RAID = 0x0c, /* storage array controoler (RAID) */ + SCSI_PTYPE_SES = 0x0d, /* enclosure services device */ + SCSI_PTYPE_SDIR = 0x0e, /* simplified direct acccess */ + SCSI_PTYPE_OCRW = 0x0f, /* optical card reader/writer */ + SCSI_PTYPE_BCC = 0x10, /* bridge controller commands */ + SCSI_PTYPE_OSD = 0x11, /* object-based storage device */ + SCSI_PTYPE_ADC = 0x12, /* automation/drive interface */ + SCSI_PTYPE_UNK = 0x1f, /* unknown or no device type */ +}; + +/* + * is_flags[1-5] in the standard inquiry response. + */ +#define SCSI_INQF1_RMB (1 << 7) /* removable medium */ + +#define SCSI_INQF2_NACA (1 << 5) /* normal ACA */ +#define SCSI_INQF2_HISUP (1 << 4) /* hierarchical LUN support */ +#define SCSI_INQF2_RDF_MASK 0xf /* response data format mask */ +#define SCSI_INQF2_RDF 2 /* this response data format */ + +#define SCSI_INQF3_PROTECT (1 << 0) /* supports protection information */ +#define SCSI_INQF3_3PC (1 << 3) /* supports third-party copy */ +#define SCSI_INQF3_TPGS_IMPL (1 << 4) /* supports REPORT TARGET PORT GRPS */ +#define SCSI_INQF3_TPGS_EXPL (1 << 5) /* supports SET TARGET PORT GROUPS */ +#define SCSI_INQF3_ACC (1 << 6) /* access controls coordinator */ +#define SCSI_INQF3_SCCS (1 << 7) /* SCC supported (see SCC-2) */ + +#define SCSI_INQF4_ADDR16 (1 << 0) /* parallel-SCSI only */ +#define SCSI_INQF4_MULTIP (1 << 4) /* multiport compliant */ +#define SCSI_INQF4_ENCSERV (1 << 6) /* embedded enclosure services */ +#define SCSI_INQF4_BQUE (1 << 7) /* basic task management model */ + +#define SCSI_INQF5_CMDQUE (1 << 1) /* full task management model */ +#define SCSI_INQF5_LINKED (1 << 3) /* linked commands supported */ +#define SCSI_INQF5_SYNC (1 << 4) /* parallel-SCSI only */ +#define SCSI_INQF5_WBUS16 (1 << 5) /* parallel-SCSI only */ + +/* + * Inquiry - page 0 - supported VPD pages. + */ +struct scsi_inquiry_supp_vpd { + net8_t is_periph; /* peripheral qualifier and type */ + net8_t is_page_code; /* page code (0x00) */ + net8_t _is_resvd; /* reserved */ + net8_t is_list_len; /* length of page list */ + net8_t is_page_list[1]; /* supported page list - var. length */ +}; + +/* + * Inquiry - page 0x80 - unit serial number VPD page. + */ +struct scsi_inquiry_unit_sn { + net8_t is_periph; /* peripheral qualifier and type */ + net8_t is_page_code; /* page code (0x80) */ + net8_t _is_resvd; /* reserved */ + net8_t is_page_len; /* length of serial number */ + net8_t is_serial[1]; /* ASCII serial number - var. length */ +}; + +/* + * Inquiry - page 0x83 - device identification. + */ +struct scsi_inquiry_dev_id { + net8_t is_periph; /* peripheral qualifier and type */ + net8_t is_page_code; /* page code (0x83) */ + net16_t is_page_len; /* len of designation descriptor list */ + + /* descriptor list follows */ +}; + +/* + * Inquiry - page 0x83 designation descriptor list entry. + */ +struct scsi_inquiry_desc { + net8_t id_proto_code; /* protocol identifier and code set */ + net8_t id_type_flags; /* designator type and flags */ + net8_t _id_resvd; /* reserved */ + net8_t id_designator_len; /* designator length */ + net8_t id_designator[1]; /* designator - variable length */ +}; + +/* + * id_proto_code field. + */ +#define SCSI_INQ_CODE_MASK 0xf /* mask for code set in id_proto_code */ + +enum scsi_inq_code_set { + SCSI_CS_BIN = 1, /* designator contains binary values */ + SCSI_CS_ASCII = 2, /* designator contains printable ASCII */ + SCSI_CS_UTF8 = 3, /* designator contains UTF-8 codes */ +}; + +/* + * id_type_flags field. + */ +#define SCSI_INQT_PIV (1 << 7) /* protocol identifier field valid */ +#define SCSI_INQT_ASSOC_BIT 4 /* shift count for association */ +#define SCSI_INQT_ASSOC_MASK 0x3 /* mask for association */ +#define SCSI_INQT_TYPE_MASK 0xf /* mask for designator type */ + +enum scsi_inq_assoc { + SCSI_ASSOC_LUN = 0, /* designator is for the LUN */ + SCSI_ASSOC_PORT = 1, /* designator is for the target port */ + SCSI_ASSOC_TARG = 2, /* designator is for the target dev */ +}; + +/* + * Designator type field values. + */ +enum scsi_inq_dtype { + SCSI_DTYPE_VENDOR = 0, /* vendor specific */ + SCSI_DTYPE_T10_VENDOR = 1, /* T10 vendor-ID based */ + SCSI_DTYPE_EUI_64 = 2, /* EUI-64 based */ + SCSI_DTYPE_NAA = 3, /* network address authority (WWN) */ + SCSI_DTYPE_RTPI = 4, /* relative target port id */ + SCSI_DTYPE_TPORTG = 5, /* target port group */ + SCSI_DTYPE_LPORTG = 6, /* logical port group */ + SCSI_DTYPE_MD5_LUN = 7, /* MD5 LU identifier */ + SCSI_DTYPE_SCSI_NAME = 8, /* SCSI name string */ +}; + +#endif /* _FC_SCSI_H_ */ diff --git a/include/fc_types.h b/include/fc_types.h new file mode 100644 index 0000000..3133bba --- /dev/null +++ b/include/fc_types.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LIBFC_TYPES_H_ +#define _LIBFC_TYPES_H_ + +#include "net_types.h" + +/* + * Host-order type definitions for Fibre Channel. + */ + +/* + * Note, in order for fc_wwn_t to be acceptable for %qx format strings, + * it cannot be declared as u_int64_t. + */ +typedef unsigned long long fc_wwn_t; /* world-wide name */ +typedef u_int32_t fc_fid_t; /* fabric address */ +typedef u_int16_t fc_xid_t; /* exchange ID */ + +/* + * Encapsulation / port option flags. + */ +#define FC_OPT_DEBUG_RX 0x01 /* log debug messages */ +#define FC_OPT_DEBUG_TX 0x02 /* log debug messages */ +#define FC_OPT_DEBUG (FC_OPT_DEBUG_RX | FC_OPT_DEBUG_TX) +#define FC_OPT_NO_TX_CRC 0x04 /* don't generate sending CRC */ +#define FC_OPT_NO_RX_CRC 0x08 /* don't check received CRC */ +#define FC_OPT_FCIP_NO_SFS 0x10 /* No special frame (FCIP only) */ +#define FC_OPT_PASSIVE 0x20 /* Responding to connect */ +#define FC_OPT_SET_MAC 0x40 /* use non-standard MAC addr (FCOE) */ +#define FC_OPT_FCOE_OLD 0x80 /* use old prototype FCoE encaps */ + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +fc_wwn_t fc_wwn_from_mac(u_int64_t, u_int32_t scheme, u_int32_t port); +fc_wwn_t fc_wwn_from_wwn(fc_wwn_t, u_int32_t scheme, u_int32_t port); + +#endif /* _LIBFC_TYPES_H_ */ diff --git a/include/fcoe_utils.h b/include/fcoe_utils.h new file mode 100644 index 0000000..29b8ef0 --- /dev/null +++ b/include/fcoe_utils.h @@ -0,0 +1,101 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOE_UTILS_H_ +#define _FCOE_UTILS_H_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define UNUSED __attribute__((__unused__)) + +#define MAX_STR_LEN 512 +#define MAX_PATH_LEN MAX_STR_LEN + +#define SYSFS_MOUNT "/sys" +#define SYSFS_NET SYSFS_MOUNT "/class/net" +#define SYSFS_FCHOST SYSFS_MOUNT "/class/fc_host" +#define SYSFS_FCOE_BUS SYSFS_MOUNT "/bus/fcoe" +#define SYSFS_FCOE_BUS_DEVICES SYSFS_FCOE_BUS "/devices" + +#define SYSFS_FCOE SYSFS_MOUNT "/module/libfcoe/parameters" /* legacy */ +#define FCOE_CREATE SYSFS_FCOE "/create" /* legacy */ +#define FCOE_CREATE_VN2VN SYSFS_FCOE "/create_vn2vn" /* legacy */ +#define FCOE_DESTROY SYSFS_FCOE "/destroy" /* legacy */ +#define FCOE_ENABLE SYSFS_FCOE "/enable" /* legacy */ +#define FCOE_DISABLE SYSFS_FCOE "/disable" /* legacy */ + +#define FCOE_BUS_CREATE SYSFS_FCOE_BUS "/ctlr_create" +#define FCOE_BUS_DESTROY SYSFS_FCOE_BUS "/ctlr_destroy" +#define FCOE_CTLR_ATTR_ENABLED "/enabled" +#define FCOE_CTLR_ATTR_MODE "/mode" + +#define FCHOSTBUFLEN 64 + +/* + * This macro assumes that progname has been set + */ +#define FCOE_LOG_ERR(fmt, args...) \ + do { \ + fprintf(stderr, "%s: " fmt, progname, ##args); \ + } while (0) + + +enum fcoe_status { + SUCCESS = 0, /* Success */ + EFAIL, /* Command Failed */ + ENOACTION, /* No action was taken */ + EFCOECONN, /* FCoE connection already exists */ + ENOFCOECONN, /* No FCoE connection on interface */ + ENOFCHOST, /* FC Host found */ + EINTERR, /* Internal error */ + EINVALARG, /* Invalid argument */ + EBADNUMARGS, /* Invalid number of arguments */ + EIGNORE, /* Ignore this error value */ + ENOSYSFS, /* sysfs is not present */ + ENOETHDEV, /* Not a valid Ethernet interface */ + ENOMONCONN, /* Not connected to fcoemon */ + ECONNTMOUT, /* Connection to fcoemon timed out */ + EHBAAPIERR, /* Error using HBAAPI/libhbalinux */ + EBADCLIFMSG, /* Messaging error */ +}; + +enum fcoe_status fcoe_validate_interface(char *ifname); +enum fcoe_status fcoe_validate_fcoe_conn(char *ifname); +enum fcoe_status fcoe_find_fchost(const char *ifname, char *fchost, int len); +enum fcoe_status fcoe_find_ctlr(const char *fchost, char *ctlr, int len); +int fcoe_checkdir(char *dir); +int check_symbolic_name_for_interface(const char *symbolic_name, + const char *ifname); +char *get_ifname_from_symbolic_name(const char *symbolic_name); +int fcoe_sysfs_read(char *buf, int size, const char *path); +enum fcoe_status fcm_write_str_to_sysfs_file(const char *path, const char *str); +enum fcoe_status fcm_write_str_to_ctlr_attr(const char *ctlr, + const char *attr, + const char *str); +#endif /* _FCOE_UTILS_H_ */ diff --git a/include/fcoe_utils_version.h.in b/include/fcoe_utils_version.h.in new file mode 100644 index 0000000..642f37d --- /dev/null +++ b/include/fcoe_utils_version.h.in @@ -0,0 +1,6 @@ +#ifndef _FCOE_UTILS_VERSION_H_ +#define _FCOE_UTILS_VERSION_H_ + +#define FCOE_UTILS_VERSION "@VERSION@" + +#endif /* _FCOE_UTILS_VERSION_H_ */ diff --git a/include/fcoemon_utils.h b/include/fcoemon_utils.h new file mode 100644 index 0000000..fb3b1cc --- /dev/null +++ b/include/fcoemon_utils.h @@ -0,0 +1,401 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _FCOEMON_UTILS_H_ +#define _FCOEMON_UTILS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fc_types.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +__attribute__((__format__(__printf__, 1, 2))) +void sa_log(const char *format, ...); +__attribute__((__format__(__printf__, 1, 2))) +void sa_log_debug(const char *format, ...); +__attribute__((__format__(__printf__, 3, 4))) +void sa_log_err(int, const char *func, const char *format, ...); + +/* + * These functions can be provided outside of libsa for those environments + * that want to redirect them. + */ +void sa_log_output(const char *); /* log message */ +void sa_log_abort(const char *); /* log message and abort */ + +#define __SA_STRING(x) #x + +/* + * Logging options. + */ +#define SA_LOGF_TIME 0x0001 /* include timestamp in message */ +#define SA_LOGF_DELTA 0x0002 /* include time since last message */ + +extern u_int sa_log_flags; /* timestamp and other option flags */ +extern int sa_log_time_delta_min; /* minimum diff to print in millisec */ +extern char *sa_log_prefix; /* string to print before any message */ + +__attribute__((__format__(__printf__, 1, 2))) +extern void assert_failed(const char *s, ...); + +#ifndef UNLIKELY +#define UNLIKELY(_x) (_x) +#endif /* UNLIKELY */ + +/* + * ASSERT macros + * + * ASSERT(expr) - this calls assert_failed() if expr is false. This variant + * is not present in production code or if DEBUG_ASSERTS is not defined. + * Be careful not to rely on expr being evaluated. + */ +#if defined(DEBUG_ASSERTS) +#define ASSERT(_x) do { \ + if (UNLIKELY(!(_x))) { \ + assert_failed("ASSERT FAILED (%s) @ %s:%d\n", \ + "" #_x, __FILE__, __LINE__); \ + } \ + } while (0) +#else +#define ASSERT(_x) +#endif /* DEBUG_ASSERTS */ + +/* + * ASSERT_NOTIMPL(expr) - this calls assert_failed() if expr is false. + * The implication is that the condition is not handled by the current + * implementation, and work should be done eventually to handle this. + */ +#define ASSERT_NOTIMPL(_x) do { \ + if (UNLIKELY(!(_x))) { \ + assert_failed("ASSERT (NOT IMPL) " \ + "(%s) @ %s:%d\n", \ + "" #_x, __FILE__, __LINE__); \ + } \ + } while (0) + +/* + * ASSERT_NOTREACHED - this is the same as ASSERT_NOTIMPL(0). + */ +#define ASSERT_NOTREACHED do { \ + assert_failed("ASSERT (NOT REACHED) @ %s:%d\n", \ + __FILE__, __LINE__); \ + } while (0) + +/* + * ASSERT_BUG(bugno, expr). This variant is used when a bug number has + * been assigned to any one of the other assertion failures. It is always + * present in code. It gives the bug number which helps locate + * documentation and helps prevent duplicate bug filings. + */ +#define ASSERT_BUG(_bugNr, _x) do { \ + if (UNLIKELY(!(_x))) { \ + assert_failed("ASSERT (BUG %d) (%s) @ %s:%d\n", \ + (_bugNr), #_x, __FILE__, __LINE__); \ + } \ + } while (0) + +#ifndef LIBSA_USE_DANGEROUS_ROUTINES +#define strcpy DONT_USE_strcpy +#define strcat DONT_USE_strcat +#define gets DONT_USE_gets +#endif /* LIBSA_USE_DANGEROUS_ROUTINES */ + +char *sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len); + +/* + * Structure for tables encoding and decoding name-value pairs such as enums. + */ +struct sa_nameval { + char *nv_name; + u_int32_t nv_val; +}; + +const char *sa_enum_decode(char *, size_t, const struct sa_nameval *, u_int); +int sa_enum_encode(const struct sa_nameval *tp, const char *, u_int *); +const char *sa_flags_decode(char *, size_t, const struct sa_nameval *, u_int); + +/* + * Timer facility. + */ + +struct sa_timer { + struct sa_timer *tm_next; + u_int64_t tm_nsec; /* relative time to event (nSec) */ + void (*tm_handler)(void *arg); + void *tm_handler_arg; + struct sa_timer **timer_head; +}; + + +#define SA_TIMER_UNITS (1000 * 1000UL) /* number of timer ticks per second */ + +/* + * Initialize a pre-allocated timer structure. + */ +void sa_timer_init(struct sa_timer *, void (*handler)(void *), void *arg); + +/* + * Test whether the timer is active. + */ +static inline int sa_timer_active(struct sa_timer *tm) +{ + return tm->tm_nsec != 0; +} + +/* + * Allocate a timer structure. Set handler. + */ +struct sa_timer *sa_timer_alloc(void (*)(void *arg), void *arg); + +/* + * Set timer to fire. Delta is in microseconds from now. + */ +void sa_timer_set(struct sa_timer *, u_long delta); + +/* + * Cancel timer. + */ +void sa_timer_cancel(struct sa_timer *); + +/* + * Free (and cancel) timer. + */ +void sa_timer_free(struct sa_timer *); + + +/* + * Handle timer checks. Called from select loop or other periodic function. + * + * The struct timeval passed in indicates how much time has passed since + * the last call, and is set before returning to the maximum amount of time + * that should elapse before the next call. + * + * Returns 1 if any timer handlers were invoked, 0 otherwise. + */ +int sa_timer_check(struct timeval *); + +/* + * Get time in nanoseconds since some arbitrary time. + */ +u_int64_t sa_timer_get(void); + +/* + * Get time in seconds since some arbitrary time. + */ +u_int sa_timer_get_secs(void); + +/* + * sa_select - Server Array select facility. + * + * This is a thin layer to poll files with a select loop. + */ + +/* + * Enter the polling loop which never exits. + */ +int sa_select_loop(void); + +/* + * Set callback for every time through the select loop. + */ +void sa_select_set_callback(void (*)(void)); + +/* + * Add a callback to handle files which are ready for receive, transmit, + * or to handle exceptions. + */ +void sa_select_add_fd(int fd, void (*rx_handler)(void *), + void (*tx_handler)(void *), + void (*ex_handler)(void *), void *arg); + +/* + * Change a single callback for a descriptor that's already been added. + */ +void sa_select_set_rx(int fd, void (*handler)(void *)); +void sa_select_set_tx(int fd, void (*handler)(void *)); +void sa_select_set_ex(int fd, void (*handler)(void *)); + +/* + * Remove all callbacks for a file descriptor. + */ +void sa_select_rem_fd(int fd); + +/* + * Cause select loop to return. + */ +void sa_select_exit(int sig); + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +extern fc_wwn_t +fc_wwn_from_mac(u_int64_t, u_int32_t scheme, u_int32_t port); + +extern int hex2int(char *b); + +extern int use_syslog; +void enable_syslog(int); +void enable_debug_log(int); + +/* + * Table and sysfs helpers + */ + +/* + * Structure for integer-indexed tables that can grow. + */ +struct sa_table { + u_int32_t st_size; /* number of entries in table */ + u_int32_t st_limit; /* end of valid entries in table (public) */ + void **st_table; /* re-allocatable array of pointers */ +}; + +/* + * Function prototypes + */ +extern int sa_sys_read_line(const char *, const char *, char *, size_t); +extern int sa_sys_write_line(const char *, const char *, const char *); +extern int sa_sys_read_int(const char *, const char *, int *); +extern int sa_sys_read_u32(const char *, const char *, u_int32_t *); +extern int sa_sys_read_u64(const char *, const char *, u_int64_t *); +extern int sa_dir_read(char *, int (*)(struct dirent *, void *), void *); +extern char *sa_strncpy_safe(char *dest, size_t len, + const char *src, size_t src_len); +extern const char *sa_enum_decode(char *, size_t, + const struct sa_nameval *, u_int32_t); +extern int sa_enum_encode(const struct sa_nameval *tp, + const char *, u_int32_t *); +extern const char *sa_flags_decode(char *, size_t, + const struct sa_nameval *, u_int32_t); +extern int sa_table_grow(struct sa_table *, u_int32_t index); +extern void sa_table_destroy_all(struct sa_table *); +extern void sa_table_destroy(struct sa_table *); +extern void sa_table_iterate(struct sa_table *tp, + void (*handler)(void *ep, void *arg), void *arg); +extern void *sa_table_search(struct sa_table *tp, + void *(*match)(void *ep, void *arg), void *arg); + +/** sa_table_init(tp) - initialize a table. + * @param tp table pointer. + * + * This just clears a table structure that was allocated by the caller. + */ +static inline void sa_table_init(struct sa_table *tp) +{ + memset(tp, 0, sizeof(*tp)); +} + +/** sa_table_lookup(tp, index) - lookup an entry in the table. + * @param tp table pointer. + * @param index the index in the table to access + * @returns the entry, or NULL if the index wasn't valid. + */ +static inline void *sa_table_lookup(const struct sa_table *tp, u_int32_t index) +{ + void *ep = NULL; + + if (index < tp->st_limit) + ep = tp->st_table[index]; + return ep; +} + +/** sa_table_lookup_n(tp, n) - find Nth non-empty entry in a table. + * @param tp table pointer. + * @param n is the entry number, the first non-empty entry is 0. + * @returns the entry, or NULL if the end of the table reached first. + */ +static inline void *sa_table_lookup_n(const struct sa_table *tp, u_int32_t n) +{ + void *ep = NULL; + u_int32_t i; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL && n-- == 0) + return ep; + } + return NULL; +} + +/** sa_table_insert(tp, index, ep) - Replace or insert an entry in the table. + * @param tp table pointer. + * @param index the index for the new entry. + * @param ep entry pointer. + * @returns index on success, or -1 if the insert failed. + * + * Note: if the table has never been used, and is still all zero, this works. + * + * Note: perhaps not safe for multithreading. Caller can lock the table + * externally, but reallocation can take a while, during which time the + * caller may not wish to hold the lock. + */ +static inline int sa_table_insert(struct sa_table *tp, + u_int32_t index, void *ep) +{ + if (index >= tp->st_limit && sa_table_grow(tp, index) < 0) + return -1; + tp->st_table[index] = ep; + return index; +} + +/** sa_table_append(tp, ep) - add entry to table and return index. + * + * @param tp pointer to sa_table structure. + * @param ep pointer to new entry, to be added at the end of the table. + * @returns new index, or -1 if table couldn't be grown. + * + * See notes on sa_table_insert(). + */ +static inline int +sa_table_append(struct sa_table *tp, void *ep) +{ + return sa_table_insert(tp, tp->st_limit, ep); +} + +/** sa_table_sort(tp, compare) - sort table in place + * + * @param tp pointer to sa_table structure. + * @param compare function to compare two entries. It is called with pointers + * to the pointers to the entries to be compared. See qsort(3). + */ +static inline void +sa_table_sort(struct sa_table *tp, int (*compare)(const void **, const void **)) +{ + qsort(tp->st_table, tp->st_limit, sizeof(void *), + (int (*)(const void *, const void *)) compare); +} + +#endif /* _FCOEMON_UTILS_H_ */ diff --git a/include/fip.h b/include/fip.h new file mode 100644 index 0000000..0b6385c --- /dev/null +++ b/include/fip.h @@ -0,0 +1,184 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef FIP_H +#define FIP_H + +#include +#include + +#define ETH_P_FCOE 0x8906 +#define ETH_P_FIP 0x8914 + +#define VLAN_HLEN 4 + +#define FIP_ALL_FCOE_MACS { 0x01, 0x10, 0x18, 0x01, 0x00, 0x00 } +#define FIP_ALL_ENODE_MACS { 0x01, 0x10, 0x18, 0x01, 0x00, 0x01 } +#define FIP_ALL_FCF_MACS { 0x01, 0x10, 0x18, 0x01, 0x00, 0x02 } +#define FIP_ALL_VN2VN_MACS { 0x01, 0x10, 0x18, 0x01, 0x00, 0x04 } + +/* The following values are chosen to match the last byte of + * the corresponding FIP multicast MAC address. + */ +enum fip_multi { + FIP_NONE = -1, + FIP_ALL_FCOE = 0, + FIP_ALL_ENODE = 1, + FIP_ALL_FCF = 2, + FIP_ALL_VN2VN = 4, +}; + +struct fiphdr { + uint8_t fip_version; /* version, upper 4 bits only */ + uint8_t __resvd_0; + uint16_t fip_proto; /* protocol code */ + uint8_t __resvd_1; + uint8_t fip_subcode; /* subcode */ + uint16_t fip_desc_len; /* descriptor list length */ + uint16_t fip_flags; +}; + +#define FIP_VERSION(n) (n << 4) +#define FIP_F_FP (1 << 15) /* FPMA supported/requested/granted */ +#define FIP_F_SP (1 << 14) /* SPMA supported/requested/granted */ +#define FIP_F_A (1 << 2) /* Available for Login */ +#define FIP_F_S (1 << 1) /* Solicited advertisement */ +#define FIP_F_F (1 << 0) /* FCF */ + +/* FCF Discovery Protocol */ +#define FIP_PROTO_DISC 1 +#define FIP_DISC_SOL 1 +#define FIP_DISC_ADV 2 + +/* Virtual Link Instantiation (encapsulated ELS) */ +#define FIP_PROTO_VLI 2 +#define FIP_VLI_REQ 1 +#define FIP_VLI_REPLY 2 + +/* FIP Keep Alive */ +#define FIP_PROTO_FKA 3 +#define FIP_FKA 1 +#define FIP_FKA_CLEAR 2 + +/* VLAN Discovery */ +#define FIP_PROTO_VLAN 4 +#define FIP_VLAN_REQ 1 +#define FIP_VLAN_NOTE 2 +#define FIP_VLAN_NOTE_VN2VN 3 + +struct fip_tlv_hdr { + uint8_t tlv_type; + uint8_t tlv_len; /* length in quad-words of entire TLV */ +}; + +#define FIP_TLV_PRIORITY 1 +#define FIP_TLV_MAC_ADDR 2 +#define FIP_TLV_FC_MAP 3 +#define FIP_TLV_NAME_IDENTIFIER 4 +#define FIP_TLV_FABRIC_NAME 5 +#define FIP_TLV_MAX_RECV_SIZE 6 +#define FIP_TLV_FLOGI 7 +#define FIP_TLV_FDISC 8 +#define FIP_TLV_LOGO 9 +#define FIP_TLV_ELP 10 + +#define FIP_TLV_VLAN 14 + +#define DEFAULT_FIP_PRIORITY 128 + +/* Priority Descriptor */ +struct fip_tlv_priority { + struct fip_tlv_hdr hdr; + unsigned char __resvd; + uint8_t priority; +}; + +/* MAC Address Descriptor */ +struct fip_tlv_mac_addr { + struct fip_tlv_hdr hdr; + unsigned char mac_addr[ETHER_ADDR_LEN]; +}; + +/* FC-MAP Descriptor */ +struct fip_tlv_fc_map { + struct fip_tlv_hdr hdr; + unsigned char __resvd[3]; + uint8_t map[3]; +}; + +/* Name Identifier Descriptor (also used for Fabric Name Descriptor) */ +struct fip_tlv_name_id { + struct fip_tlv_hdr hdr; + unsigned char __resvd[2]; + unsigned char wwn[8]; +}; + +/* Max Receive Size Descriptor */ +struct fip_tlv_max_recv_size { + struct fip_tlv_hdr hdr; + uint16_t mtu; +}; + +/* VLAN */ +struct fip_tlv_vlan { + struct fip_tlv_hdr hdr; + uint16_t vlan; /* only lower 12 bits matter */ +}; + + +/* libutil / fip.c functionality */ + +int fip_socket(int ifindex, unsigned char *mac, enum fip_multi multi); + +/* FIP message handler, passed into fip_recv */ +typedef int fip_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg); + +/** + * fip_recv - receive from a FIP packet socket + * @s: packet socket with data ready to be received + */ +int fip_recv(int s, fip_handler *fn, void *arg); + +/** + * fip_send_vlan_request - send a FIP VLAN request + * @s: ETH_P_FIP packet socket to send on + * @ifindex: network interface index to send on + * @mac: mac address of the netif + * @dest: destination selector + * + * Note: sends to destination selected by @dest + */ +ssize_t fip_send_vlan_request(int s, int ifindex, const unsigned char *mac, + enum fip_multi); + +/** + * fip_send_vlan_notification - send a FIP VLAN notification + * @s: ETH_P_FIP packet socket to send on + * @ifindex: network interface to send on + * @mac: mac address of the netif + * @dest: destination mac address + * @vtlvs: pointer to vlan tlvs to send + * @vlan_count: Number of vlan tlvs to send + */ +int +fip_send_vlan_notification(int s, int ifindex, const __u8 *mac, + const __u8 *dest, struct fip_tlv_vlan *, + int vlan_count); + +#endif /* FIP_H */ diff --git a/include/libopenfcoe.h b/include/libopenfcoe.h new file mode 100644 index 0000000..d9992be --- /dev/null +++ b/include/libopenfcoe.h @@ -0,0 +1,110 @@ +/* + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _LIBOPENFCOE_H_ +#define _LIBOPENFCOE_H_ + +#include +#include +#include + +#include "fcoemon_utils.h" +#include "fcoe_utils.h" + +#define MAC_ADDR_LEN 6 + +/* MAC_ADDR_STRLEN = strlen("00:11:22:33:44:55") */ +#define MAC_ADDR_STRLEN 17 + +enum fip_conn_type { + FIP_CONN_TYPE_UNKNOWN = 0, + FIP_CONN_TYPE_FABRIC, + FIP_CONN_TYPE_VN2VN, +}; + +static const struct sa_nameval fip_conn_type_table[] = { + { "Unknown", FIP_CONN_TYPE_UNKNOWN }, + { "Fabric", FIP_CONN_TYPE_FABRIC }, + { "VN2VN", FIP_CONN_TYPE_VN2VN }, + { NULL, 0 } +}; + +struct fcoe_ctlr_device { + /* Filesystem Information */ + int index; + char path[MAX_STR_LEN]; + char ifname[IFNAMSIZ]; + + /* Associations */ + struct sa_table fcfs; + + /* Attributes */ + u_int32_t fcf_dev_loss_tmo; + enum fip_conn_type mode; + u_int32_t lesb_link_fail; /* link failure count */ + u_int32_t lesb_vlink_fail; /* virtual link failure count */ + u_int32_t lesb_miss_fka; /* missing FIP keep-alive count */ + u_int32_t lesb_symb_err; /* symbol error during carrier count */ + u_int32_t lesb_err_block; /* errored block count */ + u_int32_t lesb_fcs_error; /* frame check sequence error count */ +}; + +/* fcf states */ +enum fcf_state { + FCOE_FCF_STATE_UNKNOWN = 0, + FCOE_FCF_STATE_DISCONNECTED, + FCOE_FCF_STATE_CONNECTED, + FCOE_FCF_STATE_DELETED, +}; + +static const struct sa_nameval fcf_state_table[] = { + { "Unknown", FCOE_FCF_STATE_UNKNOWN }, + { "Disconnected", FCOE_FCF_STATE_DISCONNECTED }, + { "Connected", FCOE_FCF_STATE_CONNECTED }, + { "Deleted", FCOE_FCF_STATE_DELETED }, + { NULL, 0 } +}; + +struct fcoe_fcf_device { + /* Filesystem Information */ + int index; + char path[MAX_STR_LEN]; + + /* Attributes */ + enum fcf_state state; + u_int32_t dev_loss_tmo; + u_int64_t fabric_name; + u_int64_t switch_name; + u_int32_t fc_map; + u_int32_t vfid; /* u16 in kernel */ + u_int8_t mac[MAC_ADDR_LEN]; + u_int32_t priority; /* u8 in kernel */ + u_int32_t fka_period; + u_int32_t selected; /* u8 in kernel */ + u_int32_t vlan_id; /* u16 in kernel */ +}; + +void read_fcoe_ctlr(struct sa_table *ctlrs); +void print_fcoe_ctlr_device(void *ep, void *arg); +void free_fcoe_ctlr_device(void *ep, void *arg); + +int mac2str(const u_int8_t *mac, char *dst, size_t size); +int str2mac(const char *src, u_int8_t *mac, size_t size); + +#endif /* _LIBOPENFCOE_H_ */ diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h new file mode 100644 index 0000000..c522800 --- /dev/null +++ b/include/linux/dcbnl.h @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2008-2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Author: Lucy Liu + */ + +#ifndef __LINUX_DCBNL_H__ +#define __LINUX_DCBNL_H__ + +#include + +/* IEEE 802.1Qaz std supported values */ +#define IEEE_8021QAZ_MAX_TCS 8 + +#define IEEE_8021QAZ_TSA_STRICT 0 +#define IEEE_8021QAZ_TSA_CB_SHAPER 1 +#define IEEE_8021QAZ_TSA_ETS 2 +#define IEEE_8021QAZ_TSA_VENDOR 255 + +/* This structure contains the IEEE 802.1Qaz ETS managed object + * + * @willing: willing bit in ETS configuration TLV + * @ets_cap: indicates supported capacity of ets feature + * @cbs: credit based shaper ets algorithm supported + * @tc_tx_bw: tc tx bandwidth indexed by traffic class + * @tc_rx_bw: tc rx bandwidth indexed by traffic class + * @tc_tsa: TSA Assignment table, indexed by traffic class + * @prio_tc: priority assignment table mapping 8021Qp to traffic class + * @tc_reco_bw: recommended tc bandwidth indexed by traffic class for TLV + * @tc_reco_tsa: recommended tc bandwidth indexed by traffic class for TLV + * @reco_prio_tc: recommended tc tx bandwidth indexed by traffic class for TLV + * + * Recommended values are used to set fields in the ETS recommendation TLV + * with hardware offloaded LLDP. + * + * ---- + * TSA Assignment 8 bit identifiers + * 0 strict priority + * 1 credit-based shaper + * 2 enhanced transmission selection + * 3-254 reserved + * 255 vendor specific + */ +struct ieee_ets { + __u8 willing; + __u8 ets_cap; + __u8 cbs; + __u8 tc_tx_bw[IEEE_8021QAZ_MAX_TCS]; + __u8 tc_rx_bw[IEEE_8021QAZ_MAX_TCS]; + __u8 tc_tsa[IEEE_8021QAZ_MAX_TCS]; + __u8 prio_tc[IEEE_8021QAZ_MAX_TCS]; + __u8 tc_reco_bw[IEEE_8021QAZ_MAX_TCS]; + __u8 tc_reco_tsa[IEEE_8021QAZ_MAX_TCS]; + __u8 reco_prio_tc[IEEE_8021QAZ_MAX_TCS]; +}; + +/* This structure contains the IEEE 802.1Qaz PFC managed object + * + * @pfc_cap: Indicates the number of traffic classes on the local device + * that may simultaneously have PFC enabled. + * @pfc_en: bitmap indicating pfc enabled traffic classes + * @mbc: enable macsec bypass capability + * @delay: the allowance made for a round-trip propagation delay of the + * link in bits. + * @requests: count of the sent pfc frames + * @indications: count of the received pfc frames + */ +struct ieee_pfc { + __u8 pfc_cap; + __u8 pfc_en; + __u8 mbc; + __u16 delay; + __u64 requests[IEEE_8021QAZ_MAX_TCS]; + __u64 indications[IEEE_8021QAZ_MAX_TCS]; +}; + +/* CEE DCBX std supported values */ +#define CEE_DCBX_MAX_PGS 8 +#define CEE_DCBX_MAX_PRIO 8 + +/** + * struct cee_pg - CEE Priority-Group managed object + * + * @willing: willing bit in the PG tlv + * @error: error bit in the PG tlv + * @pg_en: enable bit of the PG feature + * @tcs_supported: number of traffic classes supported + * @pg_bw: bandwidth percentage for each priority group + * @prio_pg: priority to PG mapping indexed by priority + */ +struct cee_pg { + __u8 willing; + __u8 error; + __u8 pg_en; + __u8 tcs_supported; + __u8 pg_bw[CEE_DCBX_MAX_PGS]; + __u8 prio_pg[CEE_DCBX_MAX_PGS]; +}; + +/** + * struct cee_pfc - CEE PFC managed object + * + * @willing: willing bit in the PFC tlv + * @error: error bit in the PFC tlv + * @pfc_en: bitmap indicating pfc enabled traffic classes + * @tcs_supported: number of traffic classes supported + */ +struct cee_pfc { + __u8 willing; + __u8 error; + __u8 pfc_en; + __u8 tcs_supported; +}; + +/* IEEE 802.1Qaz std supported values */ +#define IEEE_8021QAZ_APP_SEL_ETHERTYPE 1 +#define IEEE_8021QAZ_APP_SEL_STREAM 2 +#define IEEE_8021QAZ_APP_SEL_DGRAM 3 +#define IEEE_8021QAZ_APP_SEL_ANY 4 + +/* This structure contains the IEEE 802.1Qaz APP managed object. This + * object is also used for the CEE std as well. There is no difference + * between the objects. + * + * @selector: protocol identifier type + * @protocol: protocol of type indicated + * @priority: 3-bit unsigned integer indicating priority + * + * ---- + * Selector field values + * 0 Reserved + * 1 Ethertype + * 2 Well known port number over TCP or SCTP + * 3 Well known port number over UDP or DCCP + * 4 Well known port number over TCP, SCTP, UDP, or DCCP + * 5-7 Reserved + */ +struct dcb_app { + __u8 selector; + __u8 priority; + __u16 protocol; +}; + +/** + * struct dcb_peer_app_info - APP feature information sent by the peer + * + * @willing: willing bit in the peer APP tlv + * @error: error bit in the peer APP tlv + * + * In addition to this information the full peer APP tlv also contains + * a table of 'app_count' APP objects defined above. + */ +struct dcb_peer_app_info { + __u8 willing; + __u8 error; +}; + +struct dcbmsg { + __u8 dcb_family; + __u8 cmd; + __u16 dcb_pad; +}; + +/** + * enum dcbnl_commands - supported DCB commands + * + * @DCB_CMD_UNDEFINED: unspecified command to catch errors + * @DCB_CMD_GSTATE: request the state of DCB in the device + * @DCB_CMD_SSTATE: set the state of DCB in the device + * @DCB_CMD_PGTX_GCFG: request the priority group configuration for Tx + * @DCB_CMD_PGTX_SCFG: set the priority group configuration for Tx + * @DCB_CMD_PGRX_GCFG: request the priority group configuration for Rx + * @DCB_CMD_PGRX_SCFG: set the priority group configuration for Rx + * @DCB_CMD_PFC_GCFG: request the priority flow control configuration + * @DCB_CMD_PFC_SCFG: set the priority flow control configuration + * @DCB_CMD_SET_ALL: apply all changes to the underlying device + * @DCB_CMD_GPERM_HWADDR: get the permanent MAC address of the underlying + * device. Only useful when using bonding. + * @DCB_CMD_GCAP: request the DCB capabilities of the device + * @DCB_CMD_GNUMTCS: get the number of traffic classes currently supported + * @DCB_CMD_SNUMTCS: set the number of traffic classes + * @DCB_CMD_GBCN: set backward congestion notification configuration + * @DCB_CMD_SBCN: get backward congestion notification configration. + * @DCB_CMD_GAPP: get application protocol configuration + * @DCB_CMD_SAPP: set application protocol configuration + * @DCB_CMD_IEEE_SET: set IEEE 802.1Qaz configuration + * @DCB_CMD_IEEE_GET: get IEEE 802.1Qaz configuration + * @DCB_CMD_GDCBX: get DCBX engine configuration + * @DCB_CMD_SDCBX: set DCBX engine configuration + * @DCB_CMD_GFEATCFG: get DCBX features flags + * @DCB_CMD_SFEATCFG: set DCBX features negotiation flags + * @DCB_CMD_CEE_GET: get CEE aggregated configuration + */ +enum dcbnl_commands { + DCB_CMD_UNDEFINED, + + DCB_CMD_GSTATE, + DCB_CMD_SSTATE, + + DCB_CMD_PGTX_GCFG, + DCB_CMD_PGTX_SCFG, + DCB_CMD_PGRX_GCFG, + DCB_CMD_PGRX_SCFG, + + DCB_CMD_PFC_GCFG, + DCB_CMD_PFC_SCFG, + + DCB_CMD_SET_ALL, + + DCB_CMD_GPERM_HWADDR, + + DCB_CMD_GCAP, + + DCB_CMD_GNUMTCS, + DCB_CMD_SNUMTCS, + + DCB_CMD_PFC_GSTATE, + DCB_CMD_PFC_SSTATE, + + DCB_CMD_BCN_GCFG, + DCB_CMD_BCN_SCFG, + + DCB_CMD_GAPP, + DCB_CMD_SAPP, + + DCB_CMD_IEEE_SET, + DCB_CMD_IEEE_GET, + + DCB_CMD_GDCBX, + DCB_CMD_SDCBX, + + DCB_CMD_GFEATCFG, + DCB_CMD_SFEATCFG, + + DCB_CMD_CEE_GET, + + __DCB_CMD_ENUM_MAX, + DCB_CMD_MAX = __DCB_CMD_ENUM_MAX - 1, +}; + +/** + * enum dcbnl_attrs - DCB top-level netlink attributes + * + * @DCB_ATTR_UNDEFINED: unspecified attribute to catch errors + * @DCB_ATTR_IFNAME: interface name of the underlying device (NLA_STRING) + * @DCB_ATTR_STATE: enable state of DCB in the device (NLA_U8) + * @DCB_ATTR_PFC_STATE: enable state of PFC in the device (NLA_U8) + * @DCB_ATTR_PFC_CFG: priority flow control configuration (NLA_NESTED) + * @DCB_ATTR_NUM_TC: number of traffic classes supported in the device (NLA_U8) + * @DCB_ATTR_PG_CFG: priority group configuration (NLA_NESTED) + * @DCB_ATTR_SET_ALL: bool to commit changes to hardware or not (NLA_U8) + * @DCB_ATTR_PERM_HWADDR: MAC address of the physical device (NLA_NESTED) + * @DCB_ATTR_CAP: DCB capabilities of the device (NLA_NESTED) + * @DCB_ATTR_NUMTCS: number of traffic classes supported (NLA_NESTED) + * @DCB_ATTR_BCN: backward congestion notification configuration (NLA_NESTED) + * @DCB_ATTR_IEEE: IEEE 802.1Qaz supported attributes (NLA_NESTED) + * @DCB_ATTR_DCBX: DCBX engine configuration in the device (NLA_U8) + * @DCB_ATTR_FEATCFG: DCBX features flags (NLA_NESTED) + * @DCB_ATTR_CEE: CEE std supported attributes (NLA_NESTED) + */ +enum dcbnl_attrs { + DCB_ATTR_UNDEFINED, + + DCB_ATTR_IFNAME, + DCB_ATTR_STATE, + DCB_ATTR_PFC_STATE, + DCB_ATTR_PFC_CFG, + DCB_ATTR_NUM_TC, + DCB_ATTR_PG_CFG, + DCB_ATTR_SET_ALL, + DCB_ATTR_PERM_HWADDR, + DCB_ATTR_CAP, + DCB_ATTR_NUMTCS, + DCB_ATTR_BCN, + DCB_ATTR_APP, + + /* IEEE std attributes */ + DCB_ATTR_IEEE, + + DCB_ATTR_DCBX, + DCB_ATTR_FEATCFG, + + /* CEE nested attributes */ + DCB_ATTR_CEE, + + __DCB_ATTR_ENUM_MAX, + DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1, +}; + +/** + * enum ieee_attrs - IEEE 802.1Qaz get/set attributes + * + * @DCB_ATTR_IEEE_UNSPEC: unspecified + * @DCB_ATTR_IEEE_ETS: negotiated ETS configuration + * @DCB_ATTR_IEEE_PFC: negotiated PFC configuration + * @DCB_ATTR_IEEE_APP_TABLE: negotiated APP configuration + * @DCB_ATTR_IEEE_PEER_ETS: peer ETS configuration - get only + * @DCB_ATTR_IEEE_PEER_PFC: peer PFC configuration - get only + * @DCB_ATTR_IEEE_PEER_APP: peer APP tlv - get only + */ +enum ieee_attrs { + DCB_ATTR_IEEE_UNSPEC, + DCB_ATTR_IEEE_ETS, + DCB_ATTR_IEEE_PFC, + DCB_ATTR_IEEE_APP_TABLE, + DCB_ATTR_IEEE_PEER_ETS, + DCB_ATTR_IEEE_PEER_PFC, + DCB_ATTR_IEEE_PEER_APP, + __DCB_ATTR_IEEE_MAX +}; +#define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) + +enum ieee_attrs_app { + DCB_ATTR_IEEE_APP_UNSPEC, + DCB_ATTR_IEEE_APP, + __DCB_ATTR_IEEE_APP_MAX +}; +#define DCB_ATTR_IEEE_APP_MAX (__DCB_ATTR_IEEE_APP_MAX - 1) + +/** + * enum cee_attrs - CEE DCBX get attributes + * + * @DCB_ATTR_CEE_UNSPEC: unspecified + * @DCB_ATTR_CEE_PEER_PG: peer PG configuration - get only + * @DCB_ATTR_CEE_PEER_PFC: peer PFC configuration - get only + * @DCB_ATTR_CEE_PEER_APP: peer APP tlv - get only + */ +enum cee_attrs { + DCB_ATTR_CEE_UNSPEC, + DCB_ATTR_CEE_PEER_PG, + DCB_ATTR_CEE_PEER_PFC, + DCB_ATTR_CEE_PEER_APP_TABLE, + __DCB_ATTR_CEE_MAX +}; +#define DCB_ATTR_CEE_MAX (__DCB_ATTR_CEE_MAX - 1) + +enum peer_app_attr { + DCB_ATTR_CEE_PEER_APP_UNSPEC, + DCB_ATTR_CEE_PEER_APP_INFO, + DCB_ATTR_CEE_PEER_APP, + __DCB_ATTR_CEE_PEER_APP_MAX +}; +#define DCB_ATTR_CEE_PEER_APP_MAX (__DCB_ATTR_CEE_PEER_APP_MAX - 1) + +/** + * enum dcbnl_pfc_attrs - DCB Priority Flow Control user priority nested attrs + * + * @DCB_PFC_UP_ATTR_UNDEFINED: unspecified attribute to catch errors + * @DCB_PFC_UP_ATTR_0: Priority Flow Control value for User Priority 0 (NLA_U8) + * @DCB_PFC_UP_ATTR_1: Priority Flow Control value for User Priority 1 (NLA_U8) + * @DCB_PFC_UP_ATTR_2: Priority Flow Control value for User Priority 2 (NLA_U8) + * @DCB_PFC_UP_ATTR_3: Priority Flow Control value for User Priority 3 (NLA_U8) + * @DCB_PFC_UP_ATTR_4: Priority Flow Control value for User Priority 4 (NLA_U8) + * @DCB_PFC_UP_ATTR_5: Priority Flow Control value for User Priority 5 (NLA_U8) + * @DCB_PFC_UP_ATTR_6: Priority Flow Control value for User Priority 6 (NLA_U8) + * @DCB_PFC_UP_ATTR_7: Priority Flow Control value for User Priority 7 (NLA_U8) + * @DCB_PFC_UP_ATTR_MAX: highest attribute number currently defined + * @DCB_PFC_UP_ATTR_ALL: apply to all priority flow control attrs (NLA_FLAG) + * + */ +enum dcbnl_pfc_up_attrs { + DCB_PFC_UP_ATTR_UNDEFINED, + + DCB_PFC_UP_ATTR_0, + DCB_PFC_UP_ATTR_1, + DCB_PFC_UP_ATTR_2, + DCB_PFC_UP_ATTR_3, + DCB_PFC_UP_ATTR_4, + DCB_PFC_UP_ATTR_5, + DCB_PFC_UP_ATTR_6, + DCB_PFC_UP_ATTR_7, + DCB_PFC_UP_ATTR_ALL, + + __DCB_PFC_UP_ATTR_ENUM_MAX, + DCB_PFC_UP_ATTR_MAX = __DCB_PFC_UP_ATTR_ENUM_MAX - 1, +}; + +/** + * enum dcbnl_pg_attrs - DCB Priority Group attributes + * + * @DCB_PG_ATTR_UNDEFINED: unspecified attribute to catch errors + * @DCB_PG_ATTR_TC_0: Priority Group Traffic Class 0 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_1: Priority Group Traffic Class 1 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_2: Priority Group Traffic Class 2 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_3: Priority Group Traffic Class 3 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_4: Priority Group Traffic Class 4 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_5: Priority Group Traffic Class 5 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_6: Priority Group Traffic Class 6 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_7: Priority Group Traffic Class 7 configuration (NLA_NESTED) + * @DCB_PG_ATTR_TC_MAX: highest attribute number currently defined + * @DCB_PG_ATTR_TC_ALL: apply to all traffic classes (NLA_NESTED) + * @DCB_PG_ATTR_BW_ID_0: Percent of link bandwidth for Priority Group 0 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_1: Percent of link bandwidth for Priority Group 1 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_2: Percent of link bandwidth for Priority Group 2 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_3: Percent of link bandwidth for Priority Group 3 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_4: Percent of link bandwidth for Priority Group 4 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_5: Percent of link bandwidth for Priority Group 5 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_6: Percent of link bandwidth for Priority Group 6 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_7: Percent of link bandwidth for Priority Group 7 (NLA_U8) + * @DCB_PG_ATTR_BW_ID_MAX: highest attribute number currently defined + * @DCB_PG_ATTR_BW_ID_ALL: apply to all priority groups (NLA_FLAG) + * + */ +enum dcbnl_pg_attrs { + DCB_PG_ATTR_UNDEFINED, + + DCB_PG_ATTR_TC_0, + DCB_PG_ATTR_TC_1, + DCB_PG_ATTR_TC_2, + DCB_PG_ATTR_TC_3, + DCB_PG_ATTR_TC_4, + DCB_PG_ATTR_TC_5, + DCB_PG_ATTR_TC_6, + DCB_PG_ATTR_TC_7, + DCB_PG_ATTR_TC_MAX, + DCB_PG_ATTR_TC_ALL, + + DCB_PG_ATTR_BW_ID_0, + DCB_PG_ATTR_BW_ID_1, + DCB_PG_ATTR_BW_ID_2, + DCB_PG_ATTR_BW_ID_3, + DCB_PG_ATTR_BW_ID_4, + DCB_PG_ATTR_BW_ID_5, + DCB_PG_ATTR_BW_ID_6, + DCB_PG_ATTR_BW_ID_7, + DCB_PG_ATTR_BW_ID_MAX, + DCB_PG_ATTR_BW_ID_ALL, + + __DCB_PG_ATTR_ENUM_MAX, + DCB_PG_ATTR_MAX = __DCB_PG_ATTR_ENUM_MAX - 1, +}; + +/** + * enum dcbnl_tc_attrs - DCB Traffic Class attributes + * + * @DCB_TC_ATTR_PARAM_UNDEFINED: unspecified attribute to catch errors + * @DCB_TC_ATTR_PARAM_PGID: (NLA_U8) Priority group the traffic class belongs to + * Valid values are: 0-7 + * @DCB_TC_ATTR_PARAM_UP_MAPPING: (NLA_U8) Traffic class to user priority map + * Some devices may not support changing the + * user priority map of a TC. + * @DCB_TC_ATTR_PARAM_STRICT_PRIO: (NLA_U8) Strict priority setting + * 0 - none + * 1 - group strict + * 2 - link strict + * @DCB_TC_ATTR_PARAM_BW_PCT: optional - (NLA_U8) If supported by the device and + * not configured to use link strict priority, + * this is the percentage of bandwidth of the + * priority group this traffic class belongs to + * @DCB_TC_ATTR_PARAM_ALL: (NLA_FLAG) all traffic class parameters + * + */ +enum dcbnl_tc_attrs { + DCB_TC_ATTR_PARAM_UNDEFINED, + + DCB_TC_ATTR_PARAM_PGID, + DCB_TC_ATTR_PARAM_UP_MAPPING, + DCB_TC_ATTR_PARAM_STRICT_PRIO, + DCB_TC_ATTR_PARAM_BW_PCT, + DCB_TC_ATTR_PARAM_ALL, + + __DCB_TC_ATTR_PARAM_ENUM_MAX, + DCB_TC_ATTR_PARAM_MAX = __DCB_TC_ATTR_PARAM_ENUM_MAX - 1, +}; + +/** + * enum dcbnl_cap_attrs - DCB Capability attributes + * + * @DCB_CAP_ATTR_UNDEFINED: unspecified attribute to catch errors + * @DCB_CAP_ATTR_ALL: (NLA_FLAG) all capability parameters + * @DCB_CAP_ATTR_PG: (NLA_U8) device supports Priority Groups + * @DCB_CAP_ATTR_PFC: (NLA_U8) device supports Priority Flow Control + * @DCB_CAP_ATTR_UP2TC: (NLA_U8) device supports user priority to + * traffic class mapping + * @DCB_CAP_ATTR_PG_TCS: (NLA_U8) bitmap where each bit represents a + * number of traffic classes the device + * can be configured to use for Priority Groups + * @DCB_CAP_ATTR_PFC_TCS: (NLA_U8) bitmap where each bit represents a + * number of traffic classes the device can be + * configured to use for Priority Flow Control + * @DCB_CAP_ATTR_GSP: (NLA_U8) device supports group strict priority + * @DCB_CAP_ATTR_BCN: (NLA_U8) device supports Backwards Congestion + * Notification + * @DCB_CAP_ATTR_DCBX: (NLA_U8) device supports DCBX engine + * + */ +enum dcbnl_cap_attrs { + DCB_CAP_ATTR_UNDEFINED, + DCB_CAP_ATTR_ALL, + DCB_CAP_ATTR_PG, + DCB_CAP_ATTR_PFC, + DCB_CAP_ATTR_UP2TC, + DCB_CAP_ATTR_PG_TCS, + DCB_CAP_ATTR_PFC_TCS, + DCB_CAP_ATTR_GSP, + DCB_CAP_ATTR_BCN, + DCB_CAP_ATTR_DCBX, + + __DCB_CAP_ATTR_ENUM_MAX, + DCB_CAP_ATTR_MAX = __DCB_CAP_ATTR_ENUM_MAX - 1, +}; + +/** + * DCBX capability flags + * + * @DCB_CAP_DCBX_HOST: DCBX negotiation is performed by the host LLDP agent. + * 'set' routines are used to configure the device with + * the negotiated parameters + * + * @DCB_CAP_DCBX_LLD_MANAGED: DCBX negotiation is not performed in the host but + * by another entity + * 'get' routines are used to retrieve the + * negotiated parameters + * 'set' routines can be used to set the initial + * negotiation configuration + * + * @DCB_CAP_DCBX_VER_CEE: for a non-host DCBX engine, indicates the engine + * supports the CEE protocol flavor + * + * @DCB_CAP_DCBX_VER_IEEE: for a non-host DCBX engine, indicates the engine + * supports the IEEE protocol flavor + * + * @DCB_CAP_DCBX_STATIC: for a non-host DCBX engine, indicates the engine + * supports static configuration (i.e no actual + * negotiation is performed negotiated parameters equal + * the initial configuration) + * + */ +#define DCB_CAP_DCBX_HOST 0x01 +#define DCB_CAP_DCBX_LLD_MANAGED 0x02 +#define DCB_CAP_DCBX_VER_CEE 0x04 +#define DCB_CAP_DCBX_VER_IEEE 0x08 +#define DCB_CAP_DCBX_STATIC 0x10 + +/** + * enum dcbnl_numtcs_attrs - number of traffic classes + * + * @DCB_NUMTCS_ATTR_UNDEFINED: unspecified attribute to catch errors + * @DCB_NUMTCS_ATTR_ALL: (NLA_FLAG) all traffic class attributes + * @DCB_NUMTCS_ATTR_PG: (NLA_U8) number of traffic classes used for + * priority groups + * @DCB_NUMTCS_ATTR_PFC: (NLA_U8) number of traffic classes which can + * support priority flow control + */ +enum dcbnl_numtcs_attrs { + DCB_NUMTCS_ATTR_UNDEFINED, + DCB_NUMTCS_ATTR_ALL, + DCB_NUMTCS_ATTR_PG, + DCB_NUMTCS_ATTR_PFC, + + __DCB_NUMTCS_ATTR_ENUM_MAX, + DCB_NUMTCS_ATTR_MAX = __DCB_NUMTCS_ATTR_ENUM_MAX - 1, +}; + +enum dcbnl_bcn_attrs{ + DCB_BCN_ATTR_UNDEFINED = 0, + + DCB_BCN_ATTR_RP_0, + DCB_BCN_ATTR_RP_1, + DCB_BCN_ATTR_RP_2, + DCB_BCN_ATTR_RP_3, + DCB_BCN_ATTR_RP_4, + DCB_BCN_ATTR_RP_5, + DCB_BCN_ATTR_RP_6, + DCB_BCN_ATTR_RP_7, + DCB_BCN_ATTR_RP_ALL, + + DCB_BCN_ATTR_BCNA_0, + DCB_BCN_ATTR_BCNA_1, + DCB_BCN_ATTR_ALPHA, + DCB_BCN_ATTR_BETA, + DCB_BCN_ATTR_GD, + DCB_BCN_ATTR_GI, + DCB_BCN_ATTR_TMAX, + DCB_BCN_ATTR_TD, + DCB_BCN_ATTR_RMIN, + DCB_BCN_ATTR_W, + DCB_BCN_ATTR_RD, + DCB_BCN_ATTR_RU, + DCB_BCN_ATTR_WRTT, + DCB_BCN_ATTR_RI, + DCB_BCN_ATTR_C, + DCB_BCN_ATTR_ALL, + + __DCB_BCN_ATTR_ENUM_MAX, + DCB_BCN_ATTR_MAX = __DCB_BCN_ATTR_ENUM_MAX - 1, +}; + +/** + * enum dcb_general_attr_values - general DCB attribute values + * + * @DCB_ATTR_UNDEFINED: value used to indicate an attribute is not supported + * + */ +enum dcb_general_attr_values { + DCB_ATTR_VALUE_UNDEFINED = 0xff +}; + +#define DCB_APP_IDTYPE_ETHTYPE 0x00 +#define DCB_APP_IDTYPE_PORTNUM 0x01 +enum dcbnl_app_attrs { + DCB_APP_ATTR_UNDEFINED, + + DCB_APP_ATTR_IDTYPE, + DCB_APP_ATTR_ID, + DCB_APP_ATTR_PRIORITY, + + __DCB_APP_ATTR_ENUM_MAX, + DCB_APP_ATTR_MAX = __DCB_APP_ATTR_ENUM_MAX - 1, +}; + +/** + * enum dcbnl_featcfg_attrs - features conifiguration flags + * + * @DCB_FEATCFG_ATTR_UNDEFINED: unspecified attribute to catch errors + * @DCB_FEATCFG_ATTR_ALL: (NLA_FLAG) all features configuration attributes + * @DCB_FEATCFG_ATTR_PG: (NLA_U8) configuration flags for priority groups + * @DCB_FEATCFG_ATTR_PFC: (NLA_U8) configuration flags for priority + * flow control + * @DCB_FEATCFG_ATTR_APP: (NLA_U8) configuration flags for application TLV + * + */ +#define DCB_FEATCFG_ERROR 0x01 /* error in feature resolution */ +#define DCB_FEATCFG_ENABLE 0x02 /* enable feature */ +#define DCB_FEATCFG_WILLING 0x04 /* feature is willing */ +#define DCB_FEATCFG_ADVERTISE 0x08 /* advertise feature */ +enum dcbnl_featcfg_attrs { + DCB_FEATCFG_ATTR_UNDEFINED, + DCB_FEATCFG_ATTR_ALL, + DCB_FEATCFG_ATTR_PG, + DCB_FEATCFG_ATTR_PFC, + DCB_FEATCFG_ATTR_APP, + + __DCB_FEATCFG_ATTR_ENUM_MAX, + DCB_FEATCFG_ATTR_MAX = __DCB_FEATCFG_ATTR_ENUM_MAX - 1, +}; + +#endif /* __LINUX_DCBNL_H__ */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h new file mode 100644 index 0000000..b5b8c6d --- /dev/null +++ b/include/linux/rtnetlink.h @@ -0,0 +1,805 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include +#include +#include +#include +#include + +/* rtnetlink families. Values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define RTNL_FAMILY_IPMR 128 +#define RTNL_FAMILY_IP6MR 129 +#define RTNL_FAMILY_MAX 129 + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + RTM_NEWNDUSEROPT = 68, +#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT + + RTM_NEWADDRLABEL = 72, +#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL + RTM_DELADDRLABEL, +#define RTM_DELADDRLABEL RTM_DELADDRLABEL + RTM_GETADDRLABEL, +#define RTM_GETADDRLABEL RTM_GETADDRLABEL + + RTM_GETDCB = 78, +#define RTM_GETDCB RTM_GETDCB + RTM_SETDCB, +#define RTM_SETDCB RTM_SETDCB + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr { + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg { + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum { + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t { + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ + +/* Reserved table identifiers */ + +enum rt_class_t { + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_COMPAT=252, + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + RT_TABLE_MAX=0xFFFFFFFF +}; + + +/* Routing message attributes */ + +enum rtattr_type_t { + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, /* no longer used */ + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, /* no longer used */ + RTA_MP_ALGO, /* no longer used */ + RTA_TABLE, + RTA_MARK, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop { + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo { + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum { + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + RTAX_RTO_MIN, +#define RTAX_RTO_MIN RTAX_RTO_MIN + RTAX_INITRWND, +#define RTAX_INITRWND RTAX_INITRWND + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN 0x00000001 +#define RTAX_FEATURE_SACK 0x00000002 +#define RTAX_FEATURE_TIMESTAMP 0x00000004 +#define RTAX_FEATURE_ALLFRAG 0x00000008 + +struct rta_session { + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg { + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg { + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg { + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo { + __u32 preferred_time; + __u32 valid_time; +}; + + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg { + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; + __u32 tcm_info; +}; + +enum { + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_STAB, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +/******************************************************************** + * Neighbor Discovery userland options + ****/ + +struct nduseroptmsg { + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; /* Total length of options */ + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + /* Followed by one or more ND options */ +}; + +enum { + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + +#ifndef __KERNEL__ +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 +#endif + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT, +#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE + RTNLGRP_DCB, +#define RTNLGRP_DCB RTNLGRP_DCB + __RTNLGRP_MAX +}; +#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) + +/* TC action piece */ +struct tcamsg { + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +#define TCA_ACT_TAB 1 /* attr type must be >=1 */ +#define TCAA_MAX 1 + +/* End of information exported to user level */ + +#ifdef __KERNEL__ + +#include +#include + +static __inline__ int rtattr_strcmp(const struct rtattr *rta, const char *str) +{ + int len = strlen(str) + 1; + return len > rta->rta_len || memcmp(RTA_DATA(rta), str, len); +} + +extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); +extern int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid); +extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, + u32 group, struct nlmsghdr *nlh, gfp_t flags); +extern void rtnl_set_sk_err(struct net *net, u32 group, int error); +extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); +extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, + u32 id, u32 ts, u32 tsage, long expires, + u32 error); + +extern void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data); + +#define RTA_PUT(skb, attrtype, attrlen, data) \ +({ if (unlikely(skb_tailroom(skb) < (int)RTA_SPACE(attrlen))) \ + goto rtattr_failure; \ + __rta_fill(skb, attrtype, attrlen, data); }) + +#define RTA_APPEND(skb, attrlen, data) \ +({ if (unlikely(skb_tailroom(skb) < (int)(attrlen))) \ + goto rtattr_failure; \ + memcpy(skb_put(skb, attrlen), data, attrlen); }) + +#define RTA_PUT_NOHDR(skb, attrlen, data) \ +({ RTA_APPEND(skb, RTA_ALIGN(attrlen), data); \ + memset(skb_tail_pointer(skb) - (RTA_ALIGN(attrlen) - attrlen), 0, \ + RTA_ALIGN(attrlen) - attrlen); }) + +#define RTA_PUT_U8(skb, attrtype, value) \ +({ u8 _tmp = (value); \ + RTA_PUT(skb, attrtype, sizeof(u8), &_tmp); }) + +#define RTA_PUT_U16(skb, attrtype, value) \ +({ u16 _tmp = (value); \ + RTA_PUT(skb, attrtype, sizeof(u16), &_tmp); }) + +#define RTA_PUT_U32(skb, attrtype, value) \ +({ u32 _tmp = (value); \ + RTA_PUT(skb, attrtype, sizeof(u32), &_tmp); }) + +#define RTA_PUT_U64(skb, attrtype, value) \ +({ u64 _tmp = (value); \ + RTA_PUT(skb, attrtype, sizeof(u64), &_tmp); }) + +#define RTA_PUT_SECS(skb, attrtype, value) \ + RTA_PUT_U64(skb, attrtype, (value) / HZ) + +#define RTA_PUT_MSECS(skb, attrtype, value) \ + RTA_PUT_U64(skb, attrtype, jiffies_to_msecs(value)) + +#define RTA_PUT_STRING(skb, attrtype, value) \ + RTA_PUT(skb, attrtype, strlen(value) + 1, value) + +#define RTA_PUT_FLAG(skb, attrtype) \ + RTA_PUT(skb, attrtype, 0, NULL); + +#define RTA_NEST(skb, type) \ +({ struct rtattr *__start = (struct rtattr *)skb_tail_pointer(skb); \ + RTA_PUT(skb, type, 0, NULL); \ + __start; }) + +#define RTA_NEST_END(skb, start) \ +({ (start)->rta_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ + (skb)->len; }) + +#define RTA_NEST_COMPAT(skb, type, attrlen, data) \ +({ struct rtattr *__start = (struct rtattr *)skb_tail_pointer(skb); \ + RTA_PUT(skb, type, attrlen, data); \ + RTA_NEST(skb, type); \ + __start; }) + +#define RTA_NEST_COMPAT_END(skb, start) \ +({ struct rtattr *__nest = (void *)(start) + NLMSG_ALIGN((start)->rta_len); \ + (start)->rta_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ + RTA_NEST_END(skb, __nest); \ + (skb)->len; }) + +#define RTA_NEST_CANCEL(skb, start) \ +({ if (start) \ + skb_trim(skb, (unsigned char *) (start) - (skb)->data); \ + -1; }) + +#define RTA_GET_U8(rta) \ +({ if (!rta || RTA_PAYLOAD(rta) < sizeof(u8)) \ + goto rtattr_failure; \ + *(u8 *) RTA_DATA(rta); }) + +#define RTA_GET_U16(rta) \ +({ if (!rta || RTA_PAYLOAD(rta) < sizeof(u16)) \ + goto rtattr_failure; \ + *(u16 *) RTA_DATA(rta); }) + +#define RTA_GET_U32(rta) \ +({ if (!rta || RTA_PAYLOAD(rta) < sizeof(u32)) \ + goto rtattr_failure; \ + *(u32 *) RTA_DATA(rta); }) + +#define RTA_GET_U64(rta) \ +({ u64 _tmp; \ + if (!rta || RTA_PAYLOAD(rta) < sizeof(u64)) \ + goto rtattr_failure; \ + memcpy(&_tmp, RTA_DATA(rta), sizeof(_tmp)); \ + _tmp; }) + +#define RTA_GET_FLAG(rta) (!!(rta)) + +#define RTA_GET_SECS(rta) ((unsigned long) RTA_GET_U64(rta) * HZ) +#define RTA_GET_MSECS(rta) (msecs_to_jiffies((unsigned long) RTA_GET_U64(rta))) + +static inline struct rtattr * +__rta_reserve(struct sk_buff *skb, int attrtype, int attrlen) +{ + struct rtattr *rta; + int size = RTA_LENGTH(attrlen); + + rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size)); + rta->rta_type = attrtype; + rta->rta_len = size; + memset(RTA_DATA(rta) + attrlen, 0, RTA_ALIGN(size) - size); + return rta; +} + +#define __RTA_PUT(skb, attrtype, attrlen) \ +({ if (unlikely(skb_tailroom(skb) < (int)RTA_SPACE(attrlen))) \ + goto rtattr_failure; \ + __rta_reserve(skb, attrtype, attrlen); }) + +extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); + +/* RTNL is used as a global lock for all changes to network configuration */ +extern void rtnl_lock(void); +extern void rtnl_unlock(void); +extern int rtnl_trylock(void); +extern int rtnl_is_locked(void); +#ifdef CONFIG_PROVE_LOCKING +extern int lockdep_rtnl_is_held(void); +#endif /* #ifdef CONFIG_PROVE_LOCKING */ + +/** + * rcu_dereference_rtnl - rcu_dereference with debug checking + * @p: The pointer to read, prior to dereferencing + * + * Do an rcu_dereference(p), but check caller either holds rcu_read_lock() + * or RTNL. Note : Please prefer rtnl_dereference() or rcu_dereference() + */ +#define rcu_dereference_rtnl(p) \ + rcu_dereference_check(p, rcu_read_lock_held() || \ + lockdep_rtnl_is_held()) + +/** + * rtnl_dereference - fetch RCU pointer when updates are prevented by RTNL + * @p: The pointer to read, prior to dereferencing + * + * Return the value of the specified RCU-protected pointer, but omit + * both the smp_read_barrier_depends() and the ACCESS_ONCE(), because + * caller holds RTNL. + */ +#define rtnl_dereference(p) \ + rcu_dereference_protected(p, lockdep_rtnl_is_held()) + +static inline struct netdev_queue *dev_ingress_queue(struct net_device *dev) +{ + return rtnl_dereference(dev->ingress_queue); +} + +extern struct netdev_queue *dev_ingress_queue_create(struct net_device *dev); + +extern void rtnetlink_init(void); +extern void __rtnl_unlock(void); + +#define ASSERT_RTNL() do { \ + if (unlikely(!rtnl_is_locked())) { \ + printk(KERN_ERR "RTNL: assertion failed at %s (%d)\n", \ + __FILE__, __LINE__); \ + dump_stack(); \ + } \ +} while(0) + +static inline u32 rtm_get_table(struct rtattr **rta, u8 table) +{ + return RTA_GET_U32(rta[RTA_TABLE-1]); +rtattr_failure: + return table; +} + +#endif /* __KERNEL__ */ + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/include/linux/types.h b/include/linux/types.h new file mode 100644 index 0000000..23ea78f --- /dev/null +++ b/include/linux/types.h @@ -0,0 +1,51 @@ +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H + +#include + +#ifndef __ASSEMBLY__ + +#include + + +/* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. + */ + +#ifdef __CHECKER__ +#define __bitwise__ __attribute__((bitwise)) +#else +#define __bitwise__ +#endif +#ifdef __CHECK_ENDIAN__ +#define __bitwise __bitwise__ +#else +#define __bitwise +#endif + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +typedef __u16 __bitwise __sum16; +typedef __u32 __bitwise __wsum; + +/* + * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid + * common 32/64-bit compat problems. + * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other + * architectures) and to 8-byte boundaries on 64-bit architectures. The new + * aligned_64 type enforces 8-byte alignment so that structs containing + * aligned_64 values have the same alignment on 32-bit and 64-bit architectures. + * No conversions are necessary between 32-bit user-space and a 64-bit kernel. + */ +#define __aligned_u64 __u64 __attribute__((aligned(8))) +#define __aligned_be64 __be64 __attribute__((aligned(8))) +#define __aligned_le64 __le64 __attribute__((aligned(8))) + +#endif /* __ASSEMBLY__ */ +#endif /* _LINUX_TYPES_H */ diff --git a/include/net_types.h b/include/net_types.h new file mode 100644 index 0000000..c2ba409 --- /dev/null +++ b/include/net_types.h @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LIBSA_NET_TYPES_H_ +#define _LIBSA_NET_TYPES_H_ + +#if !defined(NTOHL) +#include +#endif /* NTOHL */ + +/* + * Type definitions for network order fields in protocol packets. + * The access functions below do gets and puts on these structures. + */ +typedef unsigned char net8_t; /* direct use and assignment allowed */ + +/* + * Aligned network order types. + */ +typedef struct { + u_int16_t net_data; +} net16_t; + +typedef struct { + u_int32_t net_data; +} net32_t; + +/* + * The 64-bit type only requires 32-bit alignment. + */ +typedef struct { + u_int32_t net_data[2]; /* most significant word first */ +} net64_t; + +/* + * 24-bit type. Byte aligned, in spite of the name. + */ +typedef struct { + unsigned char net_data[3]; +} net24_t; + +/* + * 48-bit type. Byte aligned. + */ +typedef struct { + unsigned char net_data[6]; +} net48_t; + +/* + * Unaligned network order types. + * Any of these structures can be byte aligned. No padding is implied. + */ +typedef struct { + unsigned char net_data[2]; +} ua_net16_t; + +typedef struct { + unsigned char net_data[4]; +} ua_net32_t; + +typedef struct { + unsigned char net_data[8]; +} ua_net64_t; + +/* + * Accessor functions. + */ + +/** + * net8_get(net) - fetch from a network-order 8-bit field. + * + * @param net pointer to network-order 8-bit data. + * @return the host-order value. + */ +static inline u_int8_t net8_get(const net8_t * net) +{ + return *net; +} + +/** + * net8_put(net, val) - store to a network-order 8-bit field. + * + * @param net pointer to network-order 8-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net8_put(net8_t * net, u_int8_t val) +{ + *net = val; +} + +/** + * net16_get(net) - fetch from a network-order 16-bit field. + * + * @param net pointer to type net16_t, network-order 16-bit data. + * @return the host-order value. + */ +static inline u_int16_t net16_get(const net16_t * net) +{ + return ntohs(net->net_data); +} + +/** + * net16_put(net, val) - store to a network-order 16-bit field. + * + * @param net pointer to a net16_t, network-order 16-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net16_put(net16_t * net, u_int16_t val) +{ + net->net_data = htons(val); +} + +/** + * ua_net16_get(net) - fetch from an unaligned network-order 16-bit field. + * + * @param net pointer to type ua_net16_t, unaligned, network-order 16-bit data. + * @return the host-order value. + */ +static inline u_int16_t ua_net16_get(const ua_net16_t * net) +{ + return (net->net_data[0] << 8) | net->net_data[1]; +} + +/** + * ua_net16_put(net, val) - store to a network-order 16-bit field. + * + * @param net pointer to a ua_net16_t, network-order 16-bit data. + * @param val host-order value to be stored at net. + */ +static inline void ua_net16_put(ua_net16_t * net, u_int16_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[1] = (u_int8_t)(val & 0xFF); +} + +/** + * net24_get(net) - fetch from a network-order 24-bit field. + * + * @param net pointer to type net24_t, network-order 24-bit data. + * @return the host-order value. + */ +static inline u_int32_t net24_get(const net24_t * net) +{ + return (net->net_data[0] << 16) | + (net->net_data[1] << 8) | net->net_data[2]; +} + +/** + * net24_put(net, val) - store to a network-order 24-bit field. + * + * @param net pointer to a net24_t, network-order 24-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net24_put(net24_t * net, u_int32_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[2] = (u_int8_t)(val & 0xFF); +} + +/** + * net32_get(net) - fetch from a network-order 32-bit field. + * + * @param net pointer to type net32_t, network-order 32-bit data. + * @return the host-order value. + */ +static inline u_int32_t net32_get(const net32_t * net) +{ + return ntohl(net->net_data); +} + +/** + * net32_put(net, val) - store to a network-order 32-bit field. + * + * @param net pointer to a net32_t, network-order 32-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net32_put(net32_t * net, u_int32_t val) +{ + net->net_data = htonl(val); +} + +/** + * ua_net32_get(net) - fetch from an unaligned network-order 32-bit field. + * + * @param net pointer to type ua_net32_t, unaligned, network-order 32-bit data. + * @return the host-order value. + */ +static inline u_int32_t ua_net32_get(const ua_net32_t * net) +{ + return (net->net_data[0] << 24) | (net->net_data[1] << 16) | + (net->net_data[2] << 8) | net->net_data[3]; +} + +/** + * ua_net32_put(net, val) - store to a network-order 32-bit field. + * + * @param net pointer to a ua_net32_t, network-order 32-bit data. + * @param val host-order value to be stored at net. + */ +static inline void ua_net32_put(ua_net32_t * net, u_int32_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 24) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[2] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[3] = (u_int8_t)(val & 0xFF); +} + +/** + * net48_get(net) - fetch from a network-order 48-bit field. + * + * @param net pointer to type net48_t, network-order 48-bit data. + * @return the host-order value. + */ +static inline u_int64_t net48_get(const net48_t * net) +{ + return ((u_int64_t) net->net_data[0] << 40) | + ((u_int64_t) net->net_data[1] << 32) | + ((u_int64_t) net->net_data[2] << 24) | + ((u_int64_t) net->net_data[3] << 16) | + ((u_int64_t) net->net_data[4] << 8) | + (u_int64_t) net->net_data[5]; +} + +/** + * net48_put(net, val) - store to a network-order 48-bit field. + * + * @param net pointer to a net48_t, network-order 48-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net48_put(net48_t * net, u_int64_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 40) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 32) & 0xFF); + net->net_data[2] = (u_int8_t)((val >> 24) & 0xFF); + net->net_data[3] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[4] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[5] = (u_int8_t)(val & 0xFF); +} + +/** + * net64_get(net) - fetch from a network-order 64-bit field. + * + * @param net pointer to type net64_t, network-order 64-bit data. + * @return the host-order value. + */ +static inline u_int64_t net64_get(const net64_t * net) +{ + return ((u_int64_t) ntohl(net->net_data[0]) << 32) | + ntohl(net->net_data[1]); +} + +/** + * net64_put(net, val) - store to a network-order 64-bit field. + * + * @param net pointer to a net64_t, network-order 64-bit data. + * @param val host-order value to be stored at net. + */ +static inline void net64_put(net64_t * net, u_int64_t val) +{ + net->net_data[0] = (u_int32_t)htonl(val >> 32); + net->net_data[1] = (u_int32_t)htonl((u_int32_t) val); +} + +/** + * ua_net64_get(net) - fetch from an unaligned network-order 64-bit field. + * + * @param net pointer to type ua_net64_t, unaligned, network-order 64-bit data. + * @return the host-order value. + */ +static inline u_int64_t ua_net64_get(const ua_net64_t * net) +{ + return ((u_int64_t) net->net_data[0] << 56) | + ((u_int64_t) net->net_data[1] << 48) | + ((u_int64_t) net->net_data[2] << 40) | + ((u_int64_t) net->net_data[3] << 32) | + ((u_int64_t) net->net_data[4] << 24) | + ((u_int64_t) net->net_data[5] << 16) | + ((u_int64_t) net->net_data[6] << 8) | + (u_int64_t) net->net_data[7]; +} + +/** + * ua_net64_put(net, val) - store to a network-order 64-bit field. + * + * @param net pointer to a ua_net64_t, network-order 64-bit data. + * @param val host-order value to be stored at net. + */ +static inline void ua_net64_put(ua_net64_t * net, u_int64_t val) +{ + net->net_data[0] = (u_int8_t)((val >> 56) & 0xFF); + net->net_data[1] = (u_int8_t)((val >> 48) & 0xFF); + net->net_data[2] = (u_int8_t)((val >> 40) & 0xFF); + net->net_data[3] = (u_int8_t)((val >> 32) & 0xFF); + net->net_data[4] = (u_int8_t)((val >> 24) & 0xFF); + net->net_data[5] = (u_int8_t)((val >> 16) & 0xFF); + net->net_data[6] = (u_int8_t)((val >> 8) & 0xFF); + net->net_data[7] = (u_int8_t)(val & 0xFF); +} + +/* + * Compile-time initializers for the network-order type structures. + * Note that the upper byte of these values is not masked so the + * compiler will catch initializers that don't fit in the field. + */ + +/** + * NET8_INIT(_val) - initialize a net8_t type. + * + * @param _val 8-bit value. + * @return net8_t network-order value. + */ +#define NET8_INIT(_val) (_val) + +/** + * NET24_INIT(_val) - initialize a net24_t type. + * + * @param _val host-order value. + * @return net24_t network-order value. + */ +#define NET24_INIT(_val) { { \ + ((_val) >> 16), \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET48_INIT(_val) - initialize a net48_t type. + * + * @param _val host-order value. + * @return net48_t network-order value. + */ +#define NET48_INIT(_val) { { \ + ((_val) >> 40), \ + ((_val) >> 32) & 0xff, \ + ((_val) >> 24) & 0xff, \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET16_INIT(_val) - initialize a net16_t type. + * + * @param _val host-order value. + * @return net16_t network-order value. + */ +#define NET16_INIT(_val) { htons(_val) } + +/** + * UA_NET16_INIT(_val) - initialize an unaligned 16-bit type. + * + * @param _val host-order value. + * @return ua_net24_t network-order value. + */ +#define UA_NET16_INIT(_val) { { \ + ((_val) >> 8), \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET32_INIT(_val) - initialize a 32-bit type. + * + * @param _val host-order value. + * @return net32_t network-order value. + */ +#define NET32_INIT(_val) { htonl(_val) } + +/** + * UA_NET32_INIT(_val) - initialize an unaligned 32-bit type. + * + * @param _val host-order value. + * @return ua_net32_t network-order value. + */ +#define UA_NET32_INIT(_val) { { \ + ((_val) >> 24), \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * UA_NET48_INIT(_val) - initialize an unaligned 48-bit type. + * + * @param _val host-order value. + * @return ua_net48_t network-order value. + */ +#define UA_NET48_INIT(_val) { { \ + ((_val) >> 40), \ + ((_val) >> 32) & 0xff, \ + ((_val) >> 24) & 0xff, \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +/** + * NET64_INIT(_val) - initialize an unaligned 64-bit type. + * + * @param _val host-order value. + * @return ua_net64_t network-order value. + */ +#define NET64_INIT(_val) { { \ + htonl((_val) >> 32), \ + htonl((_val) & 0xffffffff) \ + } } + +/** + * UA_NET64_INIT(_val) - initialize a 64-bit type. + * + * @param _val host-order value. + * @return net64_t network-order value. + */ +#define UA_NET64_INIT(_val) { { \ + ((_val) >> 56), \ + ((_val) >> 48) & 0xff, \ + ((_val) >> 40) & 0xff, \ + ((_val) >> 32) & 0xff, \ + ((_val) >> 24) & 0xff, \ + ((_val) >> 16) & 0xff, \ + ((_val) >> 8) & 0xff, \ + ((_val) >> 0) & 0xff \ + } } + +#endif /* _LIBSA_NET_TYPES_H_ */ diff --git a/include/rtnetlink.h b/include/rtnetlink.h new file mode 100644 index 0000000..dfe781e --- /dev/null +++ b/include/rtnetlink.h @@ -0,0 +1,65 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _RTNETLINK_ +#define _RTNETLINK_ + +int rtnl_socket(unsigned int group); +typedef int rtnl_handler(struct nlmsghdr *nh, void *arg); +int rtnl_recv(int s, rtnl_handler *fn, void *arg); +ssize_t send_getlink_dump(int s); +int rtnl_set_iff_up(int ifindex, char *ifname); +int rtnl_set_iff_down(int ifindex, char *ifname); +int vlan_create(int ifindex, int vid, char *name); +int rtnl_find_vlan(int ifindex, int vid, char *ifname); +int rtnl_get_linkname(int ifindex, char *name); +int rtnl_get_sanmac(const char *ifname, unsigned char *addr); + +static inline void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +static inline void parse_nested_rtattr(struct rtattr *tb[], int max, struct rtattr *rta) +{ + parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); +} + +static inline void parse_ifinfo(struct rtattr *tb[], struct nlmsghdr *nh) +{ + struct ifinfomsg *ifm = NLMSG_DATA(nh); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), IFLA_PAYLOAD(nh)); +} + +static inline void parse_linkinfo(struct rtattr *tb[], struct rtattr *linkinfo) +{ + parse_nested_rtattr(tb, IFLA_INFO_MAX, linkinfo); +} + +static inline void parse_vlaninfo(struct rtattr *tb[], struct rtattr *vlan) +{ + parse_nested_rtattr(tb, IFLA_VLAN_MAX, vlan); +} + +#endif /* _RTNETLINK_ */ diff --git a/include/scsi_bsg_fc.h b/include/scsi_bsg_fc.h new file mode 100644 index 0000000..1c2ebee --- /dev/null +++ b/include/scsi_bsg_fc.h @@ -0,0 +1,321 @@ +/* + * FC Transport BSG Interface + * + * Copyright (C) 2008 James Smart, Emulex Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef SCSI_BSG_FC_H +#define SCSI_BSG_FC_H + +/* + * This file intended to be included by both kernel and user space + */ + +#include + +/* + * FC Transport SGIO v4 BSG Message Support + */ + +/* Default BSG request timeout (in seconds) */ +#define FC_DEFAULT_BSG_TIMEOUT (10 * HZ) + + +/* + * Request Message Codes supported by the FC Transport + */ + +/* define the class masks for the message codes */ +#define FC_BSG_CLS_MASK 0xF0000000 /* find object class */ +#define FC_BSG_HST_MASK 0x80000000 /* fc host class */ +#define FC_BSG_RPT_MASK 0x40000000 /* fc rport class */ + + /* fc_host Message Codes */ +#define FC_BSG_HST_ADD_RPORT (FC_BSG_HST_MASK | 0x00000001) +#define FC_BSG_HST_DEL_RPORT (FC_BSG_HST_MASK | 0x00000002) +#define FC_BSG_HST_ELS_NOLOGIN (FC_BSG_HST_MASK | 0x00000003) +#define FC_BSG_HST_CT (FC_BSG_HST_MASK | 0x00000004) +#define FC_BSG_HST_VENDOR (FC_BSG_HST_MASK | 0x000000FF) + + /* fc_rport Message Codes */ +#define FC_BSG_RPT_ELS (FC_BSG_RPT_MASK | 0x00000001) +#define FC_BSG_RPT_CT (FC_BSG_RPT_MASK | 0x00000002) + + + +/* + * FC Address Identifiers in Message Structures : + * + * Whenever a command payload contains a FC Address Identifier + * (aka port_id), the value is effectively in big-endian + * order, thus the array elements are decoded as follows: + * element [0] is bits 23:16 of the FC Address Identifier + * element [1] is bits 15:8 of the FC Address Identifier + * element [2] is bits 7:0 of the FC Address Identifier + */ + + +/* + * FC Host Messages + */ + +/* FC_BSG_HST_ADDR_PORT : */ + +/* Request: + * This message requests the FC host to login to the remote port + * at the specified N_Port_Id. The remote port is to be enumerated + * with the transport upon completion of the login. + */ +struct fc_bsg_host_add_rport { + uint8_t reserved; + + /* FC Address Identier of the remote port to login to */ + uint8_t port_id[3]; +}; + +/* Response: + * There is no additional response data - fc_bsg_reply->result is sufficient + */ + + +/* FC_BSG_HST_DEL_RPORT : */ + +/* Request: + * This message requests the FC host to remove an enumerated + * remote port and to terminate the login to it. + * + * Note: The driver is free to reject this request if it desires to + * remain logged in with the remote port. + */ +struct fc_bsg_host_del_rport { + uint8_t reserved; + + /* FC Address Identier of the remote port to logout of */ + uint8_t port_id[3]; +}; + +/* Response: + * There is no additional response data - fc_bsg_reply->result is sufficient + */ + + +/* FC_BSG_HST_ELS_NOLOGIN : */ + +/* Request: + * This message requests the FC_Host to send an ELS to a specific + * N_Port_ID. The host does not need to log into the remote port, + * nor does it need to enumerate the rport for further traffic + * (although, the FC host is free to do so if it desires). + */ +struct fc_bsg_host_els { + /* + * ELS Command Code being sent (must be the same as byte 0 + * of the payload) + */ + uint8_t command_code; + + /* FC Address Identier of the remote port to send the ELS to */ + uint8_t port_id[3]; +}; + +/* Response: + */ +/* fc_bsg_ctels_reply->status values */ +#define FC_CTELS_STATUS_OK 0x00000000 +#define FC_CTELS_STATUS_REJECT 0x00000001 +#define FC_CTELS_STATUS_P_RJT 0x00000002 +#define FC_CTELS_STATUS_F_RJT 0x00000003 +#define FC_CTELS_STATUS_P_BSY 0x00000004 +#define FC_CTELS_STATUS_F_BSY 0x00000006 +struct fc_bsg_ctels_reply { + /* + * Note: An ELS LS_RJT may be reported in 2 ways: + * a) A status of FC_CTELS_STATUS_OK is returned. The caller + * is to look into the ELS receive payload to determine + * LS_ACC or LS_RJT (by contents of word 0). The reject + * data will be in word 1. + * b) A status of FC_CTELS_STATUS_REJECT is returned, The + * rjt_data field will contain valid data. + * + * Note: ELS LS_ACC is determined by an FC_CTELS_STATUS_OK, and + * the receive payload word 0 indicates LS_ACC + * (e.g. value is 0x02xxxxxx). + * + * Note: Similarly, a CT Reject may be reported in 2 ways: + * a) A status of FC_CTELS_STATUS_OK is returned. The caller + * is to look into the CT receive payload to determine + * Accept or Reject (by contents of word 2). The reject + * data will be in word 3. + * b) A status of FC_CTELS_STATUS_REJECT is returned, The + * rjt_data field will contain valid data. + * + * Note: x_RJT/BSY status will indicae that the rjt_data field + * is valid and contains the reason/explanation values. + */ + uint32_t status; /* See FC_CTELS_STATUS_xxx */ + + /* valid if status is not FC_CTELS_STATUS_OK */ + struct { + uint8_t action; /* fragment_id for CT REJECT */ + uint8_t reason_code; + uint8_t reason_explanation; + uint8_t vendor_unique; + } rjt_data; +}; + + +/* FC_BSG_HST_CT : */ + +/* Request: + * This message requests that a CT Request be performed with the + * indicated N_Port_ID. The driver is responsible for logging in with + * the fabric and/or N_Port_ID, etc as per FC rules. This request does + * not mandate that the driver must enumerate the destination in the + * transport. The driver is allowed to decide whether to enumerate it, + * and whether to tear it down after the request. + */ +struct fc_bsg_host_ct { + uint8_t reserved; + + /* FC Address Identier of the remote port to send the ELS to */ + uint8_t port_id[3]; + + /* + * We need words 0-2 of the generic preamble for the LLD's + */ + uint32_t preamble_word0; /* revision & IN_ID */ + uint32_t preamble_word1; /* GS_Type, GS_SubType, Options, Rsvd */ + uint32_t preamble_word2; /* Cmd Code, Max Size */ + +}; +/* Response: + * + * The reply structure is an fc_bsg_ctels_reply structure + */ + + +/* FC_BSG_HST_VENDOR : */ + +/* Request: + * Note: When specifying vendor_id, be sure to read the Vendor Type and ID + * formatting requirements specified in scsi_netlink.h + */ +struct fc_bsg_host_vendor { + /* + * Identifies the vendor that the message is formatted for. This + * should be the recipient of the message. + */ + uint64_t vendor_id; + + /* start of vendor command area */ + uint32_t vendor_cmd[0]; +}; + +/* Response: + */ +struct fc_bsg_host_vendor_reply { + /* start of vendor response area */ + uint32_t vendor_rsp[0]; +}; + + + +/* + * FC Remote Port Messages + */ + +/* FC_BSG_RPT_ELS : */ + +/* Request: + * This message requests that an ELS be performed with the rport. + */ +struct fc_bsg_rport_els { + /* + * ELS Command Code being sent (must be the same as + * byte 0 of the payload) + */ + uint8_t els_code; +}; + +/* Response: + * + * The reply structure is an fc_bsg_ctels_reply structure + */ + + +/* FC_BSG_RPT_CT : */ + +/* Request: + * This message requests that a CT Request be performed with the rport. + */ +struct fc_bsg_rport_ct { + /* + * We need words 0-2 of the generic preamble for the LLD's + */ + uint32_t preamble_word0; /* revision & IN_ID */ + uint32_t preamble_word1; /* GS_Type, GS_SubType, Options, Rsvd */ + uint32_t preamble_word2; /* Cmd Code, Max Size */ +}; +/* Response: + * + * The reply structure is an fc_bsg_ctels_reply structure + */ + + + + +/* request (CDB) structure of the sg_io_v4 */ +struct fc_bsg_request { + uint32_t msgcode; + union { + struct fc_bsg_host_add_rport h_addrport; + struct fc_bsg_host_del_rport h_delrport; + struct fc_bsg_host_els h_els; + struct fc_bsg_host_ct h_ct; + struct fc_bsg_host_vendor h_vendor; + + struct fc_bsg_rport_els r_els; + struct fc_bsg_rport_ct r_ct; + } rqst_data; +} __attribute__((packed)); + + +/* response (request sense data) structure of the sg_io_v4 */ +struct fc_bsg_reply { + /* + * The completion result. Result exists in two forms: + * if negative, it is an -Exxx system errno value. There will + * be no further reply information supplied. + * else, it's the 4-byte scsi error result, with driver, host, + * msg and status fields. The per-msgcode reply structure + * will contain valid data. + */ + uint32_t result; + + /* If there was reply_payload, how much was recevied ? */ + uint32_t reply_payload_rcv_len; + + union { + struct fc_bsg_host_vendor_reply vendor_reply; + + struct fc_bsg_ctels_reply ctels_reply; + } reply_data; +}; + + +#endif /* SCSI_BSG_FC_H */ diff --git a/include/scsi_netlink.h b/include/scsi_netlink.h new file mode 100644 index 0000000..62daf4b --- /dev/null +++ b/include/scsi_netlink.h @@ -0,0 +1,146 @@ +/* + * SCSI Transport Netlink Interface + * Used for the posting of outbound SCSI transport events + * + * Copyright (C) 2006 James Smart, Emulex Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef SCSI_NETLINK_H +#define SCSI_NETLINK_H + +#include + + +/* + * This file intended to be included by both kernel and user space + */ + +/* Single Netlink Message type to send all SCSI Transport messages */ +#define SCSI_TRANSPORT_MSG (NLMSG_MIN_TYPE + 1) + +/* SCSI Transport Broadcast Groups */ + /* leaving groups 0 and 1 unassigned */ +#define SCSI_NL_GRP_FC_EVENTS (1<<2) /* Group 2 */ +#define SCSI_NL_GRP_CNT 3 + + +/* SCSI_TRANSPORT_MSG event message header */ +struct scsi_nl_hdr { + uint8_t version; + uint8_t transport; + uint16_t magic; + uint16_t msgtype; + uint16_t msglen; +} __attribute__((aligned(sizeof(uint64_t)))); + +/* scsi_nl_hdr->version value */ +#define SCSI_NL_VERSION 1 + +/* scsi_nl_hdr->magic value */ +#define SCSI_NL_MAGIC 0xA1B2 + +/* scsi_nl_hdr->transport value */ +#define SCSI_NL_TRANSPORT 0 +#define SCSI_NL_TRANSPORT_FC 1 +#define SCSI_NL_MAX_TRANSPORTS 2 + +/* Transport-based scsi_nl_hdr->msgtype values are defined in each transport */ + +/* + * GENERIC SCSI scsi_nl_hdr->msgtype Values + */ + /* kernel -> user */ +#define SCSI_NL_SHOST_VENDOR 0x0001 + /* user -> kernel */ +/* SCSI_NL_SHOST_VENDOR msgtype is kernel->user and user->kernel */ + + +/* + * Message Structures : + */ + +/* macro to round up message lengths to 8byte boundary */ +#define SCSI_NL_MSGALIGN(len) (((len) + 7) & ~7) + + +/* + * SCSI HOST Vendor Unique messages : + * SCSI_NL_SHOST_VENDOR + * + * Note: The Vendor Unique message payload will begin directly after + * this structure, with the length of the payload per vmsg_datalen. + * + * Note: When specifying vendor_id, be sure to read the Vendor Type and ID + * formatting requirements specified below + */ +struct scsi_nl_host_vendor_msg { + struct scsi_nl_hdr snlh; /* must be 1st element ! */ + uint64_t vendor_id; + uint16_t host_no; + uint16_t vmsg_datalen; +} __attribute__((aligned(sizeof(uint64_t)))); + + +/* + * Vendor ID: + * If transports post vendor-unique events, they must pass a well-known + * 32-bit vendor identifier. This identifier consists of 8 bits indicating + * the "type" of identifier contained, and 24 bits of id data. + * + * Identifiers for each type: + * PCI : ID data is the 16 bit PCI Registered Vendor ID + */ +#define SCSI_NL_VID_TYPE_SHIFT 56 +#define SCSI_NL_VID_TYPE_MASK ((__u64)0xFF << SCSI_NL_VID_TYPE_SHIFT) +#define SCSI_NL_VID_TYPE_PCI ((__u64)0x01 << SCSI_NL_VID_TYPE_SHIFT) +#define SCSI_NL_VID_ID_MASK (~SCSI_NL_VID_TYPE_MASK) + + +#define INIT_SCSI_NL_HDR(hdr, t, mtype, mlen) \ + { \ + (hdr)->version = SCSI_NL_VERSION; \ + (hdr)->transport = t; \ + (hdr)->magic = SCSI_NL_MAGIC; \ + (hdr)->msgtype = mtype; \ + (hdr)->msglen = mlen; \ + } + + +#ifdef __KERNEL__ + +#include + +/* Exported Kernel Interfaces */ +int scsi_nl_add_transport(u8 tport, + int (*msg_handler)(struct sk_buff *), + void (*event_handler)(struct notifier_block *, unsigned long, void *)); +void scsi_nl_remove_transport(u8 tport); + +int scsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt, + int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload, + u32 len, u32 pid), + void (*nlevt_handler)(struct notifier_block *nb, + unsigned long event, void *notify_ptr)); +void scsi_nl_remove_driver(u64 vendor_id); + +void scsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr); +int scsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id, + char *data_buf, u32 data_len); + +#endif /* __KERNEL__ */ + +#endif /* SCSI_NETLINK_H */ diff --git a/include/scsi_netlink_fc.h b/include/scsi_netlink_fc.h new file mode 100644 index 0000000..f5cb8ed --- /dev/null +++ b/include/scsi_netlink_fc.h @@ -0,0 +1,70 @@ +/* + * FC Transport Netlink Interface + * + * Copyright (C) 2006 James Smart, Emulex Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef SCSI_NETLINK_FC_H +#define SCSI_NETLINK_FC_H + +#include "scsi_netlink.h" + +/* + * This file intended to be included by both kernel and user space + */ + +/* + * FC Transport Message Types + */ + /* kernel -> user */ +#define FC_NL_ASYNC_EVENT 0x0100 + /* user -> kernel */ +/* none */ + + +/* + * Message Structures : + */ + +/* macro to round up message lengths to 8byte boundary */ +#define FC_NL_MSGALIGN(len) (((len) + 7) & ~7) + + +/* + * FC Transport Broadcast Event Message : + * FC_NL_ASYNC_EVENT + * + * Note: if Vendor Unique message, &event_data will be start of + * vendor unique payload, and the length of the payload is + * per event_datalen + * + * Note: When specifying vendor_id, be sure to read the Vendor Type and ID + * formatting requirements specified in scsi_netlink.h + */ +struct fc_nl_event { + struct scsi_nl_hdr snlh; /* must be 1st element ! */ + uint64_t seconds; + uint64_t vendor_id; + uint16_t host_no; + uint16_t event_datalen; + uint32_t event_num; + uint32_t event_code; + uint32_t event_data; +} __attribute__((aligned(sizeof(uint64_t)))); + + +#endif /* SCSI_NETLINK_FC_H */ diff --git a/include/strarr.h b/include/strarr.h new file mode 100644 index 0000000..c1fea94 --- /dev/null +++ b/include/strarr.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _STRARR_H_ +#define _STRARR_H_ + +typedef const char * const _strarr[]; + +struct _str_arr_desc { + const unsigned limit; + const _strarr * const str_arr; + const char * const unknown_str; + const char * const out_of_range_str; +}; + +#define GEN_STR_ARR(sc, sz, nm, unk, oor, ...) \ +static const char * const _##nm##_strs[sz] = { __VA_ARGS__ }; \ +sc const struct _str_arr_desc nm = { \ + .limit = sizeof(_##nm##_strs) / sizeof(_##nm##_strs[0]),\ + .str_arr = &_##nm##_strs, \ + .unknown_str = unk, \ + .out_of_range_str = oor, \ +} + +#define EXT_STR_ARR(nm, unk, oor, ...) \ + GEN_STR_ARR(, , nm, unk, oor, __VA_ARGS__) +#define EXT_STR_ARR_SZ(nm, sz, unk, oor, ...) \ + GEN_STR_ARR(, sz, nm, unk, oor, __VA_ARGS__) +#define STR_ARR(nm, unk, oor, ...) \ + GEN_STR_ARR(static, , nm, unk, oor, __VA_ARGS__) +#define STR_ARR_SZ(nm, sz, unk, oor, ...) \ + GEN_STR_ARR(static, sz, nm, unk, oor, __VA_ARGS__) + +static inline const char * +getstr(const struct _str_arr_desc *desc, unsigned ix) +{ + const char *str; + + if (ix >= desc->limit) + return desc->out_of_range_str; + str = (*desc->str_arr)[ix]; + if (!str) + return desc->unknown_str; + + return str; +} + +#endif /* _STRARR_H_ */ diff --git a/include/sysfs_hba.h b/include/sysfs_hba.h new file mode 100644 index 0000000..abd3326 --- /dev/null +++ b/include/sysfs_hba.h @@ -0,0 +1,118 @@ +/* + * Copyright(c) 2015 SUSE GmbH. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * Maintained at www.Open-FCoE.org + */ + +#ifndef _SYSFS_HBA_H +#define _SYSFS_HBA_H + +#include + +#define SYSFS_HOST_DIR "/sys/class/fc_host" +#define SYSFS_NET_DIR "/sys/class/net" +#define SYSFS_RPORT_DIR "/sys/class/fc_remote_ports" + +#define HBA_PORTSTATE_UNKNOWN 1 /* Unknown */ +#define HBA_PORTSTATE_ONLINE 2 /* Operational */ +#define HBA_PORTSTATE_OFFLINE 3 /* User Offline */ +#define HBA_PORTSTATE_BYPASSED 4 /* Bypassed */ +#define HBA_PORTSTATE_DIAGNOSTICS 5 /* In diagnostics mode */ +#define HBA_PORTSTATE_LINKDOWN 6 /* Link Down */ +#define HBA_PORTSTATE_ERROR 7 /* Port Error */ +#define HBA_PORTSTATE_LOOPBACK 8 /* Loopback */ + +#define HBA_PORTSPEED_UNKNOWN 0x0000 /* Unknown - transceiver incable + * of reporting */ +#define HBA_PORTSPEED_1GBIT 0x0001 /* 1 GBit/sec */ +#define HBA_PORTSPEED_2GBIT 0x0002 /* 2 GBit/sec */ +#define HBA_PORTSPEED_10GBIT 0x0004 /* 10 GBit/sec */ +#define HBA_PORTSPEED_4GBIT 0x0008 /* 4 GBit/sec */ +#define HBA_PORTSPEED_8GBIT 0x0010 /* 8 GBit/sec */ +#define HBA_PORTSPEED_16GBIT 0x0020 /* 16 GBit/sec */ +#define HBA_PORTSPEED_32GBIT 0x0040 /* 32 GBit/sec */ +#define HBA_PORTSPEED_20GBIT 0x0080 /* 20 GBit/sec */ +#define HBA_PORTSPEED_40GBIT 0x0100 /* 40 GBit/sec */ +#define HBA_PORTSPEED_NOT_NEGOTIATED (1 << 15) /* Speed not established */ + +/* Event Codes */ +#define HBA_EVENT_LIP_OCCURRED 1 +#define HBA_EVENT_LINK_UP 2 +#define HBA_EVENT_LINK_DOWN 3 +#define HBA_EVENT_LIP_RESET_OCCURRED 4 +#define HBA_EVENT_RSCN 5 +#define HBA_EVENT_PROPRIETARY 0xFFFF + +struct port_attributes { + char device_name[256]; + char symbolic_name[256]; + char node_name[256]; + char port_name[256]; + char fabric_name[256]; + char speed[256]; + char supported_speeds[256]; + char maxframe_size[256]; + char port_id[256]; + char port_state[256]; + char scsi_target_id[256]; + char supported_classes[256]; + char roles[256]; +}; + +struct hba_info { + char manufacturer[64]; + char serial_number[64]; + char model_description[256]; + char hardware_version[256]; + char driver_name[256]; + char driver_version[256]; + uint32_t nports; +}; + +struct port_statistics { + uint64_t seconds_since_last_reset; + uint64_t tx_frames; + uint64_t tx_words; + uint64_t rx_frames; + uint64_t rx_words; + uint64_t error_frames; + uint64_t invalid_crc_count; + uint64_t invalid_tx_word_count; + uint64_t link_failure_count; + uint64_t fcp_control_requests; + uint64_t fcp_input_requests; + uint64_t fcp_input_megabytes; + uint64_t fcp_output_requests; + uint64_t fcp_output_megabytes; +}; + +struct hba_wwn { + union { + uint8_t wwn[8]; + uint64_t wwn64; + }; +}; + +int get_number_of_adapters(void); +struct hba_info *get_hbainfo_by_pcidev(const char *pcidev); +struct port_statistics *get_port_statistics(const char *host); +struct port_attributes *get_port_attribs(const char *host); +struct port_attributes *get_port_attribs_by_device(char *path); +struct port_attributes *get_rport_attribs(const char *rport); +struct port_attributes *get_rport_attribs_by_device(char *path); +char *get_pci_dev_from_netdev(const char *netdev); +char *get_host_from_netdev(const char *netdev); +char *get_host_by_wwpn(struct hba_wwn wwn); +char *get_host_by_fcid(uint32_t fcid); +char *get_rport_by_fcid(uint32_t fcid); + +#endif /* _SYSFS_HBA_H */ diff --git a/lib/fcoe_utils.c b/lib/fcoe_utils.c new file mode 100644 index 0000000..516eac5 --- /dev/null +++ b/lib/fcoe_utils.c @@ -0,0 +1,297 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoe_utils.h" + +int fcoe_sysfs_read(char *buf, int size, const char *path) +{ + FILE *fp; + unsigned int i; + int rc = -EINVAL; + + fp = fopen(path, "r"); + if (fp) { + if (fgets(buf, size, fp)) { + /* + * Strip trailing newline by replacing + * any '\r' or '\n' instances with '\0'. + * It's not as elegant as it could be, but + * we know that the symbolic name won't + * have either of those characters until + * the end of the line. + */ + for (i = 0; i < strlen(buf); i++) { + if (buf[i] == '\n' || + buf[i] == '\r') { + buf[i] = '\0'; + break; + } + } + rc = 0; + } + + fclose(fp); + } + + return rc; +} + +static int fcoe_check_fchost(const char *ifname, const char *dname) +{ + char buf[MAX_STR_LEN]; + char path[MAX_PATH_LEN]; + int rc = -EINVAL; + + sprintf(path, "%s/%s/symbolic_name", SYSFS_FCHOST, dname); + + if (!fcoe_sysfs_read(buf, MAX_STR_LEN, path)) + rc = check_symbolic_name_for_interface(buf, ifname); + + return rc; +} + +enum fcoe_status fcoe_find_fchost(const char *ifname, char *fchost, int len) +{ + int n, dname_len, status; + struct dirent **namelist; + int rc = ENOFCOECONN; + + status = n = scandir(SYSFS_FCHOST, &namelist, 0, alphasort); + + for (n-- ; n >= 0 ; n--) { + if (rc) { + /* check symbolic name */ + if (!fcoe_check_fchost(ifname, namelist[n]->d_name)) { + dname_len = strnlen(namelist[n]->d_name, len); + + if (len > dname_len) { + strncpy(fchost, namelist[n]->d_name, + dname_len + 1); + /* rc = 0 indicates found */ + rc = SUCCESS; + } else { + /* + * The fc_host is too large + * for the buffer. + */ + rc = EINTERR; + } + } + } + free(namelist[n]); + } + if (status >= 0) + free(namelist); + + return rc; +} + +enum fcoe_status fcoe_validate_interface(char *ifname) +{ + enum fcoe_status rc = SUCCESS; + char path[MAX_PATH_LEN]; + + + if (!strlen(ifname)) + rc = ENOETHDEV; + + /* + * TODO: Is there a better way to check if the + * interface name is correct? + */ + sprintf(path, "%s/%s", SYSFS_NET, ifname); + if (!rc && fcoe_checkdir(path)) + rc = ENOETHDEV; + + return rc; +} + +/* + * Validate an existing instance for an FC interface + */ +enum fcoe_status fcoe_validate_fcoe_conn(char *ifname) +{ + char fchost[FCHOSTBUFLEN]; + enum fcoe_status rc = SUCCESS; + + rc = fcoe_validate_interface(ifname); + + if (!rc) + rc = fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN); + + return rc; +} + +/* + * Open and close to check if directory exists + */ +int fcoe_checkdir(char *dir) +{ + DIR *d = NULL; + + if (!dir) + return -EINVAL; + /* check if we have sysfs */ + d = opendir(dir); + if (!d) + return -EINVAL; + closedir(d); + return 0; +} + +/* + * Parse the interface name from the symbolic name string. + * Assumption: Symbolic name is of the type " over " + * Specifically there is a space before the + */ +char *get_ifname_from_symbolic_name(const char *symbolic_name) +{ + char *last_space = strrchr(symbolic_name, ' '); + + if (!last_space || strlen(last_space) == 1) + return NULL; + + return (char *)(last_space + 1); +} + +int check_symbolic_name_for_interface(const char *symbolic_name, + const char *ifname) +{ + int rc = -EINVAL; + char *symb; + + if (!ifname) + return rc; + + symb = get_ifname_from_symbolic_name(symbolic_name); + + /* + * It's important to use the length of the ifname + * from the symbolic_name here. If the ifname length + * were used then if the user passed in a substring + * of the the interface name it would match because + * we'd only be looking for the first few characters, + * not the whole string. + */ + if (symb && !strncmp(ifname, symb, strlen(symb))) + rc = 0; + + return rc; +} + +enum fcoe_status fcm_write_str_to_sysfs_file(const char *path, const char *str) +{ + FILE *fp = NULL; + enum fcoe_status ret = EFAIL; + + fp = fopen(path, "w"); + if (!fp) + goto err_out; + + if (EOF == fputs(str, fp)) + goto out; + + ret = SUCCESS; +out: + fclose(fp); +err_out: + return ret; +} + +static int fchost_filter(const struct dirent *dent) +{ + return !strncmp(dent->d_name, "host", 4); +} + +static int fcoe_check_ctlr(const char *fchost, const char *dname) +{ + int n, status; + struct dirent **namelist; + char path[MAX_PATH_LEN]; + int rc = -EINVAL; + + sprintf(path, "%s/%s", SYSFS_FCOE_BUS_DEVICES, dname); + status = n = scandir(path, &namelist, fchost_filter, alphasort); + for (n-- ; n >= 0 ; n--) { + if (rc) { + if (!strncmp(namelist[n]->d_name, fchost, 20)) + rc = SUCCESS; + else + rc = EINTERR; + } + free(namelist[n]); + } + if (status >= 0) + free(namelist); + + return rc; +} + +static int ctlr_filter(const struct dirent *dent) +{ + return !strncmp(dent->d_name, "ctlr_", 5); +} + +enum fcoe_status fcoe_find_ctlr(const char *fchost, char *ctlr, int len) +{ + int n, dname_len, status; + struct dirent **namelist; + int rc = ENOFCOECONN; + + status = n = scandir(SYSFS_FCOE_BUS_DEVICES, &namelist, + ctlr_filter, alphasort); + for (n-- ; n >= 0 ; n--) { + if (rc) { + /* check ctlr against known host */ + if (!fcoe_check_ctlr(fchost, + namelist[n]->d_name)) { + + dname_len = strnlen(namelist[n]->d_name, len); + + if (len > dname_len) { + strncpy(ctlr, namelist[n]->d_name, + dname_len + 1); + /* rc = 0 indicates found */ + rc = SUCCESS; + } else { + /* + * The fc_host is too large + * for the buffer. + */ + rc = EINTERR; + } + } + } + free(namelist[n]); + } + if (status >= 0) + free(namelist); + + return rc; +} + +enum fcoe_status fcm_write_str_to_ctlr_attr(const char *ctlr, + const char *attr, + const char *str) +{ + char path[MAX_PATH_LEN]; + + sprintf(path, "%s/%s/%s", SYSFS_FCOE_BUS_DEVICES, ctlr, attr); + return fcm_write_str_to_sysfs_file(path, str); +} diff --git a/lib/fip.c b/lib/fip.c new file mode 100644 index 0000000..c943a74 --- /dev/null +++ b/lib/fip.c @@ -0,0 +1,438 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +/* Routines for automatic FIP VLAN discovery and creation */ +/* Shared by fcoemon and fipvlan */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fip.h" +#include "fcoemon_utils.h" +#include "rtnetlink.h" + +#define FIP_LOG(...) sa_log(__VA_ARGS__) +#define FIP_LOG_ERR(error, ...) sa_log_err(error, __func__, __VA_ARGS__) +#define FIP_LOG_ERRNO(...) sa_log_err(errno, __func__, __VA_ARGS__) +#define FIP_LOG_DBG(...) sa_log_debug(__VA_ARGS__) + +static int fip_mac_is_valid(unsigned char *mac) +{ + if (0x01 & mac[0]) + return 0; + return !!(mac[0] | mac[1] | mac[2] | mac[3] | mac[4] | mac[5]); +} + +/** + * fip_get_sanmac - get SAN MAC through dcbnl interface + * @ifindex: network interface index to send on + * @addr: output buffer to the SAN MAC address + * + * Returns 0 for success, none 0 for failure + */ +static int fip_get_sanmac(int ifindex, unsigned char *addr) +{ + int s; + int rc = -EIO; + struct ifreq ifr; + + memset(addr, 0, ETHER_ADDR_LEN); + s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s < 0) + return s; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = ifindex; + rc = ioctl(s, SIOCGIFNAME, &ifr); + close(s); + if (rc) + return rc; + + rc = rtnl_get_sanmac(ifr.ifr_name, addr); + if (rc) + return rc; + + return !fip_mac_is_valid(addr); +} + +/** + * fip_socket_add_addr - add a MAC address to the input socket + * @s: ETH_P_FIP packet socket to setsockopt on + * @ifindex: network interface index to send on + * @add: true to add false to del + * @mac: MAC address to add or delete + * @multi: false if unicast, true if multicast address + */ +static int +fip_socket_add_addr(int s, int ifindex, bool add, const __u8 *mac, bool multi) +{ + struct packet_mreq mr; + int rc = 0; + + memset(&mr, 0, sizeof(mr)); + mr.mr_ifindex = ifindex; + mr.mr_type = multi ? PACKET_MR_MULTICAST : PACKET_MR_UNICAST; + mr.mr_alen = ETHER_ADDR_LEN; + memcpy(mr.mr_address, mac, ETHER_ADDR_LEN); + if (setsockopt(s, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mr, sizeof(mr)) < 0) { + FIP_LOG_DBG("PACKET_%s_MEMBERSHIP:failed\n", + add ? "ADD" : "DROP"); + rc = -errno; + } + return rc; +} + +/** + * fip_socket_sanmac - add SAN MAC to the unicast list for input socket + * @s: ETH_P_FIP packet socket to setsockopt on + * @ifindex: network interface index to send on + * @add: 1 to add 0 to del + */ +static int fip_socket_sanmac(int s, int ifindex, unsigned char *mac, int add) +{ + unsigned char smac[ETHER_ADDR_LEN]; + + if (fip_get_sanmac(ifindex, smac)) { + FIP_LOG_DBG("%s: no sanmac, ifindex %d\n", __func__, ifindex); + memcpy(smac, mac, ETHER_ADDR_LEN); + } + + return fip_socket_add_addr(s, ifindex, add, smac, false); +} + +/** + * fip_socket_multi - add multicast MAC address to the input socket + * @s: ETH_P_FIP packet socket to setsockopt on + * @ifindex: network interface index to send on + * @add: true to add, false to del + * @multi: Multicast destination + */ +static void +fip_socket_multi(int s, int ifindex, bool add, enum fip_multi multi) +{ + __u8 smac[ETHER_ADDR_LEN] = FIP_ALL_FCOE_MACS; + + smac[ETHER_ADDR_LEN - 1] = multi; + fip_socket_add_addr(s, ifindex, add, smac, true); +} + +/** + * fip_ethhdr - fills up the ethhdr for FIP + * @ifindex: network interface index to send on + * @mac: mac address of the sending network interface + * @eh: buffer for ether header + * @dest: destination selector + * + * Note: assuming no VLAN + */ +static void fip_ethhdr(int ifindex, const unsigned char *mac, struct ethhdr *eh, + enum fip_multi multi) +{ + unsigned char smac[ETHER_ADDR_LEN]; + unsigned char dmac[ETHER_ADDR_LEN] = FIP_ALL_FCF_MACS; + if (fip_get_sanmac(ifindex, smac)) + memcpy(smac, mac, ETHER_ADDR_LEN); + + dmac[ETHER_ADDR_LEN - 1] = multi; + eh->h_proto = htons(ETH_P_FIP); + memcpy(eh->h_source, smac, ETHER_ADDR_LEN); + memcpy(eh->h_dest, dmac, ETHER_ADDR_LEN); +} + +/** + * drain_socket - Discard receive packets on a socket + */ +static void drain_socket(int s) +{ + char buf[4096]; + struct sockaddr_ll sa; + struct iovec iov[] = { + { .iov_base = buf, .iov_len = sizeof(buf), }, + }; + struct msghdr msg = { + .msg_name = &sa, + .msg_namelen = sizeof(sa), + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + }; + + while (recvmsg(s, &msg, MSG_DONTWAIT) > 0) { + /* Drop the packet */ + } +} + +/** + * fip_socket - create and bind a packet socket for FIP + * @ifindex: ifindex of netdevice to bind to + * @multi: Indication of any multicast address to bind to + */ +int fip_socket(int ifindex, unsigned char *mac, enum fip_multi multi) +{ + struct sockaddr_ll sa = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_FIP), + .sll_ifindex = ifindex, + }; + int s; + int rc; + + s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_FIP)); + if (s < 0) { + FIP_LOG_ERR(errno, "Failed to open FIP socket.\n"); + return s; + } + + rc = fip_socket_sanmac(s, ifindex, mac, 1); + if (rc < 0 && rc != -ENXIO) { + FIP_LOG_ERR(errno, "Failed to open SANMAC socket.\n"); + close(s); + return rc; + } + if (multi != FIP_NONE) + fip_socket_multi(s, ifindex, true, multi); + + rc = bind(s, (struct sockaddr *) &sa, sizeof(sa)); + if (rc < 0) { + FIP_LOG_ERR(errno, "Bind failed.\n"); + close(s); + return rc; + } + + /* + * Drain the packets that were received between socket and bind. We + * could've received packets not meant for our interface. This can + * interfere with vlan discovery + */ + drain_socket(s); + + return s; +} + + +/** + * fip_send_vlan_request - send a FIP VLAN request + * @s: ETH_P_FIP packet socket to send on + * @ifindex: network interface index to send on + * @mac: mac address of the sending network interface + * @dest: destination selector + * + * Note: sends to address selected by @dest + */ +ssize_t fip_send_vlan_request(int s, int ifindex, const unsigned char *mac, + enum fip_multi dest) +{ + struct sockaddr_ll sa = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_FIP), + .sll_ifindex = ifindex, + .sll_hatype = ARPHRD_ETHER, + .sll_pkttype = PACKET_MULTICAST, + .sll_halen = ETHER_ADDR_LEN, + .sll_addr = FIP_ALL_FCF_MACS, + }; + struct fiphdr fh = { + .fip_version = FIP_VERSION(1), + .fip_proto = htons(FIP_PROTO_VLAN), + .fip_subcode = FIP_VLAN_REQ, + .fip_desc_len = htons(2), + .fip_flags = 0, + }; + struct { + struct fip_tlv_mac_addr mac; + } tlvs = { + .mac = { + .hdr = { + .tlv_type = FIP_TLV_MAC_ADDR, + .tlv_len = 2, + }, + }, + }; + struct ethhdr eh; + struct iovec iov[] = { + { .iov_base = &eh, .iov_len = sizeof(eh), }, + { .iov_base = &fh, .iov_len = sizeof(fh), }, + { .iov_base = &tlvs, .iov_len = sizeof(tlvs), }, + }; + struct msghdr msg = { + .msg_name = &sa, + .msg_namelen = sizeof(sa), + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + }; + int rc; + + fip_ethhdr(ifindex, mac, &eh, dest); + sa.sll_addr[ETHER_ADDR_LEN - 1] = dest; + memcpy(tlvs.mac.mac_addr, eh.h_source, ETHER_ADDR_LEN); + FIP_LOG_DBG("sending FIP VLAN request"); + rc = sendmsg(s, &msg, 0); + if (rc < 0) { + rc = -errno; + FIP_LOG_ERRNO("sendmsg error"); + } + return rc; +} + +/** + * fip_send_vlan_notification - send a FIP VLAN notification + * @s: ETH_P_FIP packet socket to send on + * @ifindex: network interface index to send on + * @mac: mac address of the sending network interface + * @dest: destination mac address + * @tlvs: pointer to vlan tlvs to send + * @tlv_count: number of vlan tlvs to send + */ +int +fip_send_vlan_notification(int s, int ifindex, const __u8 *mac, + const __u8 *dest, struct fip_tlv_vlan *vtlvs, + int vlan_count) +{ + struct sockaddr_ll dsa = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_FIP), + .sll_ifindex = ifindex, + .sll_hatype = ARPHRD_ETHER, + .sll_pkttype = PACKET_OTHERHOST, + .sll_halen = ETHER_ADDR_LEN, + }; + struct fiphdr sfh = { + .fip_version = FIP_VERSION(1), + .fip_proto = htons(FIP_PROTO_VLAN), + .fip_subcode = FIP_VLAN_NOTE_VN2VN, + .fip_desc_len = htons(2), + .fip_flags = 0, + }; + struct { + struct fip_tlv_mac_addr mac; + } tlvs = { + .mac = { + .hdr = { + .tlv_type = FIP_TLV_MAC_ADDR, + .tlv_len = 2, + }, + }, + }; + struct ethhdr eh; + struct iovec iov[] = { + { .iov_base = &eh, .iov_len = sizeof(eh), }, + { .iov_base = &sfh, .iov_len = sizeof(sfh), }, + { .iov_base = &tlvs, .iov_len = sizeof(tlvs), }, + { .iov_base = NULL, .iov_len = 0, }, + }; + struct msghdr msg = { + .msg_name = &dsa, + .msg_namelen = sizeof(dsa), + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov) - 1, + }; + int rc; + + if (vlan_count) { + sfh.fip_desc_len = htons(3); + iov[3].iov_base = vtlvs; + iov[3].iov_len = vlan_count * sizeof(*vtlvs); + msg.msg_iovlen = ARRAY_SIZE(iov); + } + + if (fip_get_sanmac(ifindex, eh.h_source)) + memcpy(eh.h_source, mac, ETHER_ADDR_LEN); + eh.h_proto = htons(ETH_P_FIP); + memcpy(dsa.sll_addr, dest, ETHER_ADDR_LEN); + memcpy(eh.h_dest, dest, ETHER_ADDR_LEN); + memcpy(tlvs.mac.mac_addr, eh.h_source, ETHER_ADDR_LEN); + rc = sendmsg(s, &msg, 0); + if (rc < 0) { + rc = -errno; + FIP_LOG_ERR(errno, "%s:sendmsg error\n", __func__); + } else { + FIP_LOG_DBG("%s: sent %d bytes to ifindex %d\n", + __func__, rc, ifindex); + } + return rc; +} + +/** + * fip_recv - receive from a FIP packet socket + * @s: packet socket with data ready to be received + * @fn: FIP receive callback to process the payload + * @arg: argument to pass through to @fn + */ +int fip_recv(int s, fip_handler *fn, void *arg) +{ + char buf[4096]; + struct sockaddr_ll sa; + struct iovec iov[] = { + { .iov_base = buf, .iov_len = sizeof(buf), }, + }; + struct msghdr msg = { + .msg_name = &sa, + .msg_namelen = sizeof(sa), + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + }; + struct fiphdr *fh; + size_t len, desc_len; + int rc; + struct ethhdr *eth = (struct ethhdr *)buf; + + rc = recvmsg(s, &msg, MSG_DONTWAIT); + if (rc < 0) { + FIP_LOG_ERRNO("packet socket recv error"); + return rc; + } + + len = rc; + if (len < sizeof(*fh)) { + FIP_LOG_ERR(EINVAL, "received packed smaller that FIP header"); + return -1; + } + + if (eth->h_proto == htons(ETH_P_8021Q)) + fh = (struct fiphdr *) (buf + sizeof(struct ethhdr) + VLAN_HLEN); + else + fh = (struct fiphdr *) (buf + sizeof(struct ethhdr)); + + desc_len = ntohs(fh->fip_desc_len); + if (len < (sizeof(*fh) + (desc_len << 2))) { + FIP_LOG_ERR(EINVAL, "received data less that FIP descriptor"); + return -1; + } + + if (fn) + return fn(fh, &sa, arg); + return 0; +} diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c new file mode 100644 index 0000000..3b84137 --- /dev/null +++ b/lib/rtnetlink.c @@ -0,0 +1,555 @@ +/* + * Copyright(c) 2010 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rtnetlink.h" +#include "fcoemon_utils.h" + +#define RTNL_LOG(...) sa_log(__VA_ARGS__) +#define RTNL_LOG_ERR(error, ...) sa_log_err(error, __func__, __VA_ARGS__) +#define RTNL_LOG_ERRNO(...) sa_log_err(errno, __func__, __VA_ARGS__) +#define RTNL_LOG_DBG(...) sa_log_debug(__VA_ARGS__) + +#define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN)) + +/** + * rtnl_socket - create and bind a routing netlink socket + */ +int rtnl_socket(unsigned int groups) +{ + struct sockaddr_nl sa = { + .nl_family = AF_NETLINK, + .nl_groups = groups, + }; + int s; + int rc; + + RTNL_LOG_DBG("creating netlink socket"); + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + RTNL_LOG_ERRNO("netlink socket error"); + return s; + } + + rc = bind(s, (struct sockaddr *) &sa, sizeof(sa)); + if (rc < 0) { + RTNL_LOG_ERRNO("netlink bind error"); + close(s); + return rc; + } + + return s; +} + +/** + * send_getlink_dump - send an RTM_GETLINK dump request to list all interfaces + * @s: routing netlink socket to use + */ +ssize_t send_getlink_dump(int s) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + }, + .ifm = { + .ifi_type = ARPHRD_ETHER, + }, + }; + int rc; + + RTNL_LOG_DBG("sending RTM_GETLINK dump request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink sendmsg error"); + + return rc; +} + +#define NLMSG(c) ((struct nlmsghdr *) (c)) + +/** + * rtnl_recv - receive from a routing netlink socket + * @s: routing netlink socket with data ready to be received + * + * Returns: 0 when NLMSG_DONE is received + * <0 on error + * >0 when more data is expected + */ +int rtnl_recv(int s, rtnl_handler *fn, void *arg) +{ + char buf[8192]; + struct nlmsghdr *nh; + size_t len; + int rc = 0; + int ret; + bool more = false; + +more: + ret = recv(s, buf, sizeof(buf), 0); + if (ret < 0) { + RTNL_LOG_ERRNO("netlink recvmsg error"); + return ret; + } + + len = ret; + for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_flags & NLM_F_MULTI) + more = true; + + switch (nh->nlmsg_type) { + case NLMSG_NOOP: + RTNL_LOG_DBG("NLMSG_NOOP"); + break; + case NLMSG_ERROR: + rc = ((struct nlmsgerr *)NLMSG_DATA(nh))->error; + RTNL_LOG_DBG("NLMSG_ERROR (%d) %s", rc, strerror(-rc)); + break; + case NLMSG_DONE: + more = false; + RTNL_LOG_DBG("NLMSG_DONE"); + break; + default: + if (!fn || fn(nh, arg) < 0) + RTNL_LOG("unexpected netlink message type %d", + nh->nlmsg_type); + break; + } + } + if (more) + goto more; + return rc; +} + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +static void add_rtattr(struct nlmsghdr *n, int type, const void *data, int alen) +{ + struct rtattr *rta = NLMSG_TAIL(n); + int len = RTA_LENGTH(alen); + + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); +} + +static struct rtattr *add_rtattr_nest(struct nlmsghdr *n, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + add_rtattr(n, type, NULL, 0); + return nest; +} + +static void end_rtattr_nest(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; +} + +static ssize_t rtnl_send_set_iff_up(int s, int ifindex, char *ifname) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[RTA_SPACE(IFNAMSIZ)]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_SETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + .ifm = { + .ifi_index = ifindex, + .ifi_flags = IFF_UP, + .ifi_change = IFF_UP, + }, + }; + int rc; + + if (ifname) + add_rtattr(&req.nh, IFLA_IFNAME, ifname, strlen(ifname)); + + RTNL_LOG_DBG("sending RTM_SETLINK request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +int rtnl_set_iff_up(int ifindex, char *ifname) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = rtnl_send_set_iff_up(s, ifindex, ifname); + if (rc < 0) + goto out; + rc = rtnl_recv(s, NULL, NULL); +out: + close(s); + return rc; +} + +static ssize_t rtnl_send_set_iff_down(int s, int ifindex, char *ifname) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[RTA_SPACE(IFNAMSIZ)]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_SETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + }, + .ifm = { + .ifi_index = ifindex, + .ifi_flags = 0, + .ifi_change = IFF_UP, + }, + }; + int rc; + + if (ifname) + add_rtattr(&req.nh, IFLA_IFNAME, ifname, strlen(ifname)); + + RTNL_LOG_DBG("sending RTM_SETLINK request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +int rtnl_set_iff_down(int ifindex, char *ifname) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = rtnl_send_set_iff_down(s, ifindex, ifname); + if (rc < 0) + goto out; + rc = rtnl_recv(s, NULL, NULL); +out: + close(s); + return rc; +} + +static ssize_t rtnl_send_vlan_newlink(int s, int ifindex, int vid, char *name) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[1024]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_NEWLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | + NLM_F_EXCL | NLM_F_ACK, + }, + }; + struct rtattr *linkinfo, *data; + int rc; + + add_rtattr(&req.nh, IFLA_LINK, &ifindex, 4); + add_rtattr(&req.nh, IFLA_IFNAME, name, strlen(name)); + linkinfo = add_rtattr_nest(&req.nh, IFLA_LINKINFO); + add_rtattr(&req.nh, IFLA_INFO_KIND, "vlan", strlen("vlan")); + data = add_rtattr_nest(&req.nh, IFLA_INFO_DATA); + add_rtattr(&req.nh, IFLA_VLAN_ID, &vid, 2); + end_rtattr_nest(&req.nh, data); + end_rtattr_nest(&req.nh, linkinfo); + + RTNL_LOG_DBG("sending RTM_NEWLINK request"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +int vlan_create(int ifindex, int vid, char *name) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + + rc = rtnl_send_vlan_newlink(s, ifindex, vid, name); + if (rc < 0) + goto out; + rc = rtnl_recv(s, NULL, NULL); +out: + close(s); + return rc; +} + +static ssize_t rtnl_send_getlink(int s, int ifindex, char *name) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg ifm; + char attrbuf[RTA_SPACE(IFNAMSIZ)]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST, + }, + .ifm = { + .ifi_family = AF_UNSPEC, + .ifi_index = ifindex, + }, + }; + int rc; + + if (!ifindex && !name) + return -1; + + if (name) + add_rtattr(&req.nh, IFLA_IFNAME, name, strlen(name)); + + RTNL_LOG_DBG("sending RTM_GETLINK"); + rc = send(s, &req, req.nh.nlmsg_len, 0); + if (rc < 0) + RTNL_LOG_ERRNO("netlink send error"); + + return rc; +} + +static int rtnl_getlinkname_handler(struct nlmsghdr *nh, void *arg) +{ + char *name = arg; + struct rtattr *ifla[__IFLA_MAX]; + + switch (nh->nlmsg_type) { + case RTM_NEWLINK: + parse_ifinfo(ifla, nh); + strncpy(name, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ); + return 0; + } + return -1; +} + +int rtnl_get_linkname(int ifindex, char *name) +{ + int s; + int rc; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = rtnl_send_getlink(s, ifindex, NULL); + if (rc < 0) + return rc; + rc = rtnl_recv(s, rtnl_getlinkname_handler, name); + if (rc < 0) + goto out; +out: + close(s); + return rc; +} + +struct vlan_identifier { + int ifindex; + int vid; + int found; + unsigned char ifname[IFNAMSIZ]; +}; + +static int rtnl_find_vlan_handler(struct nlmsghdr *nh, void *arg) +{ + struct vlan_identifier *vlan = arg; + struct rtattr *ifla[__IFLA_MAX]; + struct rtattr *linkinfo[__IFLA_INFO_MAX]; + struct rtattr *vlaninfo[__IFLA_VLAN_MAX]; + + switch (nh->nlmsg_type) { + case RTM_NEWLINK: + parse_ifinfo(ifla, nh); + if (!ifla[IFLA_LINK]) + break; + if (vlan->ifindex != *(int *)RTA_DATA(ifla[IFLA_LINK])) + break; + if (!ifla[IFLA_LINKINFO]) + break; + parse_linkinfo(linkinfo, ifla[IFLA_LINKINFO]); + if (!linkinfo[IFLA_INFO_KIND]) + break; + if (strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vlan")) + break; + if (!linkinfo[IFLA_INFO_DATA]) + break; + parse_vlaninfo(vlaninfo, linkinfo[IFLA_INFO_DATA]); + if (!vlaninfo[IFLA_VLAN_ID]) + break; + if (vlan->vid != *(int *)RTA_DATA(vlaninfo[IFLA_VLAN_ID])) + break; + if (!ifla[IFLA_IFNAME]) + break; + vlan->found = 1; + memcpy(vlan->ifname, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ); + } + return 0; +} + +int rtnl_find_vlan(int ifindex, int vid, char *ifname) +{ + int s; + int rc; + struct vlan_identifier vlan = { + .ifindex = ifindex, + .vid = vid, + .found = 0, + }; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (s < 0) + return s; + rc = send_getlink_dump(s); + if (rc < 0) + goto out; + rc = rtnl_recv(s, rtnl_find_vlan_handler, &vlan); + if (rc < 0) + goto out; + if (vlan.found) { + memcpy(ifname, vlan.ifname, IFNAMSIZ); + rc = 0; + } else { + rc = -ENODEV; + } +out: + close(s); + return rc; +} + +int rtnl_get_sanmac(const char *ifname, unsigned char *addr) +{ + int s; + int rc = -EIO; + struct { + struct nlmsghdr nh; + struct dcbmsg dcb; + char attrbuf[1204]; + } req = { + .nh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct dcbmsg)), + .nlmsg_type = RTM_GETDCB, + .nlmsg_pid = getpid(), + .nlmsg_flags = NLM_F_REQUEST, + }, + .dcb = { + .cmd = DCB_CMD_GPERM_HWADDR, + .dcb_family = AF_UNSPEC, + .dcb_pad = 0, + }, + }; + + struct nlmsghdr *nh = &req.nh; + struct dcbmsg *dcb; + struct rtattr *rta; + + /* prep the message */ + memset((void *)req.attrbuf, 0, sizeof(req.attrbuf)); + add_rtattr(nh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); + add_rtattr(nh, DCB_ATTR_PERM_HWADDR, NULL, 0); + + s = rtnl_socket(0); + if (s < 0) { + RTNL_LOG_ERRNO("failed to create the socket"); + return s; + } + + rc = send(s, (void *)nh, nh->nlmsg_len, 0); + if (rc < 0) { + RTNL_LOG_ERRNO("failed to send to the socket"); + goto err_close; + } + + memset((void *)&req, 0, sizeof(req)); + rc = recv(s, (void *)&req, sizeof(req), 0); + if (rc < 0) { + RTNL_LOG_ERRNO("failed to recv from the socket"); + rc = -EIO; + goto err_close; + } + + if (nh->nlmsg_type != RTM_GETDCB) { + RTNL_LOG_DBG("Ignoring netlink msg %x\n", nh->nlmsg_type); + rc = -EIO; + goto err_close; + } + dcb = (struct dcbmsg *)NLMSG_DATA(nh); + if (dcb->cmd != DCB_CMD_GPERM_HWADDR) { + RTNL_LOG_DBG("Unexpected response for DCB command %x\n", + dcb->cmd); + rc = -EIO; + goto err_close; + } + rta = (struct rtattr *)(((char *)dcb) + + NLMSG_ALIGN(sizeof(struct dcbmsg))); + if (rta->rta_type != DCB_ATTR_PERM_HWADDR) { + RTNL_LOG_DBG("Unexpected DCB RTA attr %x\n", rta->rta_type); + rc = -EIO; + goto err_close; + } + /* SAN MAC follows the LAN MAC */ + memcpy(addr, NLA_DATA(rta) + ETH_ALEN, ETH_ALEN); + rc = 0; +err_close: + close(s); + return rc; +} diff --git a/lib/sa_log.c b/lib/sa_log.c new file mode 100644 index 0000000..3659e44 --- /dev/null +++ b/lib/sa_log.c @@ -0,0 +1,271 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#define _GNU_SOURCE /* for GNU definition of strerror_r */ +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +int use_syslog; +static int debug; + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +void enable_syslog(int enable) +{ + use_syslog = enable; +} + +void enable_debug_log(int enable) +{ + debug = enable; +} + +/* + * log with a variable argument list. + */ +static void +sa_log_va(const char *func, const char *format, va_list arg) +{ + size_t len; + size_t flen; + int add_newline; + char sa_buf[SA_LOG_BUF_LEN]; + char *bp; + + /* + * If the caller didn't provide a newline at the end, we will. + */ + len = strlen(format); + add_newline = 0; + if (!len || format[len - 1] != '\n') + add_newline = 1; + bp = sa_buf; + len = sizeof(sa_buf); + if (func) { + flen = snprintf(bp, len, "%s: ", func); + len -= flen; + bp += flen; + } + flen = vsnprintf(bp, len, format, arg); + if (add_newline && flen < len) { + bp += flen; + *bp++ = '\n'; + *bp = '\0'; + } + sa_log_output(sa_buf); +} + +/* + * log + */ +void +sa_log(const char *format, ...) +{ + va_list arg; + + va_start(arg, format); + sa_log_va(NULL, format, arg); + va_end(arg); +} + +/* + * debug log, controlled by static debug flag + */ +void +sa_log_debug(const char *format, ...) +{ + va_list arg; + + if (!debug) + return; + + va_start(arg, format); + sa_log_va(NULL, format, arg); + va_end(arg); +} + +/* + * log with error number. + */ +void +sa_log_err(int error, const char *func, const char *format, ...) +{ + va_list arg; + char buf[SA_LOG_BUF_LEN]; + + if (func) + sa_log("%s: error %d %s", func, error, + strerror_r(error, buf, sizeof(buf))); + else + sa_log("error %d %s", error, + strerror_r(error, buf, sizeof(buf))); + va_start(arg, format); + sa_log_va(func, format, arg); + va_end(arg); +} + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +/* + * Assert failures. + */ +void +assert_failed(const char *format, ...) +{ + va_list arg; + char buf[SA_LOG_BUF_LEN]; + + va_start(arg, format); + vsnprintf(buf, sizeof(buf), format, arg); + va_end(arg); + sa_log_abort(buf); +} + +/* + * Log options. + * These may be set directly by callers. + */ +u_int sa_log_flags; /* timestamp and other option flags */ +int sa_log_time_delta_min = 1; /* minimum diff to print in millisec */ +char *sa_log_prefix; /* string to print before any message */ + +/* + * Put timestamp on front of each log line, as controlled by tunables above. + */ +static void +sa_log_timestamp(void) +{ + static struct timeval tlast; + char ctime_buf[30]; + struct timeval t; + struct timeval diff; + + gettimeofday(&t, NULL); + if (sa_log_flags & SA_LOGF_TIME) { + ctime_r(&t.tv_sec, ctime_buf); + ctime_buf[11 + 8] = '\0'; /* trim ctime after seconds */ + fprintf(stderr, "%s.%3.3ld ", + ctime_buf + 11, t.tv_usec / 1000); + } + if (sa_log_flags & SA_LOGF_DELTA) { + if (tlast.tv_sec == 0) + tlast = t; + timersub(&t, &tlast, &diff); + tlast = t; + if (diff.tv_sec != 0 || + diff.tv_usec >= sa_log_time_delta_min * 1000) + fprintf(stderr, "%4ld.%3.3ld ", + diff.tv_sec, diff.tv_usec / 1000); + else + fprintf(stderr, "%8s ", ""); + } + if (sa_log_prefix) + fprintf(stderr, "%s: ", sa_log_prefix); +} + +void +sa_log_output(const char *buf) +{ + if (use_syslog) { + syslog(LOG_INFO, "%s", buf); + return; + } + sa_log_timestamp(); + fprintf(stderr, "%s", buf); + fflush(stderr); +} + +void +sa_log_abort(const char *buf) +{ + sa_log_output(buf); + abort(); +} + +/* + * Make a printable NUL-terminated copy of the string. + * The source buffer might not be NUL-terminated. + */ +char * +sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len) +{ + char *dp = dest; + const char *sp = src; + + while (len-- > 1 && src_len-- > 0 && *sp != '\0') { + *dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.'); + sp++; + } + *dp = '\0'; + + /* + * Take off trailing blanks. + */ + while (--dp >= dest && isspace(*dp)) + *dp = '\0'; + return dest; +} + +/** sa_enum_decode(buf, len, tp, val) + * + * @param buf buffer for result (may be used or not). + * @param len size of buffer (at least 32 bytes recommended). + * @param tp pointer to table of names and values, struct sa_nameval. + * @param val value to be decoded into a name. + * @returns pointer to name string and in buf. Unknown values are put into buffer in hex. + */ +const char * +sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val) +{ + for (; tp->nv_name != NULL; tp++) { + if (tp->nv_val == val) { + snprintf(buf, len, "%s", tp->nv_name); + return tp->nv_name; + } + } + snprintf(buf, len, "Unknown (code 0x%X)", val); + return buf; +} + +/** sa_enum_encode(tp, name, valp) + * + * @param tp pointer to table of names and values, struct sa_nameval. + * @param name string to be encoded into a value + * @returns zero on success, non-zero if no matching string found. + */ +int +sa_enum_encode(const struct sa_nameval *tp, const char *name, u_int32_t *valp) +{ + for (; tp->nv_name != NULL; tp++) { + if (strcasecmp(tp->nv_name, name) == 0) { + *valp = tp->nv_val; + return 0; + } + } + return -1; +} diff --git a/lib/sa_other.c b/lib/sa_other.c new file mode 100644 index 0000000..af53a54 --- /dev/null +++ b/lib/sa_other.c @@ -0,0 +1,69 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +/* + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. + */ +fc_wwn_t +fc_wwn_from_mac(u_int64_t mac, u_int scheme, u_int port) +{ + fc_wwn_t wwn; + + ASSERT(mac < (1ULL << 48)); + wwn = mac | ((fc_wwn_t) scheme << 60); + switch (scheme) { + case 1: + ASSERT(port == 0); + break; + case 2: + ASSERT(port < 0xfff); + wwn |= (fc_wwn_t) port << 48; + break; + default: + ASSERT_NOTREACHED; + break; + } + return wwn; +} + +/* assumes input is pointer to two hex digits */ +/* returns -1 on error */ +int +hex2int(char *b) +{ + int i; + int n = 0; + int m; + + for (i = 0, m = 1; i < 2; i++, m--) { + if (isxdigit(*(b+i))) { + if (*(b+i) <= '9') + n |= (*(b+i) & 0x0f) << (4*m); + else + n |= ((*(b+i) & 0x0f) + 9) << (4*m); + } else + return -1; + } + return n; +} + diff --git a/lib/sa_select.c b/lib/sa_select.c new file mode 100644 index 0000000..80f6454 --- /dev/null +++ b/lib/sa_select.c @@ -0,0 +1,222 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +#define NFC_NFDS 64 + +/* +* Static module state. +*/ +static struct sa_sel_state { + fd_set ts_rx_fds; + fd_set ts_tx_fds; + fd_set ts_ex_fds; + int ts_max_fd; + int ts_sig; + struct sa_sel_fd { + void (*ts_rx_handler)(void *); + void (*ts_tx_handler)(void *); + void (*ts_ex_handler)(void *); + void *ts_handler_arg; + } ts_fd[NFC_NFDS]; + void (*ts_callback)(void); +} sa_sel_state; + +/** + * sa_select_loop() - listens to registered descriptors + * + * Return value: + * -1 on failure or interrupted signal number + */ +int sa_select_loop(void) +{ + struct sa_sel_state *ss = &sa_sel_state; + struct sa_sel_fd *fp; + fd_set rx_fds; + fd_set tx_fds; + fd_set ex_fds; + struct timeval tval; + struct timeval *tvp; + int rv, i; + + ss->ts_sig = 0; + while (ss->ts_sig == 0) { + sa_timer_check(&tval); + if (ss->ts_sig) + break; + if (tval.tv_sec == 0 && tval.tv_usec == 0) + tvp = NULL; + else + tvp = &tval; + rx_fds = ss->ts_rx_fds; + tx_fds = ss->ts_tx_fds; + ex_fds = ss->ts_ex_fds; + rv = select(ss->ts_max_fd + 1, &rx_fds, &tx_fds, &ex_fds, tvp); + if (rv == -1) { + if (errno == EINTR) + continue; + return rv; + } + + fp = ss->ts_fd; + for (i = 0; rv > 0 && i <= sa_sel_state.ts_max_fd; i++, fp++) { + if (FD_ISSET(i, &rx_fds)) { + if (fp->ts_rx_handler != NULL) + (*fp->ts_rx_handler) + (fp->ts_handler_arg); + else { + ASSERT(!FD_ISSET(i, &ss->ts_rx_fds)); + } + --rv; + } + if (FD_ISSET(i, &tx_fds)) { + if (fp->ts_tx_handler != NULL) + (*fp->ts_tx_handler) + (fp->ts_handler_arg); + else { + ASSERT(!FD_ISSET(i, &ss->ts_tx_fds)); + } + --rv; + } + if (FD_ISSET(i, &ex_fds)) { + if (fp->ts_ex_handler != NULL) + (*fp->ts_ex_handler) + (fp->ts_handler_arg); + else { + ASSERT(!FD_ISSET(i, &ss->ts_ex_fds)); + } + --rv; + } + } + if (ss->ts_callback != NULL) + (*ss->ts_callback)(); + } + return ss->ts_sig; +} + +void +sa_select_add_fd(int fd, + void (*rx_handler)(void *), + void (*tx_handler)(void *), + void (*ex_handler)(void *), + void *arg) +{ + struct sa_sel_state *ss = &sa_sel_state; + struct sa_sel_fd *fp; + + ASSERT_NOTIMPL(fd < NFC_NFDS); + ASSERT(rx_handler != NULL || tx_handler != NULL || ex_handler != NULL); + if (ss->ts_max_fd < fd) + ss->ts_max_fd = fd; + fp = &ss->ts_fd[fd]; + fp->ts_handler_arg = arg; + if (rx_handler != NULL) { + fp->ts_rx_handler = rx_handler; + FD_SET(fd, &ss->ts_rx_fds); + } + if (tx_handler != NULL) { + fp->ts_tx_handler = tx_handler; + FD_SET(fd, &ss->ts_tx_fds); + } + if (ex_handler != NULL) { + fp->ts_ex_handler = ex_handler; + FD_SET(fd, &ss->ts_ex_fds); + } +} + +void +sa_select_set_rx(int fd, void (*handler)(void *)) +{ + struct sa_sel_state *ss = &sa_sel_state; + + ASSERT(fd <= ss->ts_max_fd); + ss->ts_fd[fd].ts_rx_handler = handler; + if (handler != NULL) + FD_SET(fd, &ss->ts_rx_fds); + else + FD_CLR(fd, &ss->ts_rx_fds); +} + +void +sa_select_set_tx(int fd, void (*handler)(void *)) +{ + struct sa_sel_state *ss = &sa_sel_state; + + ASSERT(fd <= ss->ts_max_fd); + ss->ts_fd[fd].ts_tx_handler = handler; + if (handler != NULL) + FD_SET(fd, &ss->ts_tx_fds); + else + FD_CLR(fd, &ss->ts_tx_fds); +} + +void +sa_select_set_ex(int fd, void (*handler)(void *)) +{ + struct sa_sel_state *ss = &sa_sel_state; + + ASSERT(fd <= ss->ts_max_fd); + ss->ts_fd[fd].ts_ex_handler = handler; + if (handler != NULL) + FD_SET(fd, &ss->ts_ex_fds); + else + FD_CLR(fd, &ss->ts_ex_fds); +} + +void +sa_select_rem_fd(int fd) +{ + struct sa_sel_state *ss = &sa_sel_state; + struct sa_sel_fd *fp; + + ASSERT_NOTIMPL(fd < NFC_NFDS); + FD_CLR(fd, &ss->ts_rx_fds); + FD_CLR(fd, &ss->ts_tx_fds); + FD_CLR(fd, &ss->ts_ex_fds); + fp = &ss->ts_fd[fd]; + fp->ts_rx_handler = NULL; + fp->ts_tx_handler = NULL; + fp->ts_ex_handler = NULL; + fp->ts_handler_arg = NULL; +} + +/* + * Set callback for every time through the select loop. + */ +void +sa_select_set_callback(void (*cb)(void)) +{ + sa_sel_state.ts_callback = cb; +} + +/* + * Cause select loop to exit. + * This is invoked from a handler which wants the select loop to return + * after the handler is finished. For example, during receipt of a network + * packet, the program may decide to clean up and exit, but in order to do + * this cleanly, all lower-level protocol handlers should return first. + */ +void +sa_select_exit(int sig) +{ + sa_sel_state.ts_sig = sig; +} diff --git a/lib/sa_sys.c b/lib/sa_sys.c new file mode 100644 index 0000000..9c6818f --- /dev/null +++ b/lib/sa_sys.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "fcoemon_utils.h" + +#include + +/* + * Read a line from the specified file in the specified directory + * into the buffer. The file is opened and closed. + * Any trailing white space is trimmed off. + * This is useful for accessing /sys files. + * Returns 0 or an error number. + */ +int +sa_sys_read_line(const char *dir, const char *file, char *buf, size_t len) +{ + FILE *fp; + char file_name[256]; + char *cp; + int rc = 0; + + snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); + fp = fopen(file_name, "r"); + if (fp == NULL) + rc = -1; + else { + cp = fgets(buf, len, fp); + if (cp == NULL) { + fprintf(stderr, + "%s: read error or empty file %s," + " errno=0x%x\n", __func__, + file_name, errno); + rc = -1; + } else { + + /* + * Trim off trailing newline or other white space. + */ + cp = buf + strlen(buf); + while (--cp >= buf && isspace(*cp)) + *cp = '\0'; + } + fclose(fp); + } + return rc; +} + +/* + * Write a string to the specified file in the specified directory. + * The file is opened and closed. + * The string has a new-line appended to it. + * This is useful for accessing /sys files. + * Returns 0 or an error number. + */ +int +sa_sys_write_line(const char *dir, const char *file, const char *string) +{ + FILE *fp; + char file_name[256]; + int rc = 0; + + snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); + fp = fopen(file_name, "w"); + if (fp == NULL) { + fprintf(stderr, "%s: fopen of %s failed, errno=0x%x\n", + __func__, file_name, errno); + rc = -1; + } else { + rc = fprintf(fp, "%s\n", string); + if (rc < 0) + fprintf(stderr, + "%s: write to %s of %s failed, errno=0x%x\n", + __func__, file_name, string, errno); + fclose(fp); + } + return rc; +} + +int sa_sys_read_int(const char *dir, const char *file, int *vp) +{ + char buf[256]; + int rc; + long val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc) + return rc; + + val = strtol(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + return -1; + } + if (val > INT_MAX || val < INT_MIN) + return ERANGE; + + *vp = val; + return 0; +} + +int +sa_sys_read_u32(const char *dir, const char *file, u_int32_t *vp) +{ + char buf[256]; + int rc; + u_int32_t val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + val = strtoul(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + rc = -1; + } else + *vp = val; + } + return rc; +} + +int +sa_sys_read_u64(const char *dir, const char *file, u_int64_t *vp) +{ + char buf[256]; + int rc; + u_int64_t val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + val = strtoull(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + rc = -1; + } else + *vp = val; + } + return rc; +} + +/* + * Read through a directory and call a function for each entry. + */ +int +sa_dir_read(char *dir_name, int (*func)(struct dirent *dp, void *), void *arg) +{ + DIR *dir; + struct dirent *dp; + int error = 0; + + dir = opendir(dir_name); + if (dir == NULL) + error = errno; + else { + while ((dp = readdir(dir)) != NULL && error == 0) { + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + error = (*func)(dp, arg); + } + closedir(dir); + } + return error; +} + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +static const u_int32_t sa_table_growth = 16; /* entries to grow by */ + +/** sa_table_grow(tp, index) - add space to a table for index. + * + * @param tp pointer to sa_table structure. + * @param index - new index past the end of the current table. + * @returns new index, or -1 if table couldn't be grown. + * + * Note: if the table has never been used, and is still all zero, this works. + * + * Note: perhaps not safe for multithreading. Caller can lock the table + * externally, but reallocation can take a while, during which time the + * caller may not wish to hold the lock. + */ +int +sa_table_grow(struct sa_table *tp, u_int32_t index) +{ + u_int32_t new_size; + void **ap; + + if (index >= tp->st_size) { + new_size = index + sa_table_growth; + ap = realloc(tp->st_table, new_size * sizeof(*ap)); + if (ap == NULL) + return -1; + memset(ap + tp->st_size, 0, + (new_size - tp->st_size) * sizeof(*ap)); + tp->st_table = ap; + tp->st_size = new_size; + } + tp->st_limit = index + 1; + return index; +} + +/** sa_table_destroy(tp) - free memory used by table. + * + * @param tp pointer to sa_table structure. + */ +void +sa_table_destroy(struct sa_table *tp) +{ + if (tp->st_table) { + free(tp->st_table); + tp->st_table = NULL; + } + tp->st_limit = 0; + tp->st_size = 0; +} + +/** sa_table_destroy_all(tp) - free memory used by table, including entries. + * + * @param tp pointer to sa_table structure. + */ +void +sa_table_destroy_all(struct sa_table *tp) +{ + unsigned int i; + + if (tp->st_table) { + for (i = 0; i < tp->st_limit; i++) { + if (tp->st_table[i]) { + free(tp->st_table[i]); + tp->st_table[i] = NULL; + } + } + } + sa_table_destroy(tp); +} + +/** sa_table_iterate(tp, handler, arg) + * + * @param tp pointer to sa_table structure. + * @param handler function to be called for each non-NULL entry. + * @param arg argument for function. + */ +void +sa_table_iterate(struct sa_table *tp, + void (*handler)(void *ep, void *arg), + void *arg) +{ + unsigned int i; + void *ep; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL) + (*handler)(ep, arg); + } +} + +/** sa_table_search(tp, match, arg) + * + * @param tp pointer to sa_table structure. + * @param match function to compare entries with arg and + * return non-NULL if match. + * @param arg argument for match function. + * + * Note that the value found could actually be something not in the table + * if the match function is doing something clever, like returning a + * sub-structure of the table entry. + */ +void * +sa_table_search(struct sa_table *tp, void *(*match)(void *ep, void *arg), + void *arg) +{ + unsigned int i; + void *found = NULL; + void *ep; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL) { + found = (*match)(ep, arg); + if (found != NULL) + break; + } + } + return found; +} diff --git a/lib/sa_timer.c b/lib/sa_timer.c new file mode 100644 index 0000000..aee213f --- /dev/null +++ b/lib/sa_timer.c @@ -0,0 +1,226 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "fcoemon_utils.h" +#include "net_types.h" +#include "fc_types.h" + +#define SA_TIMER_HZ (1000 * 1000 * 1000ULL) /* nanoseconds per second */ +#define SA_TIMER_FUZZ (500 * 1000ULL) /* 500 microseconds is close enough */ + +static struct sa_timer *sa_timer_head; /* queue of scheduled events */ +static u_int64_t sa_timer_nsec; /* nanoseconds since start */ + +/* + * Initialize a timer structure. Set handler. + */ +void +sa_timer_init(struct sa_timer *tm, void (*handler)(void *), void *arg) +{ + ASSERT(handler != NULL); + memset(tm, 0, sizeof(*tm)); + tm->tm_handler = handler; + tm->tm_handler_arg = arg; +} + +/* + * Allocate a timer structure. Set handler. + */ +struct sa_timer * +sa_timer_alloc(void (*handler)(void *arg), void *arg) +{ + struct sa_timer *tm; + + tm = malloc(sizeof(*tm)); + if (tm) + sa_timer_init(tm, handler, arg); + return tm; +} + +u_int64_t +sa_timer_get(void) +{ + u_int64_t nsec; +#ifndef _POSIX_TIMERS + struct timeval tv; + + gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */ + nsec = tv.tv_sec * SA_TIMER_HZ + tv.tv_usec * 1000; +#else /* _POSIX_TIMERS */ + struct timespec ts; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + ASSERT_NOTIMPL(rc == 0); + nsec = ts.tv_sec * SA_TIMER_HZ + ts.tv_nsec; +#endif /* _POSIX_TIMERS */ + +#if 0 /* XXX */ + ASSERT(nsec >= sa_timer_nsec); /* really must be monotonic */ +#else + if (nsec < sa_timer_nsec) + sa_log("sa_timer_get: negative time lapse " + "old %qud new %qud diff %qd nsec\n", + (long long unsigned int) sa_timer_nsec, + (long long unsigned int) nsec, + (long long int) (nsec - sa_timer_nsec)); +#endif + sa_timer_nsec = nsec; + return nsec; +} + +/* + * Get monotonic time since some arbitrary time in the past. + * If _POSIX_MONOTONIC_CLOCK isn't available, we'll use time of day. + */ +u_int +sa_timer_get_secs(void) +{ + u_int sec; + +#ifndef _POSIX_TIMERS + struct timeval tv; + + gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */ + sec = tv.tv_sec; +#else /* _POSIX_TIMERS */ + struct timespec ts; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + ASSERT_NOTIMPL(rc == 0); + sec = ts.tv_sec; +#endif /* _POSIX_TIMERS */ + return sec; +} + +/* + * Set timer to fire. Delta is in microseconds from now. + */ +void +sa_timer_set(struct sa_timer *tm, u_long delta_usec) +{ + struct sa_timer *cur; + struct sa_timer **prev; + + ASSERT(delta_usec != 0); + ASSERT(tm->tm_handler != NULL); + sa_timer_cancel(tm); + ASSERT(sa_timer_active(tm) == 0); + tm->tm_nsec = + sa_timer_get() + delta_usec * SA_TIMER_HZ / SA_TIMER_UNITS; + ASSERT(tm->tm_nsec != 0); + + /* + * Insert timer into sorted linked list. + * Find insertion point, before cur. + */ + for (prev = &sa_timer_head; + (cur = *prev) != NULL && cur->tm_nsec <= tm->tm_nsec; + prev = &cur->tm_next) + ; + *prev = tm; + tm->tm_next = cur; +} + +/* + * Cancel timer if it is active. + */ +void +sa_timer_cancel(struct sa_timer *tm) +{ + struct sa_timer *cur; + struct sa_timer **prev; + + if (sa_timer_active(tm)) { + for (prev = &sa_timer_head; (cur = *prev) != NULL; + prev = &cur->tm_next) + if (cur == tm) { + tm->tm_nsec = 0; + *prev = tm->tm_next; + break; + } + ASSERT(cur == tm); + } +} + +/* + * Free (and cancel) timer. + */ +void +sa_timer_free(struct sa_timer *tm) +{ + if (sa_timer_active(tm)) + sa_timer_cancel(tm); + free(tm); +} + +/* + * Handle timer checks. Called from select loop or other periodic function. + * + * The struct timeval is set before returning to the maximum amount of time + * that should elapse before the next call. + * + * Returns 1 if any timer functions were called, 0 otherwise. + */ +int +sa_timer_check(struct timeval *tv) +{ + u_int64_t now = 0; + u_int64_t next_due = 0; + struct sa_timer *tm; + int ret = 0; + + /* + * Remember, the list may change during the handler. + */ + for (;;) { + now = sa_timer_get(); + tm = sa_timer_head; + if (tm == NULL) { + next_due = now; + break; + } + + next_due = tm->tm_nsec; + if (next_due > now + SA_TIMER_FUZZ) + break; + + /* + * Remove this element from the list. + */ + sa_timer_head = tm->tm_next; + tm->tm_next = NULL; + + /* + * Mark cancelled and call handler. + */ + tm->tm_nsec = 0; + ASSERT(tm->tm_handler != NULL); + (*tm->tm_handler)(tm->tm_handler_arg); + ret = 1; + } + + ASSERT(next_due >= now); + next_due -= now; + tv->tv_sec = (time_t) (next_due / SA_TIMER_HZ); + tv->tv_usec = (long) (next_due % SA_TIMER_HZ) / 1000; + + return ret; +} diff --git a/lib/sysfs_hba.c b/lib/sysfs_hba.c new file mode 100644 index 0000000..5cb7fd3 --- /dev/null +++ b/lib/sysfs_hba.c @@ -0,0 +1,699 @@ +/* + * Copyright (c) 2008, Intel Corporation. + * Copyright(c) 2015 SUSE GmbH. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * Maintained at www.Open-FCoE.org + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysfs_hba.h" +#include "fcoemon_utils.h" +#include "net_types.h" + +#define hton64(p, v) \ + do { \ + p[0] = (u_char) ((v) >> 56) & 0xFF; \ + p[1] = (u_char) ((v) >> 48) & 0xFF; \ + p[2] = (u_char) ((v) >> 40) & 0xFF; \ + p[3] = (u_char) ((v) >> 32) & 0xFF; \ + p[4] = (u_char) ((v) >> 24) & 0xFF; \ + p[5] = (u_char) ((v) >> 16) & 0xFF; \ + p[6] = (u_char) ((v) >> 8) & 0xFF; \ + p[7] = (u_char) (v) & 0xFF; \ + } while (0) + +struct port_attributes *get_port_attribs_by_device(char *path) +{ + char link[1024]; + char *host; + char *rport; + int ret; + + ret = readlink(path, link, sizeof(link)); + if (ret == -1) + return NULL; + + host = strstr(link, "host"); + rport = strstr(link, "rport"); + + host[strlen(host) - strlen(rport) - 1] = '\0'; + + return get_port_attribs(host); +} + +struct port_attributes *get_rport_attribs_by_device(char *path) +{ + char link[1024]; + char *target; + char *rport; + int ret; + + ret = readlink(path, link, sizeof(link)); + if (ret == -1) + return NULL; + + target = strstr(link, "target"); + rport = strstr(link, "rport"); + + rport[strlen(rport) - strlen(target) - 1] = '\0'; + + return get_rport_attribs(rport); +} + +static void get_device_serial_number(struct pci_device *dev, + struct hba_info *info) +{ + uint32_t pcie_cap_header; + uint32_t dword_high = 0; + uint32_t dword_low = 0; + uint16_t pcie_cap_id; + pciaddr_t offset; + uint16_t status; + uint8_t cap_ptr; + int rc; + + /* Default */ + snprintf(info->serial_number, sizeof(info->serial_number), "Unknown"); + + /* + * Read the Status Regiser in the PCIe configuration + * header space to see if the PCI Capability List is + * supported by this device. + */ + rc = pci_device_cfg_read_u16(dev, &status, PCI_STATUS); + if (rc) { + fprintf(stderr, "Failed reading PCI status register\n"); + return; + } + + if (!(status & PCI_STATUS_CAP_LIST)) { + fprintf(stderr, "PCI capabilities are not supported\n"); + return; + } + + /* + * Read the offset (cap_ptr) of first entry in the capability list in + * the PCI configuration space. + */ + rc = pci_device_cfg_read_u8(dev, &cap_ptr, PCI_CAPABILITY_LIST); + if (rc) { + fprintf(stderr, + "Failed reading PCI Capability List Register\n"); + return; + } + offset = cap_ptr; + + /* Search for the PCIe capability */ + while (offset) { + uint8_t next_cap; + uint8_t cap_id; + + rc = pci_device_cfg_read_u8(dev, &cap_id, + offset + PCI_CAP_LIST_ID); + if (rc) { + fprintf(stderr, + "Failed reading capability ID at 0x%"PRIx64"\n", + offset + PCI_CAP_LIST_ID); + return; + } + + if (cap_id != PCI_CAP_ID_EXP) { + rc = pci_device_cfg_read_u8(dev, &next_cap, + offset + PCI_CAP_LIST_NEXT); + if (rc) { + fprintf(stderr, + "Failed reading next capability ID at 0x%"PRIx64"\n", + offset + PCI_CAP_LIST_NEXT); + return; + } + + offset = (pciaddr_t) next_cap; + continue; + } + + /* + * PCIe Capability Structure exists! + */ + + /* + * The first PCIe extended capability is located at + * offset 0x100 in the device configuration space. + */ + offset = 0x100; + do { + rc = pci_device_cfg_read_u32(dev, &pcie_cap_header, + offset); + if (rc) { + fprintf(stderr, + "Failed reading PCIe config header\n"); + return; + } + + /* Get the PCIe Extended Capability ID */ + pcie_cap_id = pcie_cap_header & 0xffff; + + if (pcie_cap_id != PCI_EXT_CAP_ID_DSN) { + /* Get the offset of the next capability */ + offset = (pciaddr_t) pcie_cap_header >> 20; + continue; + } + + /* + * Found the serial number register! + */ + + (void) pci_device_cfg_read_u32(dev, &dword_low, + offset + 4); + (void) pci_device_cfg_read_u32(dev, &dword_high, + offset + 8); + snprintf(info->serial_number, + sizeof(info->serial_number), + "%02X%02X%02X%02X%02X%02X\n", + dword_high >> 24, (dword_high >> 16) & 0xff, + (dword_high >> 8) & 0xff, + (dword_low >> 16) & 0xff, + (dword_low >> 8) & 0xff, dword_low & 0xff); + break; + } while (offset); + + break; + } +} + +static void get_pci_device_info(struct pci_device *dev, struct hba_info *info) +{ + char *unknown = "unknown"; + const char *vname; + const char *dname; + uint8_t revision; + + vname = pci_device_get_vendor_name(dev); + if (!vname) + vname = unknown; + + strncpy(info->manufacturer, vname, sizeof(info->manufacturer)); + + dname = pci_device_get_device_name(dev); + if (!dname) + dname = unknown; + + strncpy(info->model_description, dname, + sizeof(info->model_description)); + + pci_device_cfg_read_u8(dev, &revision, PCI_REVISION_ID); + snprintf(info->hardware_version, sizeof(info->hardware_version), + "%02x", revision); + + info->nports = 1; + + get_device_serial_number(dev, info); +} + +static void get_module_info(const char *pcidev, struct hba_info *info) +{ + char buf[1024]; + char *path; + int err; + + strncpy(info->driver_name, "Unknown", sizeof(info->driver_name)); + strncpy(info->driver_version, "Unknown", sizeof(info->driver_version)); + + err = asprintf(&path, "/sys/bus/pci/devices/%s/driver/module", pcidev); + if (err == -1) + return; + + sa_sys_read_line(path, "version", + info->driver_version, sizeof(info->driver_version)); + + err = readlink(path, buf, sizeof(buf) - 1); + free(path); + if (err == -1) + return; + + buf[err] = '\0'; + + if (strstr(buf, "module")) + strncpy(info->driver_name, + strstr(buf, "module") + strlen("module") + 1, + sizeof(info->driver_name)); + +} + +struct hba_info *get_hbainfo_by_pcidev(const char *pcidev) +{ + struct pci_device_iterator *iterator; + struct pci_slot_match match; + struct pci_device *dev; + struct hba_info *info; + int rc; + + rc = pci_system_init(); + if (rc) + return NULL; + + info = calloc(1, sizeof(struct hba_info)); + if (!info) + return NULL; + + sscanf(pcidev, "%x:%x:%x.%x", &match.domain, &match.bus, &match.dev, + &match.func); + + iterator = pci_slot_match_iterator_create(&match); + if (!iterator) { + free(info); + return NULL; + } + + for (;;) { + dev = pci_device_next(iterator); + if (!dev) + break; + get_pci_device_info(dev, info); + get_module_info(pcidev, info); + } + + free(iterator); + pci_system_cleanup(); + + return info; +} + +struct port_attributes *get_rport_attribs(const char *rport) +{ + struct port_attributes *pa; + char *path; + int err; + + err = asprintf(&path, "%s/%s", SYSFS_RPORT_DIR, rport); + if (err == -1) + return NULL; + + pa = calloc(1, sizeof(*pa)); + if (!pa) + goto free_path; + + strncpy(pa->device_name, rport, sizeof(pa->device_name)); + sa_sys_read_line(path, "node_name", pa->node_name, + sizeof(pa->node_name)); + sa_sys_read_line(path, "port_name", pa->port_name, + sizeof(pa->port_name)); + sa_sys_read_line(path, "port_id", pa->port_id, sizeof(pa->port_id)); + sa_sys_read_line(path, "scsi_target_id", pa->scsi_target_id, + sizeof(pa->scsi_target_id)); + sa_sys_read_line(path, "maxframe_size", pa->maxframe_size, + sizeof(pa->maxframe_size)); + sa_sys_read_line(path, "port_state", pa->port_state, + sizeof(pa->port_state)); + sa_sys_read_line(path, "supported_classes", pa->supported_classes, + sizeof(pa->supported_classes)); + sa_sys_read_line(path, "roles", pa->roles, sizeof(pa->roles)); + +free_path: + free(path); + + return pa; +} + +struct port_statistics *get_port_statistics(const char *host) +{ + struct port_statistics *ps; + char *path; + int err; + + err = asprintf(&path, "%s/%s/statistics/", SYSFS_HOST_DIR, host); + if (err == -1) + return NULL; + + ps = calloc(1, sizeof(*ps)); + if (!ps) + goto free_path; + + sa_sys_read_u64(path, "seconds_since_last_reset", + &ps->seconds_since_last_reset); + sa_sys_read_u64(path, "tx_frames", &ps->tx_frames); + sa_sys_read_u64(path, "tx_words", &ps->tx_words); + sa_sys_read_u64(path, "rx_frames", &ps->rx_frames); + sa_sys_read_u64(path, "rx_words", &ps->rx_words); + sa_sys_read_u64(path, "error_frames", &ps->error_frames); + sa_sys_read_u64(path, "invalid_crc_count", &ps->invalid_crc_count); + sa_sys_read_u64(path, "invalid_tx_word_count", + &ps->invalid_tx_word_count); + sa_sys_read_u64(path, "link_failure_count", &ps->link_failure_count); + sa_sys_read_u64(path, "fcp_control_requests", + &ps->fcp_control_requests); + sa_sys_read_u64(path, "fcp_input_requests", &ps->fcp_input_requests); + sa_sys_read_u64(path, "fcp_input_megabytes", &ps->fcp_input_megabytes); + sa_sys_read_u64(path, "fcp_output_requests", &ps->fcp_output_requests); + sa_sys_read_u64(path, "fcp_output_megabytes", + &ps->fcp_output_megabytes); + +free_path: + free(path); + + return ps; +} + +struct port_attributes *get_port_attribs(const char *host) +{ + struct port_attributes *pa; + char *path; + int err; + + err = asprintf(&path, "%s/%s/", SYSFS_HOST_DIR, host); + if (err == -1) + return NULL; + + pa = calloc(1, sizeof(*pa)); + if (!pa) + goto free_path; + + strncpy(pa->device_name, host, sizeof(pa->device_name)); + + sa_sys_read_line(path, "symbolic_name", pa->symbolic_name, + sizeof(pa->symbolic_name)); + sa_sys_read_line(path, "node_name", pa->node_name, + sizeof(pa->node_name)); + sa_sys_read_line(path, "port_name", pa->port_name, + sizeof(pa->port_name)); + sa_sys_read_line(path, "fabric_name", pa->fabric_name, + sizeof(pa->fabric_name)); + sa_sys_read_line(path, "speed", pa->speed, sizeof(pa->speed)); + sa_sys_read_line(path, "supported_speeds", pa->supported_speeds, + sizeof(pa->supported_speeds)); + sa_sys_read_line(path, "maxframe_size", pa->maxframe_size, + sizeof(pa->maxframe_size)); + sa_sys_read_line(path, "port_id", pa->port_id, sizeof(pa->port_id)); + sa_sys_read_line(path, "port_state", pa->port_state, + sizeof(pa->port_state)); + +free_path: + free(path); + + return pa; +} + +char *get_pci_dev_from_netdev(const char *netdev) +{ + char buf[1024]; + char *pcidev; + char *path; + char *cp; + int func; + int dom; + int bus; + int dev; + int ret; + + ret = asprintf(&path, "%s/%s/device", SYSFS_NET_DIR, netdev); + if (ret == -1) + return NULL; + + ret = readlink(path, buf, sizeof(buf) - 1); + free(path); + if (ret == -1) { + char realdev[256]; + char *subif; + size_t len; + + subif = strchr(netdev, '.'); + if (!subif) + return NULL; + + len = strlen(netdev) - strlen(subif); + strncpy(realdev, netdev, len); + if (realdev[len] != '\0') + realdev[len] = '\0'; + + ret = asprintf(&path, "%s/%s/lower_%s", SYSFS_NET_DIR, + netdev, realdev); + if (ret == -1) + return NULL; + + ret = readlink(path, buf, sizeof(buf) - 1); + free(path); + + if (ret == -1) + return NULL; + } + + do { + cp = strrchr(buf, '/'); + if (!cp) + break; + + ret = sscanf(cp + 1, "%x:%x:%x.%x", &dom, &bus, &dev, &func); + if (ret == 4) + break; + + *cp = '\0'; + } while (cp && cp > buf); + + ret = asprintf(&pcidev, "%04x:%02x:%02x.%x", dom, bus, dev, func); + if (ret == -1) + return NULL; + + return pcidev; +} + +char *get_host_by_wwpn(struct hba_wwn wwn) +{ + struct dirent *dp; + DIR *dir; + char *host = NULL; + char path[1024]; + uint64_t port_name; + struct hba_wwn port_wwn; + + dir = opendir(SYSFS_HOST_DIR); + if (!dir) + return NULL; + + for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { + if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + + snprintf(path, sizeof(path), "%s/%s", SYSFS_HOST_DIR, + dp->d_name); + + sa_sys_read_u64(path, "port_name", &port_name); + hton64(port_wwn.wwn, port_name); + if (memcmp((void *)&port_wwn, &wwn, sizeof(struct hba_wwn))) + continue; + + host = strdup(dp->d_name); + break; + } + + closedir(dir); + + return host; +} + +char *get_host_by_fcid(uint32_t fcid) +{ + struct dirent *dp; + DIR *dir; + char *host = NULL; + char path[1024]; + uint32_t port_id; + + dir = opendir(SYSFS_HOST_DIR); + if (!dir) + return NULL; + + for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { + if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + + snprintf(path, sizeof(path), "%s/%s", SYSFS_HOST_DIR, + dp->d_name); + + sa_sys_read_u32(path, "port_id", &port_id); + if (port_id != fcid) + continue; + + host = strdup(dp->d_name); + break; + } + + closedir(dir); + + return host; +} + +char *get_rport_by_fcid(uint32_t fcid) +{ + struct dirent *dp; + DIR *dir; + char *rport = NULL; + char path[1024]; + uint32_t port_id; + + dir = opendir(SYSFS_RPORT_DIR); + if (!dir) + return NULL; + + for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { + if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + + snprintf(path, sizeof(path), "%s/%s", SYSFS_RPORT_DIR, + dp->d_name); + + sa_sys_read_u32(path, "port_id", &port_id); + if (port_id != fcid) + continue; + + rport = strdup(dp->d_name); + break; + } + + closedir(dir); + + return rport; +} + +static int get_ctlr_num(const char *netdev) +{ + struct dirent *dp; + int ctlr_num = -1; + char path[1024]; + char *ctlr; + DIR *dir; + + sprintf(path, "%s/%s", SYSFS_NET_DIR, netdev); + + dir = opendir(path); + if (!dir) + return -1; + + for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { + if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + + ctlr = strstr(dp->d_name, "ctlr_"); + if (!ctlr) + continue; + + ctlr_num = atoi(&ctlr[strlen(ctlr) - 1]); + break; + } + + closedir(dir); + + return ctlr_num; +} + +char *get_host_from_netdev(const char *netdev) +{ + struct dirent *dp; + char *host = NULL; + char *path = NULL; + DIR *dir; + int ret; + int ctlr_num; + + ctlr_num = get_ctlr_num(netdev); + if (ctlr_num == -1) + return NULL; + + ret = asprintf(&path, "%s/%s/ctlr_%d/", SYSFS_NET_DIR, + netdev, ctlr_num); + if (ret == -1) + return NULL; + + dir = opendir(path); + free(path); + path = NULL; + + if (!dir) + return NULL; + + for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { + if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + + host = strstr(dp->d_name, "host"); + if (host) { + struct stat sb; + + ret = asprintf(&path, "%s/%s/ctlr_%d/%s/fc_host/%s", + SYSFS_NET_DIR, netdev, ctlr_num, host, host); + if (ret == -1) + goto out_closedir; + + ret = stat(path, &sb); + free(path); + path = NULL; + + if (ret == -1) + host = NULL; + break; + + } + } + +out_closedir: + closedir(dir); + + return host ? strdup(host) : NULL; +} + +int get_number_of_adapters(void) +{ + struct dirent *dp; + int num = 0; + DIR *dir; + + dir = opendir(SYSFS_HOST_DIR); + if (!dir) + return errno; + + for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) { + if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + continue; + if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') + continue; + + if (strstr(dp->d_name, "host")) + num++; + + } + + closedir(dir); + + return num; +} diff --git a/libopenfcoe.c b/libopenfcoe.c new file mode 100644 index 0000000..07090d5 --- /dev/null +++ b/libopenfcoe.c @@ -0,0 +1,244 @@ +/* + * Copyright(c) 2012-2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +#include "libopenfcoe.h" + +#define SYSFS_HOST_DIR "/sys/class/fc_host" +#define SYSFS_HBA_DIR "/sys/class/net" + +int mac2str(const u_int8_t *mac, char *dst, size_t size) +{ + if (dst && size > MAC_ADDR_STRLEN) { + snprintf(dst, size, "%02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return 0; + } + return -1; +} + +int str2mac(const char *src, u_int8_t *mac, size_t size) +{ + int i = 0; + int rc = -1; + + if (size < 6) + goto out_err; + + if (!src) + goto out_err; + + if (strlen(src) != MAC_ADDR_STRLEN) + goto out_err; + + memset(mac, 0, size); + for (i = 0; i < 6; i++, mac++) + if (1 != sscanf(&src[i * 3], "%02hhX", mac)) + goto out_err; + rc = 0; +out_err: + return rc; +} + +static int add_fcoe_fcf_device(struct dirent *dp, void *arg) +{ + struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)arg; + struct fcoe_fcf_device *fcf; + + if (!strstr(dp->d_name, "fcf") || + (!strcmp(dp->d_name, "fcf_dev_loss_tmo"))) + return 0; + + fcf = malloc(sizeof(struct fcoe_fcf_device)); + if (!fcf) + return -ENOMEM; + + memset(fcf, 0, sizeof(struct fcoe_fcf_device)); + + /* Save the path */ + snprintf(fcf->path, sizeof(fcf->path), + "%s/%s", ctlr->path, dp->d_name); + + /* Use the index from the logical enumeration */ + fcf->index = atoi(dp->d_name + sizeof("fcf_") - 1); + + /* Save the fcf in the fcport's table */ + if (sa_table_insert(&ctlr->fcfs, fcf->index, + fcf) < 0) { + fprintf(stderr, "%s: insert of fcf %d failed\n", + __func__, fcf->index); + goto fail; + } + + return 0; + +fail: + free(fcf); + return -ENOENT; +} + +static void read_fcoe_fcf_device(void *ep, UNUSED void *arg) +{ + struct fcoe_fcf_device *fcf = (struct fcoe_fcf_device *)ep; + char buf[MAX_STR_LEN]; + + sa_sys_read_line(fcf->path, "state", buf, sizeof(buf)); + sa_enum_encode(fcf_state_table, buf, &fcf->state); + sa_sys_read_u32(fcf->path, "dev_loss_tmo", &fcf->dev_loss_tmo); + sa_sys_read_u64(fcf->path, "fabric_name", &fcf->fabric_name); + sa_sys_read_u64(fcf->path, "switch_name", &fcf->switch_name); + sa_sys_read_u32(fcf->path, "fc_map", &fcf->fc_map); + sa_sys_read_u32(fcf->path, "vfid", &fcf->vfid); + + sa_sys_read_line(fcf->path, "mac", buf, MAX_STR_LEN); + str2mac(buf, &fcf->mac[0], MAC_ADDR_LEN); + + sa_sys_read_u32(fcf->path, "priority", &fcf->priority); + sa_sys_read_u32(fcf->path, "fka_period", &fcf->fka_period); + sa_sys_read_u32(fcf->path, "selected", &fcf->selected); + sa_sys_read_u32(fcf->path, "vlan_id", &fcf->vlan_id); +} + +static void read_fcoe_fcf(void *ep, UNUSED void *arg) +{ + struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)ep; + + /* Iterate through the ctlr and add any fcfs */ + sa_dir_read(ctlr->path, add_fcoe_fcf_device, ctlr); + + /* Populate each fabric */ + sa_table_iterate(&ctlr->fcfs, read_fcoe_fcf_device, NULL); +} + +static void free_fcoe_fcf_device(void *ep, UNUSED void *arg) +{ + struct fcoe_fcf_device *fcf = (struct fcoe_fcf_device *)ep; + + free(fcf); +} + +#define SYSFS_MOUNT "/sys" +#define FCOE_CTLR_DEVICE_DIR SYSFS_MOUNT "/bus/fcoe/devices/" + +static int find_fchost(struct dirent *dp, void *arg) +{ + char *fchost = arg; + + if (strstr(dp->d_name, "host")) { + strncpy(fchost, dp->d_name, MAX_STR_LEN); + return 1; + } + + return 0; +} + +static int read_fcoe_ctlr_device(struct dirent *dp, void *arg) +{ + struct sa_table *ctlrs = arg; + struct fcoe_ctlr_device *ctlr; + char buf[MAX_STR_LEN]; + char lesb_path[MAX_STR_LEN]; + char hpath[MAX_STR_LEN]; + char fchost[MAX_STR_LEN]; + char *cp, *ifname; + int rc; + + if (strncmp(dp->d_name, "ctlr_", 5)) + return 0; + + ctlr = malloc(sizeof(struct fcoe_ctlr_device)); + if (!ctlr) + return 0; /* Must return 0 or loop will break */ + + memset(ctlr, 0, sizeof(struct fcoe_ctlr_device)); + sa_table_init(&ctlr->fcfs); + + /* Save the path */ + snprintf(ctlr->path, sizeof(ctlr->path), + FCOE_CTLR_DEVICE_DIR "%s", dp->d_name); + + /* Use the index from the logical enumeration */ + ctlr->index = atoi(dp->d_name + sizeof("ctlr_") - 1); + + rc = sa_dir_read(ctlr->path, find_fchost, fchost); + if (!rc) + goto fail; + + sprintf(hpath, "%s/%s/", SYSFS_FCHOST, fchost); + + rc = sa_sys_read_line(hpath, "symbolic_name", buf, sizeof(buf)); + + /* Skip the HBA if it isn't Open-FCoE */ + cp = strstr(buf, " over "); + if (!cp) + goto fail; + + ifname = get_ifname_from_symbolic_name(buf); + strncpy(ctlr->ifname, ifname, IFNAMSIZ-1); + + /* Get fcf device loss timeout */ + sa_sys_read_u32(ctlr->path, "fcf_dev_loss_tmo", + &ctlr->fcf_dev_loss_tmo); + + sa_sys_read_line(ctlr->path, "mode", buf, sizeof(buf)); + sa_enum_encode(fip_conn_type_table, buf, &ctlr->mode); + + snprintf(lesb_path, sizeof(lesb_path), "%s/lesb/", ctlr->path); + + /* Get LESB statistics */ + sa_sys_read_u32(lesb_path, "link_fail", + &ctlr->lesb_link_fail); + sa_sys_read_u32(lesb_path, "vlink_fail", + &ctlr->lesb_vlink_fail); + sa_sys_read_u32(lesb_path, "miss_fka", + &ctlr->lesb_miss_fka); + sa_sys_read_u32(lesb_path, "symb_err", + &ctlr->lesb_symb_err); + sa_sys_read_u32(lesb_path, "err_block", + &ctlr->lesb_err_block); + sa_sys_read_u32(lesb_path, "fcs_error", + &ctlr->lesb_fcs_error); + + /* Save the ctlr in the supplied table */ + if (sa_table_insert(ctlrs, ctlr->index, ctlr) < 0) { + fprintf(stderr, "%s: insert of ctlr %d failed\n", + __func__, ctlr->index); + goto fail; + } + + return 0; + +fail: + free(ctlr); + return -ENOENT; +} + +void read_fcoe_ctlr(struct sa_table *ctlrs) +{ + sa_dir_read(FCOE_CTLR_DEVICE_DIR, read_fcoe_ctlr_device, ctlrs); + sa_table_iterate(ctlrs, read_fcoe_fcf, NULL); +} + +void free_fcoe_ctlr_device(void *ep, UNUSED void *arg) +{ + struct fcoe_ctlr_device *ctlr = (struct fcoe_ctlr_device *)ep; + + sa_table_iterate(&ctlr->fcfs, free_fcoe_fcf_device, NULL); + + free(ctlr); +}