pax_global_header00006660000000000000000000000064123322465130014513gustar00rootroot0000000000000052 comment=fddc639906230f4ac6ab9844a0ef4d3a7d559c37 iscsitarget-1.4.20.3+svn502/000077500000000000000000000000001233224651300153125ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/COPYING000066400000000000000000000431321233224651300163500ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. iscsitarget-1.4.20.3+svn502/ChangeLog000066400000000000000000000442721233224651300170750ustar00rootroot00000000000000Summary of changes from v1.4.20 to Current ================================= Arne Redlich o adapt backward compat mechanism in Makefile to kernels >= 2.6.33 o uninitialized data fixes pointed out by valgrind o fix invalid read in iSNS code pointed out by valgrind o Fixed iSNS initialization issue, thanks to majianpeng@gmail.com o The NIPQUAD macro has been removed, instead "%pI4" is used o Converted semaphores to mutexes o Fixed printing IPv4 addresses in /proc/net/iet/session for kernels >= 2.6.33 o Fixed block device handling for kernels >= 2.6.38 Ross Walker o restore SCSI SN behaviour of IET < 1.4.20 (VMWare compatibility) o check buffer boundaries in iSNS code (CVE-2010-2221) o fix memleak in iSNS ACL code o clean up iSCSI login response error handling o rework ietd startup to avoid concurrent ietds o fix soft lockup during UA allocation o change DISTDIR to DESTDIR o avoid deletion of targets with active sessions o byte swap fix in MD5 / SHA1 code o Updated initd scripts to reflect PID file name change o Removed stray RPM build dependency on openssl from spec file o Fixed crypto API error handling (ERR_PTR instead of a NULL) o Fixed small memory leak in volume args buffer on BSD o Fixed io context handling on kernels >= 2.6.25 o Fixed max_cmd_sn not being set for the session o Fixed issue where StatSN was being updated when a command was ignored o Fixed connection accounting in ietd for session reinstatements o Fixed MaxOutStandingR2T handling o Stripped zone id off ipv6 addresses in SendTargets o Time2Retain fixed at 0 since it isn't implemented Chris Siebenmann, Wido den Hollander o 2.6.36 compile fix Craig o Makefile respects LDFLAGS Jason Zhang o Fixed re-enabling listening after max connections was exceeded o Fix NULL-pointer dereference due to race in UA handling Martin Hicks o Added variable for depmod o Don't install kernel module executable o Don't use bash for initd script Summary of changes from v1.4.19 to v1.4.20 ================================= Minh Tran o fix TTT for final PDU in ietd Qinghua(Kevin) Ye o release reservations during session delete Ross Walker o multiple updates for abort task compliance o replace sync_page_range with filemap_write_and_wait_range in fileio o replace do_sync_read/write with vfs_read/write in fileio o multiple updates for target_del_all, atomic deletion of targets, sessions and connections o install moves existing kernel module, uninstall moves it back o cleaned up RPM and DKMS package building o cleaned up usr-kernel ioctl calls, added version checking, semaphore to prevent ioctls during kernel cleanup (target_del_all) o cleaned up session and connection reinstatement o setup IO contexts in wthread, fixes CFQ performance problem o enabled multiple connections per session o moved common parameters from iotypes to volume o UA handling on aborted/terminated connections o removed OpenSSL dependency by borrowing kernel md5/sha1 code o code cleanups Arne Redlich o multiple kernel compatibility updates o code cleanups Lars Ellenberg o support for logical sector sizes o case insensitive volume options o code cleanups Luca Berra o fix warnings when compiling via gcc strict checks Kun Huang o temporary target redirections via ietadm for existing targets Jagadish Kumar o multiple code cleanups around network and worker thread handling Summary of changes from v1.4.18 to v1.4.19 ================================= Andreas Florath o re-enable MaxSessions parameter o code cleanups Arne Redlich o NOP-In support Harshal Shete o add notes about cross compilation to README SZÉKELYI Szabolcs o fix ietadm manpage Ross S. W. Walker o fix initiators/targets.allow bugs o fix compiler warnings o fix return value of session_add() o update RPM specfile improving RedHat and SuSE compatibility (based on a patch from Matthew Wild) o update RELEASE NOTES Kevin Ye o fix session cleanup in case of -EPIPE during login Summary of changes from v0.4.17 to v1.4.18 ================================= Lars Ellenberg o improve IETs procfs support to allow a larger number of targets o compatibility fix for 2.6.28 (independently also provided by Francois Micaux) o ignore SIGPIPE in ietd o allow cleanup of stale targets in the kernel module Andreas Florath o support for a global worker threadpool instead of per target pools (configurable via the worker_thread_pool_size module parameter) Arne Redlich o compatibility fixes for 2.6.30, 2.6.29 o in cooperation with Xie Gang: Unit Attention support (thanks to Stefan Rubner for spotting a bug) o in cooperation with Shreyansh Jain: support for splitting text messages into several PDUs, allowing more targets to be reported during discovery (thanks to Cheng Rengquan for bugfixes) o clean up all connections, sessions and targets in the kernel module if the daemon is gone o rework sense data handling, plugging memleaks o fix valgrind warnings of uses of uninitialized variables o support for Reject PDUs (thanks to FUJITA Tomonori for a bugfix) o fix SERVICE ACTION IN: IET only supports READ CAPACITY 16, return sense data for all others o allow READ CAPACITY even if a LU is RESERVEd o iSNS: report the actual target port instead of the default iSCSI port o fix list corruption if target thread creation fails o fix stopping of threads that have never been awakened before o fix CHAP account handling bugs o fix netlink socket cleanup o code cleanups Cheng Renquan o remove superfluous linefeed from debug messages o enable runtime switching of debug levels for the kernel module and provide description for the module's debug flags parameter sdrb o return proper status class in case of login failures Ross S. W. Walker o support more than one portal per target during discovery, and allow configuration of the presented portals using targets.allow - previously only the incoming interface was reported (thanks to David Shirley for reporting a bug) o deprecate initiators.allow o regex based filtering in initiators.allow and targets.allow o move config files to /etc/iet o add version and author info and description to kernel module o fix debug log levels o fix ietd.conf permissions o move kernel module to /lib/modules//extra where it belongs o improve init scripts, using common names and functions across distributions o improve usage comments in ietd.conf o specfile to build RPMs, including weak module support o DKMS support Ming Zhang o Use the LU's sector size in the format mode page instead of defaulting to 512 Summary of changes from v0.4.16 to v0.4.17 ================================= Charley Cheng o handle the \0-termination of strings properly when building iSNS TLVs Denis ChengRq o fix stopping of wthreads that have never been awakened Shreyansh Jain o fix debugging code that hexdumps PDU content to the syslog Jagadish Kumar o avoid busy looping if a connection's socket is out of wmem Arne Redlich o add support for "ietadm --op show --user" o SCSI fixes: MODE SENSE handling, INQUIRY CmdDt, HiSup, WCE and RCD handling o fix handling of NOP-Out w/ ping data o fix compilation against glibc >= 2.8 o fix potential NULL-pointer derefences and resource leaks in the LUN param parsing code o documentation updates Ross S. W. Walker o 2.6.26 and SLES compile fix o SLES 10 SP2 compat patch Ming Zhang o make needlessly global function static Summary of changes from v0.4.15 to v0.4.16 ================================= Arne Redlich o fix overzealous assert() in digest_data() o add checking on return value of ISCSI_PARAM_GET o 2.6.22, 2.6.23 and 2.6.24 compile fixes o add conn->rwsize check o avoid potential NULL-ptr dereferences in rx and tx buffer o fix the shell syntax in init scripts Dave Jiang o fix digest endieness on LE archs FUJITA Tomonori o fix SPARC alignement issues (based on a patch from joel.bertrand@systella.fr) Ross S. W. Walker o fix DISTDIR in Makefile for /etc install o add support to nullio for volumes > 2TB o remove init.d memory size adjustment o add error code reporting to blockio_open_path o blockio gen_scsiid bug fix o add verbosity to kernel output and task management Summary of changes from v0.4.14 to v0.4.15 ================================= Juhani Rautiainen o Add RELEASE/RESERVE support Ross S. W. Walker o Improve the build system to support several kernel versions o Add block-io support Summary of changes from v0.4.13 to v0.4.14 ================================= Arne Redlich o Kill unused "state" in struct iscsi_cmnd. o Fixed fileio_sync() to propagate error to the caller (and initiator). o Don't attempt to show target/session params if communication with ietd fails. o Fixes to ietadm parameters handling. FUJITA Tomonori o rewritten iSNS code, many iSNS fixes. o added iSNS SCN support. o IPv6 fixes to userspace. Ming Zhang o Fix the READ_* commands error handling bug. o fix the mode sense response. o wrong #endif sequence in misc.h Richard Bollinger o add a patch to ietd.c that allows defunct sessions to go away. o add write-back cache and read-only support. Frederic Temporelli o Fix for the combination of 32-bit userland and 64-bit kernel on mips. Henry Liu o corrected many task management functions, prevent crashing on LUN RESET, TARGET WARM RESET. K Chapman o Fixed a typo in check_segment_length(). Emmanuel Florac o Add ietadm manpage. Summary of changes from v0.4.12 to v0.4.13 ================================= Arne Redlich o patch to avoid digest calculation for PDUs whose data has been skipped already for various reasons. o Correct a bug managing non-default MaxRxDSL. o added to ietadm ability to show target parameters. o add on the workaround to AIX initiator MaxCmdSN bug. FUJITA Tomonori o added to ietadm ability to show the iSCSI parameters for an established session. Ming Zhang o Fixed this bug : ietd should manage the iscsi name in a case insensitive way to conform to the RFC. o workaround to AIX initiator MaxCmdSN bug. o Fixed socket() return value judgment. Bastiaan Bakker o add 'condrestart' command to the RedHat initscript. Robert Whitehead o correct the bug that prevents iet to start if there isn't an /etc/ietd.conf file. Summary of changes from v0.4.11 to v0.4.12 ================================= Arne Redlich o Fix MaxRecvDataSegmentLength handling. o Fix login parameter handling. o Update man pages. Bastiaan Bakker o Add features to specify the listen address and port. o Fix setuid and setgid bugs in ietd daemon. FUJITA Tomonori o Add IPv6 support. Junjiro Okajima o Fix a bug about getting parameters from kernel space. Krzysztof Blaszkowski o Fix for requests with unaligned to 4 length. Summary of changes from v0.4.10 to v0.4.11 ================================= FUJITA Tomonori o Fix Task Management Function bugs. Ming Zhang o Update man pages. Summary of changes from v0.4.9 to v0.4.10 ================================= Arne Redlich o Fix 0x83 inquiry output. o Fix iSCSI parameter handling bugs. FUJITA Tomonori o Add the access control based on initiator address and target name patterns. Junjiro Okajima o Fix parameter checking bugs. Ming Zhang o Add the nullio mode (only useful for performance evaluation). Summary of changes from v0.4.8 to v0.4.9 ================================= FUJITA Tomonori o Fix parameter negotiation handling bugs. Wang Zhenyu o Fix digest negotiation handling bugs. Summary of changes from v0.4.7 to v0.4.8 ================================= FUJITA Tomonori o Fix unsolicited data handling bugs. o Rewrite parameter handling code. o Rewrite ietadm tool. o Improve dynamic configuration support. o Cleanups on the kernel-user interface. o Improve wrong PDU handling. o Implement a framework to handle multiple configuration methods. o Implement basic access control support. Summary of changes from v0.4.6 to v0.4.7 ================================= Florian Zierer o Add the startup script for Gentoo. FUJITA Tomonori o Rewrite parameter handling code. o Fix task management code bug. o Fix 0x83 inquiry output (Thanks to Christophe Varoqui). Ming Zhang o Acquire T10 ID. o Fix parameter handling bugs. o Some user-space cleanups. Philipp Hug o Fix ietd.8 man typo. Summary of changes from v0.4.5 to v0.4.6 ================================= FUJITA Tomonori o Replace the makeshift event notification code with netlink. o Add task management code except for ACA and reassign stuff. o Fix r2t lun bug (Thanks to Ming Zhang). Summary of changes from v0.4.4 to v0.4.5 ================================= FUJITA Tomonori o Rewrite the iSCSI command handling code. o Rewrite the I/O data handling code. o Fix worker thread. o Several cleanups. Summary of changes from v0.4.3 to v0.4.4 ================================= Krzysztof Blaszkowski o Fix an out-of-memory bug. Summary of changes from v0.4.2 to v0.4.3 ================================= Arne Redlich o Fix header digest bug. o Fix unexpected closed connection bug. o Fix iSCSI parameter bug. FUJITA Tomonori o Fix network thread. Summary of changes from v0.4.1 to v0.4.2 ================================= FUJITA Tomonori o Fix network thread. o Fix MaxOutstandingR2T handling. Ming Zhang o Add large volume support (over 2TB). Summary of changes from v0.4.0 to v0.4.1 ================================= Arne Redlich o Add mutual CHAP support. Note that you need to replace "User" with "IncomingUser" in ietd.conf. FUJITA Tomonori o Fix InitialR2T=No support. o Fix INQUIRY command handling. o Fix network and worker thread. o Start to split SCSI stuff. o Rewrite the R2T handling code. o Several cleanups. Summary of changes from v0.3.8 to v0.4.0 ================================= Arne Redlich o iSNS bug fix. FUJITA Tomonori o Move to 2.6 kernels. o Rewrite the kernel thread performing network I/O. o Add header and data digests (Thanks to Arne Redlich). Ming Zhang o Add mode sense page 0x3 and 0x4 support (Thanks to K Chapman). o iSNS bug fix. Summary of changes from v0.3.7 to v0.3.8 ================================= Arne Redlich o Fix ietadm global option bug. FUJITA Tomonori o Fix TCP option bugs (Thanks to Chuck Berg). o Fix REPORT LUN (handling lots of LUs). Summary of changes from v0.3.6 to v0.3.7 ================================= Arne Redlich o Fix target_alloc_pages(). FUJITA Tomonori o Fix REPORT LUN bug. Summary of changes from v0.3.5 to v0.3.6 ================================= Arne Redlich o Fix bugs about rejecting PDUs. FUJITA Tomonori o Cleanups on target_cmnd structure. o Kill highmem stuff. o Fix REPORT LUN (handling lots of LUs). Summary of changes from v0.3.4 to v0.3.5 ================================= Arne Redlich o Fix ietd security hole. o Fix REPORT LUN bug. o FIX NOOP_OUT padding bug. FUJITA Tomonori o Rewrite event notification code. Libor Vanek o Add max_sessions option. o Fix command parsing bug. Ming Zhang o Cleanups for 64-bit architectures. Summary of changes from v0.3.3 to v0.3.4 ================================= FUJITA Tomonori o Improve dynamic configuration support (adding targets and users). Summary of changes from v0.3.2 to v0.3.3 ================================= FUJITA Tomonori o Fix Makefile for the startup script. Summary of changes from v0.3.1 to v0.3.2 ================================= Ali Lehmann o Add a new startup script for Debian. FUJITA Tomonori o Fix the istd's handling of connections in out-of-memory situations. o Fix bugs in regular file support. o Fix `ietadm --mode del all`. Libor Vanek o Add a new startup script for RedHat. Ming Zhang o Add uid/gid option to ietd daemon. o Fix a access freed-memory bug in kernel/daemon.c. Summary of changes from v0.3.0 to v0.3.1 ================================= FUJITA Tomonori o Fix memory leaks in ietd daemon (Thanks to Ming). o Fix bugs about REPORT_LUNS commands (Thanks to Ming). o Fix a bug about Target Task Tag. o Add regular file support to fileio mode. Summary of changes from v0.2.6 to v0.3.0 ================================= Ali Lehmann o Update ietd.8 man page. FUJITA Tomonori o Fix shutdown code. o Fix istd kernel thread bugs. o Replace procfs interface with ioctl. o Add dynamic configuration support. o Update README and the boot script. Ming Zhang o Add config option to ietd daemon. Summary of changes from v0.2.5 to v0.2.6 ================================= Ali Lehmann o Add ietd.8 and ietd.conf.5 man pages. FUJITA Tomonori o Update README, Makefile, and the boot script. Summary of changes from v0.2.4 to v0.2.5 ================================= FUJITA Tomonori o Update README. Summary of changes from v0.2.3 to v0.2.4 ================================= Ming Zhang o Add a preliminary iSNS support. o Fix merge mistakes that I made at the previous release. Summary of changes from v0.2.2 to v0.2.3 ================================= Ming Zhang o Improve INQUIRY, REQUEST_SENSE, and MODE_SENSE command supports o Add fake RESERVE* and RELEASE* command supports Summary of changes from v0.2.1 to v0.2.2 ================================= FUJITA Tomonori o Improve the write performance of the file IO mode Ming Zhang o Fix unupdated pg_cnt when allocating a new tcmnd o Several cleanups Summary of changes from v0.2.0 to v0.2.1 ================================= FUJITA Tomonori o Fix a bug that makes the target use CPU unnecessarily o Add a feature that enable you to pass options to an IO mode Summary of changes from v0.1.0 to v0.2.0 ================================= FUJITA Tomonori o Rewrite read and write kernel threads which perform network IO o Fix race issues in the proc interface o Fix shutdown code Ming Zhang o Fix memory leaks in file and block IO modes Summary of changes from the ardis v20040211 to v0.1.0 ================================= FUJITA Tomonori o Remove a kernel patch. Multiple threads execute I/O operations o Replace IO functions with the kernel starndard functions o Add multiple IO modes feature o Fix several race issues o Fix several out-of-memory situation bugs iscsitarget-1.4.20.3+svn502/Makefile000066400000000000000000000255631233224651300167650ustar00rootroot00000000000000# # Makefile for the Linux kernel device drivers. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (not a .c file). # # Note 2! The CFLAGS definitions are now in the main makefile. #export KSRC := /usr/src/linux DEPMOD = depmod SUBDIRS := $(shell pwd) ifeq ($(KSRC),) KSRC ?= /lib/modules/$(shell uname -r)/build endif ifneq ($(wildcard $(KSRC)/include/generated/utsrelease.h),) VERSION_FILE := $(KSRC)/include/generated/utsrelease.h else ifneq ($(wildcard $(KSRC)/include/linux/utsrelease.h),) VERSION_FILE := $(KSRC)/include/linux/utsrelease.h else VERSION_FILE := $(KSRC)/include/linux/version.h endif endif KVER := $(shell $(CC) $(CFLAGS) $(LDFLAGS) -E -dM $(VERSION_FILE) | \ grep UTS_RELEASE | awk '{ print $$3 }' | sed 's/\"//g') KMOD := /lib/modules/$(KVER)/extra KMAJ := $(shell echo $(KVER) | \ sed -e 's/^\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*.*/\1/') KMIN := $(shell echo $(KVER) | \ sed -e 's/^[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*.*/\1/') KREV := $(shell echo $(KVER) | \ sed -e 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\).*/\1/') kver_eq = $(shell [ $(KMAJ) -eq $(1) -a $(KMIN) -eq $(2) -a $(KREV) -eq $(3) ] && \ echo 1 || echo 0) kver_lt = $(shell [ $(KMAJ) -lt $(1) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -lt $(2) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -eq $(2) -a $(KREV) -lt $(3) ] && \ echo 1 || echo 0) kver_le = $(shell [ $(KMAJ) -lt $(1) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -lt $(2) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -eq $(2) -a $(KREV) -le $(3) ] && \ echo 1 || echo 0) kver_gt = $(shell [ ( $(KMAJ) -gt $(1) ) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -gt $(2) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -eq $(2) -a $(KREV) -gt $(3) ] && \ echo 1 || echo 0) kver_ge = $(shell [ ( $(KMAJ) -gt $(1) ) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -gt $(2) -o \ $(KMAJ) -eq $(1) -a $(KMIN) -eq $(2) -a $(KREV) -ge $(3) ] && \ echo 1 || echo 0) kver_lk = $(shell [ `echo $(KVER) | egrep $(1)` ] && echo 1 || echo 0) # # Please when adding patch sets start with the latest to the earliest # the idea behind this is that by properly patching the latest code # base first the earlier patch sets will not need to be modified. # ifeq ($(call kver_lt,3,14,0),1) PATCHES := $(PATCHES) compat-3.13.patch endif ifeq ($(call kver_lt,3,13,0),1) PATCHES := $(PATCHES) compat-3.12.patch endif ifeq ($(call kver_lt,3,10,0),1) PATCHES := $(PATCHES) compat-3.9.patch endif ifeq ($(call kver_lt,3,7,0),1) PATCHES := $(PATCHES) compat-3.6.patch endif ifeq ($(call kver_lt,3,6,0),1) PATCHES := $(PATCHES) compat-3.5.patch endif ifeq ($(call kver_lt,3,5,0),1) PATCHES := $(PATCHES) compat-3.2-3.4.patch endif ifeq ($(call kver_lt,3,3,0),1) PATCHES := $(PATCHES) compat-2.6.39-3.2.patch endif ifeq ($(call kver_le,2,6,38),1) PATCHES := $(PATCHES) compat-2.6.38.patch endif ifeq ($(call kver_le,2,6,37),1) PATCHES := $(PATCHES) compat-2.6.36-2.6.37.patch endif # Compatibility patch for kernels > 2.6.32 <= 2.6.35 ifeq ($(call kver_le,2,6,35),1) PATCHES := $(PATCHES) compat-2.6.33-2.6.35.patch endif # Compatibility patch for kernels <= 2.6.32 ifeq ($(call kver_le,2,6,32),1) PATCHES := $(PATCHES) compat-2.6.32.patch endif # Compatibility patch for kernels <= 2.6.31 ifeq ($(call kver_le,2,6,31),1) PATCHES := $(PATCHES) compat-2.6.31.patch endif # Compatibility patch for kernels <= 2.6.30 ifeq ($(call kver_le,2,6,30),1) PATCHES := $(PATCHES) compat-2.6.30.patch endif # Compatibility patch for kernels <= 2.6.29 ifeq ($(call kver_le,2,6,29),1) PATCHES := $(PATCHES) compat-2.6.29.patch endif # Compatibility patch for kernels <= 2.6.28 ifeq ($(call kver_le,2,6,28),1) PATCHES := $(PATCHES) compat-2.6.28.patch endif # Compatibility patch for kernels >= 2.6.25 and <= 2.6.27 ifeq ($(call kver_le,2,6,27),1) PATCHES := $(PATCHES) compat-2.6.25-2.6.27.patch endif # Compatibility patch for kernels <= 2.6.24 ifeq ($(call kver_le,2,6,24),1) PATCHES := $(PATCHES) compat-2.6.24.patch endif # Compatibility patch for kernels <= 2.6.23 ifeq ($(call kver_le,2,6,23),1) PATCHES := $(PATCHES) compat-2.6.23.patch endif # Compatibility patch for kernels <= 2.6.22 ifeq ($(call kver_le,2,6,22),1) PATCHES := $(PATCHES) compat-2.6.22.patch endif # Compatibility patch for kernels >= 2.6.19 and <= 2.6.21 ifeq ($(call kver_le,2,6,21),1) PATCHES := $(PATCHES) compat-2.6.19-2.6.21.patch endif # Compatibility patch for kernels >= 2.6.14 and <= 2.6.18 ifeq ($(call kver_le,2,6,18),1) PATCHES := $(PATCHES) compat-2.6.14-2.6.18.patch endif # We don't support kernels < 2.6.14 except for explicit distros ifeq ($(call kver_lt,2,6,14),1) UNSUPPORTED := true endif # Compatibility patches for SuSE distros ifneq ($(wildcard /etc/SuSE-release),) # Compatibility patch for SLES 10 SP2 ifeq ($(call kver_lk,"2\.6\.16\.60-.*"),1) PATCHES += compat-sles10sp2.patch UNSUPPORTED := endif endif # Compatibility patches for Redhat distros ifneq ($(wildcard /etc/redhat-release),) # Compatibility patch for RHEL4/CentOS4 ifeq ($(call kver_lk,"2\.6\.9-.*\.(EL|plus\.c4)"),1) PATCHES += compat-rhel4.patch UNSUPPORTED := endif endif MANPAGES:= ietadm.8 ietd.8 ietd.conf.5 ifeq ($(MANDIR),) MANPATH := $(shell (manpath 2>/dev/null || \ echo $MANPATH) | sed 's/:/ /g') ifneq ($(MANPATH),) test_dir = $(findstring $(dir), $(MANPATH)) else test_dir = $(shell [ -e $(dir) ] && echo $(dir)) endif MANDIR := /usr/share/man /usr/man MANDIR := $(foreach dir, $(MANDIR), $(test_dir)) MANDIR := $(firstword $(MANDIR)) endif ifeq ($(MANDIR),) MANDIR := /usr/share/man endif DOCS:= ChangeLog COPYING RELEASE_NOTES README README.vmware README.initiators ifeq ($(DOCDIR),) DOCDIR := /usr/share/doc/iscsitarget endif all: usr kernel usr: patch $(MAKE) -C usr kernel: patch $(MAKE) -C $(KSRC) SUBDIRS=$(shell pwd)/kernel modules patch: $(UNSUPPORTED) integ_check $(PATCHES) $(UNSUPPORTED): @echo "Sorry, your kernel version and/or distribution is currently" @echo "not supported." @echo "" @echo "Please read the README file for information on how you can" @echo "contribute compatibility/bug fixes to the IET project." @exit 1 integ_check: @if [ -e .patched.* -a ! -e .patched.$(KVER) ]; then \ $(MAKE) unpatch; \ fi $(PATCHES): .patched.$(KVER) .patched.$(KVER): @set -e; \ if [ ! -e .patched.* ]; then \ for p in $(PATCHES); do \ echo "Applying Patch $$p"; \ patch -p1 < patches/$$p; \ echo $$p >>.patched.$(KVER); \ done; \ fi unpatch: @set -e; \ if [ -e .patched.* ]; then \ for p in `cat .patched.*`; do \ reverse="$$p $$reverse"; \ done; \ for r in $$reverse; do \ echo "Reversing patch $$r"; \ patch -p1 -R < patches/$$r; \ done; \ rm -f .patched.*; \ fi depmod: @echo "Running depmod" @if [ x$(DESTDIR) != x -o x$(INSTALL_MOD_PATH) != x ]; then \ $(DEPMOD) -aq -b $(DESTDIR)$(INSTALL_MOD_PATH) $(KVER); \ else \ $(DEPMOD) -aq $(KVER); \ fi install-files: install-usr install-etc install-doc install-kernel install: install-files depmod install-kernel: kernel/iscsi_trgt.ko @if [ -d $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER) ]; then \ if [ -f /etc/debian_version ]; then \ find $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER) \ -name iscsi_trgt.ko -type f \ -exec /bin/sh -c "dpkg-divert --rename {}" \;; \ else \ find $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER) \ -name iscsi_trgt.ko -type f \ -execdir mv \{\} \{\}.orig \;; \ fi \ fi @install -vD -m 644 kernel/iscsi_trgt.ko \ $(DESTDIR)$(INSTALL_MOD_PATH)$(KMOD)/iscsi/iscsi_trgt.ko install-usr: usr/ietd usr/ietadm @install -vD usr/ietd $(DESTDIR)/usr/sbin/ietd @install -vD usr/ietadm $(DESTDIR)/usr/sbin/ietadm install-etc: install-initd @if [ ! -e $(DESTDIR)/etc/ietd.conf ]; then \ if [ ! -e $(DESTDIR)/etc/iet/ietd.conf ]; then \ install -vD -m 640 etc/ietd.conf \ $(DESTDIR)/etc/iet/ietd.conf; \ fi \ fi @if [ ! -e $(DESTDIR)/etc/initiators.allow ]; then \ if [ ! -e $(DESTDIR)/etc/iet/initiators.allow ]; then \ install -vD -m 644 etc/initiators.allow \ $(DESTDIR)/etc/iet/initiators.allow; \ fi \ fi @if [ ! -e $(DESTDIR)/etc/targets.allow ]; then \ if [ ! -e $(DESTDIR)/etc/iet/targets.allow ]; then \ install -vD -m 644 etc/targets.allow \ $(DESTDIR)/etc/iet/targets.allow; \ fi \ fi install-initd: @if [ -f /etc/debian_version ]; then \ install -vD -m 755 etc/initd/initd.debian \ $(DESTDIR)/etc/init.d/iscsi-target; \ elif [ -f /etc/redhat-release ]; then \ install -vD -m 755 etc/initd/initd.redhat \ $(DESTDIR)/etc/rc.d/init.d/iscsi-target; \ elif [ -f /etc/gentoo-release ]; then \ install -vD -m 755 etc/initd/initd.gentoo \ $(DESTDIR)/etc/init.d/iscsi-target; \ elif [ -f /etc/slackware-version ]; then \ install -vD -m 755 etc/initd/initd \ $(DESTDIR)/etc/rc.d/iscsi-target; \ else \ install -vD -m 755 etc/initd/initd \ $(DESTDIR)/etc/init.d/iscsi-target; \ fi install-doc: install-man @ok=true; for f in $(DOCS) ; \ do [ -e $$f ] || \ { echo $$f missing ; ok=false; } ; \ done ; $$ok @set -e; for f in $(DOCS) ; do \ install -v -D -m 644 $$f \ $(DESTDIR)$(DOCDIR)/$$f ; \ done install-man: @ok=true; for f in $(MANPAGES) ; \ do [ -e doc/manpages/$$f ] || \ { echo doc/manpages/$$f missing ; ok=false; } ; \ done ; $$ok @set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ install -v -D -m 644 doc/manpages/$$f \ $(DESTDIR)$(MANDIR)/man$$s/$$f ; \ done uninstall: uninstall-kernel depmod uninstall-usr uninstall-etc uninstall-doc uninstall-kernel: rm -f $(DESTDIR)$(INSTALL_MOD_PATH)$(KMOD)/iscsi/iscsi_trgt.ko @if [ -f /etc/debian_version ]; then \ find $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER) \ -name iscsi_trgt.ko.distrib -type f \ -exec /bin/sh -c "dpkg-divert --remove --rename \ \`dirname {}\`/iscsi_trgt.ko" \;; \ else \ find $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER) \ -name iscsi_trgt.ko.orig -type f \ -execdir mv \{\} iscsi_trgt.ko \;; \ fi uninstall-usr: @rm -f $(DESTDIR)/usr/sbin/ietd @rm -f $(DESTDIR)/usr/sbin/ietadm uninstall-etc: uninstall-initd uninstall-initd: if [ -f /etc/debian_version ]; then \ rm -f $(DESTDIR)/etc/init.d/iscsi-target; \ elif [ -f /etc/redhat-release ]; then \ rm -f $(DESTDIR)/etc/rc.d/init.d/iscsi-target; \ elif [ -f /etc/gentoo-release ]; then \ rm -f $(DESTDIR)/etc/init.d/iscsi-target; \ elif [ -f /etc/slackware-version ]; then \ rm -f $(DESTDIR)/etc/rc.d/iscsi-target; \ else \ rm -f $(DESTDIR)/etc/init.d/iscsi-target; \ fi uninstall-doc: uninstall-man rm -rf $(DESTDIR)$(DOCDIR) uninstall-man: set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ rm -f $(DESTDIR)$(MANDIR)/man$$s/$$f ; \ done clean: $(MAKE) -C usr clean $(MAKE) -C $(KSRC) SUBDIRS=$(shell pwd)/kernel clean distclean: unpatch clean find . -name \*.orig -exec rm -f \{\} \; find . -name \*.rej -exec rm -f \{\} \; find . -name \*~ -exec rm -f \{\} \; find . -name Module.symvers -exec rm -f \{\} \; iscsitarget-1.4.20.3+svn502/README000066400000000000000000000133561233224651300162020ustar00rootroot00000000000000Introduction ------------- iSCSI Enterprise Target is for building an iSCSI storage system on Linux. It is aimed at developing an iSCSI target satisfying enterprise requirements. We borrow code from an Ardis iSCSI target (with respect to the GPL). Installation ------------- There is no longer a need to manually apply the compatibility patches located under ./patches. The make process will now auto-detect whether these patches need to be applied for your platform and auto-apply the correct ones for you. DO NOT MANUALLY APPLY THESE COMPATIBILITY PATCHES This will cause the auto-patch routine in the Makefile to break badly and it will put the code-base in such a state where you will need to remove it and re-extract the tar ball or run a svn revert to get it to compile properly again. The kernel module requires the kernel source or development package. You may need to recompile your kernel with "Cryptographic API" enabled under "Cryptographic options" in the kernel config if it isn't by default (most kernels have this enabled). If you do need to enable the "Cryptographic API" you will also need to enable "CRC32c CRC algorithm" if you use header or data digests. They are the kernel options, CONFIG_CRYPTO and CONFIG_CRYPTO_CRC32C, respectively. The iSCSI target consists of a kernel module (iscsi_trgt.ko) , daemon (ietd) and control utility (ietadm). Compilation of the kernel module, daemon and control utility should require nothing more than a: make To build individual components: make kernel - to build the kernel modules make usr - to build the daemon and control program If you have multiple kernel sources installed and wish to target the make for a particular source, you would issue: make KSRC= The path can also be set by editing the main Makefile. If KSRC is omitted, make program will try to locate the kernel sources for the current running kernel. Be sure to check whether it finds the right kernel sources. To install everything, use: make [KSRC=] install To install individual components: make [KSRC=] install-kernel - for kernel module make [KSRC=] install-usr - for daemon and utility make [KSRC=] install-init - for init.d script make [KSRC=] install-etc - for config files make [KSRC=] install-man - for man pages The kernel modules will be installed in the module directory of the kernel. The daemon and the control tool will be installed as ietd and ietadm under /usr/sbin. The init script will be installed as iscsi-target under /etc/init.d. The default ietd.conf, initiators.{allow,deny} are installed under /etc (the Makefile will not replace any existing config files). The manual pages are installed under /usr/man or /usr/share/man depending on your platform. The COPYING and this README are installed under /usr/docs/iscsitarget or /usr/share/docs/iscsitarget depending on your platform. If you use Linux distribution that does not have /etc/init.d, the boot script will not be installed. So you need to install it to an appropriate directory manually. Cross compilation ----------------- To cross compile the iSCSI Enterprise Target for a particular target the following things need to be setup on the development machine: 1) Cross compiler toolchain. The tools need to be in the shell's path for the following to work. 2) Linux kernel source code of the target. The global Makefile doesn't support cross compilation so the user space code (ietd and ietadm) and the kernel code need to be build separately. The command line to build the user space code is make CC=compiler_name usr e.g make CC=powerpc-unknown-linux-gnu-gcc usr The command line to build the kernel module is make ARCH= CROSS_COMPILE= \ KSRC= kernel e.g. make ARCH=powerpc CROSS_COMPILE=powerpc-unknown-linux-gnu- \ KSRC=/root/linux-2.6.30.2 kernel Configuration ------------- The daemon is configured via the configuration file /etc/ietd.conf. See the man page and the example file for the current syntax. The ietadm utility is for managing IET software dynamically. You can change the configurations of running targets. See the help message. The access control based on initiator address and target name patterns is configured via two configuration files (/etc/initiators.allow and /etc/initiators.deny). These files work like tcpd files (/etc/hosts.allow and /etc/hosts.deny). This feature enables you to hide a particular targets from some initiators. See the example files for the supported expressions. You can change the configuration dynamically. The modifications to the files become effective immediately. Starting ------------- The target is not started automatically. So execute: /etc/init.d/iscsi-target start Note that you must edit the configuration file before starting the target. Stopping ------------- Execute: /etc/init.d/iscsi-target stop Contact ------------- Please send bug reports, comments, feature requests etc. to our mailing list . Developer Notes ---------------- The central resource for IET development is the mailing list. Our subversion repository can be found at: http://iscsitarget.svn.sourceforge.net/svnroot/iscsitarget When submitting patches, please diff against the code in our repository's trunk and adhere otherwise to the same rules that apply to Linux kernel development, in particular the Linux kernel coding style ($KSRC/Documentation/CodingStyle) and the rules for submitting patches ($KSRC/Documentation/SubmittingPatches), i.e. please send patches inline as plain text. Also, please sign-off your patches according to the "Developer's Certificate of Origin", which can also be found in the Linux Kernel's SubmittingPatches document. iscsitarget-1.4.20.3+svn502/README.initiators000066400000000000000000000022631233224651300203610ustar00rootroot00000000000000New functionality of initiators.allow/deny ========================================== Prior to version 0.4.18 the way initiators.allow and initiators.deny worked was to read initiators.deny for any targets that matched and to blacklist any initiator ip addresses listed there, UNLESS the initiators' ip address is also explicitly allowed by an entry in initiators.allow. From version 0.4.18 on we are depreciating initiators.deny all together in favor of an implicit deny on any failure to match a target or initiator in initiators.allow. This provides all the flexibility of the two files with one less file to worry about misconfiguring. For backwards compatibility while we phase out initiators.deny, if initiators.deny exists then we will USE THE OLD METHOD, if it doesn't exist then we will use the newer implicit deny method. On new installations we will not install initiators.deny, but we will still distribute the example version under the etc directory in the archive. We will also not overwrite or delete existing versions of these files on install. Also, new versions of initiators.allow will have the default "ALL ALL" line at the bottom to allow any initiator access to any target. iscsitarget-1.4.20.3+svn502/README.mcs000066400000000000000000000027261233224651300167620ustar00rootroot00000000000000Multiple Connections per Session ================================ Starting in 1.4.20 IET has experimental support for multiple connections per session (MC/S). IET always supported multiple sessions by a given initiator, but since SCSI command ordering can only be preserved within a session this meant that commands could (and over time will) be issued out-of-order which causes sequential operations to randomize over time reducing the effective throughput considerably. By utilizing multiple connections per session this eliminates the need to use multiple sessions and MPIO on the host system, as the abstraction appears to the host system as a single disk and it is up to the initiator to handle the distribution of SCSI commands over the connections of a session. It should be noted though, that on the initiator side one should use either MC/S OR MPIO for a target-initiator pair as on many operating systems the behavior is undefined when using both. Linux maintainers have decided to only support MPIO for it's initiators. Microsoft supports both, but says that using both at once on a given target-initiator pair is unsupported. IET being an iSCSI target really doesn't care either way, as it will process commands in the order per the RFC spec, but it is highly recommend you don't use both at once unless MPIO is used in a strictly active-passive fail-over fashion, while MC/S is used active-active, any other combination of MPIO and MC/S will most likely find you in trouble. iscsitarget-1.4.20.3+svn502/README.vmware000066400000000000000000000046611233224651300175010ustar00rootroot00000000000000iet vmware esx (v3) disk identification ======================================= Disks, also known as LUNs, can be identified by the underlying SCSI protocol using the vital product pages (VPD). Page 0x83 contains the unit identification (required by SCSI spec), page 0x80 contains the disk serial number page (optional as per SCSI spec). iet provides a feature that allows one to define a ScsiId parameter within the Lun statement. This parameter controls the value of page 0x83. Before SVN rev 72 (including 0.4.14) did not control page 0x80. vmware and disk serial numbers ------------------------------ VMware ESX uses page 0x80 to identify disks. If two disks have the same serial number, then vmware thinks there are multiple paths to that disk. This becomes obvious if you have multiple targets defined in iet or you have targets defined on different iet servers. This is an issue with all versions prior and including release 0.4.14 of iet. Target iqn.1990-01.edu.amherst.iscsi-target-development:vmware.t1 Lun 0 Path=/mnt/bigslice1,Type=fileio Target iqn.1990-01.edu.amherst.iscsi-target-development:vmware.t2 Lun 0 Path=/mnt/bigslice2,Type=fileio So, if the bigslice1 is 100G and bigslice2 is 200G, you will see one LUN in vmware which might have the size 100G or 200G consisting of 2 paths to that disk. This gets really ugly if one path is down and it then accesses a different disk. Prior to SVN rev 75 ------------------- Before SVN rev 75 (this includes 0.4.14), iet emitted a 4 byte long disk serial number consisting of spaces (0x20). In vmware you can see this by looking at the Id line in /proc/vmware/scsi/hba40/... Id: 20 20 20 20 56 49 52 54 55 41 As it turns out, vmware also appends this resulting Id to the first 6 bytes of the product identification field, which is VIRTUA (VIRTUAL-DISK). To avoid all this ----------------- After and including SVN rev 75, you can configure serial numbers in your ietd.conf: Lun 0 Path=/dev/sdc,Type=fileio,ScsiSN=AMHDSK-061031-03 Note if you do not specify a ScsiSN, like all the previous versions, iet emitted a 4 byte long disk serial number consisting of spaces (0x20). References: ----------- http://www.vmware.com/community/message.jspa?messageID=364352 Todo Items ---------- VMware currently does not certify iet as a compatible iSCSI solution. The open source community is trying to make iet as compatible as possible. Written by Steffen Plotner. Last updated 2006/12/05 iscsitarget-1.4.20.3+svn502/RELEASE_NOTES000066400000000000000000000214271233224651300172730ustar00rootroot00000000000000 ========================================= Release Notes for iSCSI Enterprise Target ========================================= March 18th, 2011 Version 1.4.20.3 ---------------- The IET development team is pleased to announce the release of version 1.4.20.3 of the iSCSI Enterprise Target. This release is a bugfix release. The following issues were fixed: - multiple compatibility updates to bring kernel support up to 2.6.38 - fixed iSNS issue that prevented IET from registering - fixed re-enabling listening after max connections was exceeded - fixed NULL-pointer dereference due to race in UA handling - fixed initd scripts to reflect pid file name change - fixed crypto API error handling - fixed small memory leak in volume args buffer on BSD platforms - fixed max_cmd_sn not being set for the session - fixed issue where StatSN was being updated when a command was ignored - fixed connection accounting in ietd for session reinstatements - MaxOutStandingR2T fixed at 1 for 1.4.20 branch due to broken handling - fixed ipv6 address handling in SendTargets ========================================= Release Notes for iSCSI Enterprise Target ========================================= July 14th, 2010 Version 1.4.20.2 ---------------- The IET development team is pleased to announce the release of version 1.4.20.2 of the iSCSI Enterprise Target. This release is a bugfix release. The following issues were fixed: - various issues in the iSNS code resulting from buffer boundaries not being checked (CVE-2010-2221) - memory leak in the iSNS ACL code - the kernel module allocating UAs in atomic context could cause a soft lockup - handling iSCSI logins was faulty, causing issues with QLogic HBAs - ietd was not sufficiently protected against multiple instances - ietd exited when trying to remove targets with active sessions - MD5/SHA-1 used for CHAP authentication were faulty on big endian platforms ========================================= Release Notes for iSCSI Enterprise Target ========================================= April 25th, 2010 Version 1.4.20.1 ---------------- The IET development team is pleased to announce the release of version 1.4.20.1 of the iSCSI Enterprise Target. This release is a bugfix release. The following issues were fixed: - VMWare ESX does not recognize existing IET LUs after upgrading from older IET versions to 1.4.20 because of a change in the SCSI SN. The old behaviour from IET versions < 1.4.20 is restored with this release. Thanks to Steffen Plotner for pointing it out and helping in debugging / testing. - With kernel 2.6.33 the utsrelease.h used by IETs backward compatibility infrastructure was moved to a new location. This lead to compilation issues with kernels >= 2.6.33. April 13th, 2010 Version 1.4.20 -------------- The IET development team is pleased to announce the release of version 1.4.20 of the iSCSI Enterprise Target. This release includes numerous bug fixes and compatibility improvements and it is highly recommended that all production servers be on this version for compatibility and stability reasons. We would like to thank all those that contributed for this release. As we are a small project we rely heavily on user contributions and we welcome all who wish to participate in improving IET. New since 1.4.19: New Features: - Added ability to atomically delete all targets/sessions/connections - Added ability to move existing kernel module on install and move back on uninstall per distribution requirements (dpkg-divert) - Added support for logical sector sizes (512, 1024, 2048, 4096) and devices whose native sector size > 512 - Added case insensitive volume parameter processing - Added ability to set temporary target redirections via ietadm for existing targets - Added support for auto-generating MD5 hash for SCSI ID, if not specified, and auto-setting SCSI SN to the hex of the SCSI ID, if not specified. Experimental Features: - Added experimental support for multiple connections per session Updated Features: - Updated kernel compatibility to 2.6.33 - Updated RPM and DKMS build/install/maintenance handling - Updated kernel and usr ioctl functions to perform version checking between kernel module and ietd Fixes: - Fixed bug in ietd text final text response - Fixed bug with reservation release during session deletion - Fixed compliance issues around ABORT TASK/ABORT TASK SET - Fixed fileio so it should work with all file system types - Fixed session and connection reinstatement to work per RFC - Fixed performance issue with CFQ scheduler - Fixed corner case with fileio_sync missing last page in a sync - Fixed bug in ietd where an interface without an address would cause it to go into an infinite loop. - Fixed race condition between issuing ioctls during kernel cleanup - Fixed race condition between ietd shutdown and initiators which would cause initiators to fail to reconnect properly - Fixed a race condition with data-out handling with multiple connections per session - Fixed possible wthread lost wakeup condition when the number of wthreads is less then the number of CPUs/cores. November 15th, 2009 Version 1.4.19 -------------- The IET development team is pleased to announce the release of version 1.4.19 of the iSCSI Enterprise Target. This release includes numerous bug fixes and compatibility improvements and it is highly recommended that all production servers be on this version for compatibility and stability reasons. We would like to thank all those that contributed for this release. As we are a small project we rely heavily on user contributions and we welcome all who wish to participate in improving IET. New since 1.4.18: - Added ability to limit number of sessions per target (MaxSessions) - Added NOP-In heartbeat for connections so dropped or abandoned connections and their sessions close faster - Fixed a serious bug in initiators.allow code - Updated RPM .spec file with added SuSE compatibility and better overall cross-platform friendliness. October 5th, 2009 Version 1.4.18 -------------- The version numbering of IET has changed in this release from the 0.X.X series to the 1.X.X series. We did this because we felt that, since IET has been stable and in production use for many years now, it deserved a version number that better reflected that stability and maturity. We are preserving the minor/maintenance numbers though in order to help maintain some consistency between prior and future releases. New since 0.4.17: - Added support for recent kernel versions up to 2.6.31 - Added support for even more targets via: * multi PDU support during discovery * improved procfs support * global thread pool (module parameter) - Added support to list all target addresses during discovery, with the ability to filter those addresses via targets.allow - Replaced initiators.deny with an implicit deny on a failure to find a match in initiators.allow while preserving backwards compatiblity with existing installations (iff initiators.deny doesn't exist) - Added ability to filter initiators by their IQNs as well as their IP addresses, using basic regex pattern to specify IQNs - Moved the config files to /etc/iet/ while preserving backwards compatiblity with existing installations (iff a valid config file exists in the new location will it be used) - Added support for Unit Attention Conditions * SCSI RESERVE/RELEASE now issues a UAC on reservation loss - Added support for DKMS and building RPM packages right from the source archive (from either the source tar ball or subversion) Plus many many bug and compatibility fixes (see ChangeLog for details) Special thanks go to: --------------------- Shreyansh Jain, Lars Ellenberg, Ming Zhang, Francois Micaux, Cheng Renquan, Andreas Florath, sdrb, Xie Gang, Stefan Rubner, FUJITA Tomonori, Oliver R., Matthew Wild, Kevin Ye, Min Tran, Luca Berra, Kun Huang, VMware, spren, Jagadish Kumar, Steven Umbehocker, Emmanuel Florac, Harshal Shete, Tim Westervoorde, Andrei Tanas, Chris Siebenmann, Manoj Iyer, Rob Caldwell Pasi Karkkainen, Eugen Rieck and to all list members who submitted bug reports, suggestions and comments. Without whose contributions IET would not have been possible. Arne Redlich & Ross Walker iscsitarget-1.4.20.3+svn502/dkms.conf000066400000000000000000000057061233224651300171270ustar00rootroot00000000000000# # Master copy of dkms.conf for iscsitarget # PACKAGE_NAME="iscsitarget" PACKAGE_VERSION="trunk" MOD_PATH=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION} BUILT_MODULE_NAME="iscsi_trgt" BUILT_MODULE_LOCATION="kernel" DEST_MODULE_LOCATION="/kernel/iscsi" MAKE="make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build/kernel modules" CLEAN="make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build/kernel clean" AUTOINSTALL="yes" # # Patches newest - oldest, distro spec at bottom # PATCH[0]="compat-3.9.patch" PATCH_MATCH[0]="(2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39)|3\.(0|1|2|3|4|5|6|7|8|9)\.0)" PATCH[1]="compat-3.6.patch" PATCH_MATCH[1]="(2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39)|3\.(0|1|2|3|4|5|6)\.0)" PATCH[2]="compat-3.5.patch" PATCH_MATCH[2]="(2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39)|3\.(0|1|2|3|4|5)\.0)" PATCH[3]="compat-3.2-3.4.patch" PATCH_MATCH[3]="(2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39)|3\.(0|1|2|3|4)\.0)" PATCH[4]="compat-2.6.39-3.2.patch" PATCH_MATCH[4]="(2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39)|3\.(0|1|2)\.0)" PATCH[5]="compat-2.6.38.patch" PATCH_MATCH[5]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38)" PATCH[6]="compat-2.6.36-2.6.37.patch" PATCH_MATCH[6]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37)" PATCH[7]="compat-2.6.33-2.6.35.patch" PATCH_MATCH[7]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35)" PATCH[8]="compat-2.6.32.patch" PATCH_MATCH[8]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32)" PATCH[9]="compat-2.6.31.patch" PATCH_MATCH[9]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)" PATCH[10]="compat-2.6.30.patch" PATCH_MATCH[10]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30)" PATCH[11]="compat-2.6.29.patch" PATCH_MATCH[11]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29)" PATCH[12]="compat-2.6.28.patch" PATCH_MATCH[12]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28)" PATCH[13]="compat-2.6.25-2.6.27.patch" PATCH_MATCH[13]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24|25|26|27)" PATCH[14]="compat-2.6.24.patch" PATCH_MATCH[14]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23|24)" PATCH[15]="compat-2.6.23.patch" PATCH_MATCH[15]="2\.6\.(9|14|15|16|17|18|19|20|21|22|23)" PATCH[16]="compat-2.6.22.patch" PATCH_MATCH[16]="2\.6\.(9|14|15|16|17|18|19|20|21|22)" PATCH[17]="compat-2.6.19-2.6.21.patch" PATCH_MATCH[17]="2\.6\.(9|14|15|16|17|18|19|20|21)" PATCH[18]="compat-2.6.14-2.6.18.patch" PATCH_MATCH[18]="2\.6\.(9|14|15|16|17|18)" PATCH[19]="compat-sles10sp2.patch" PATCH_MATCH[19]="2\.6\.16\.60-.*" PATCH[20]="compat-rhel4.patch" PATCH_MATCH[20]="2\.6\.9-.*\.(el|plus\.c4)" iscsitarget-1.4.20.3+svn502/doc/000077500000000000000000000000001233224651300160575ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/doc/manpages/000077500000000000000000000000001233224651300176525ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/doc/manpages/ietadm.8000066400000000000000000000207021233224651300212070ustar00rootroot00000000000000.\" Automatically generated by Pod::Man 2.09 (Pod::Simple 3.04) .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "IETADM 1" .TH IETADM 8 "2006-06-06" "iSCSI Enterprise Target admin" "User Manuals" .SH "NAME" ietadm \- iSCSI Enterprise Target Administration Utility. .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBietadm \-\-op [operation] \-\-tid=[id] [\-\-sid [id]] [\-\-params [key=value,...]]\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" ietadm is used to monitor and modify in real-time the iSCSI Enterprise Target targets and volumes. .SH "USAGE" .IX Header "USAGE" You'll have to get target ids from /proc/net/iet/volumes and sessions ids from /proc/net/iet/session. Some usage examples can be : .PP \&\fBietadm \-\-op show \-\-tid=1\fR .PP display status of target 1 (see /proc/net/iet/volumes to get the matching target name) .PP \&\fBietadm \-\-op new \-\-tid=2\fR .PP create dynamically a new target, numbered 2. \s-1CAUTION\s0 : the target will disappear if you restart ietd, you'll have to edit /etc/ietd.conf to make it permanent! .SH "ERROR MESSAGES" .IX Header "ERROR MESSAGES" ietadm misses error messages. Look carefully at the \s-1STDERR\s0 output : in case of error it will send a 3 number error code, ending with \-1, for instance : .PP ietd_request 203 3 \-1 .SH "OPTIONS" .IX Header "OPTIONS" \&\fB\-\-op new \-\-tid=[id] \-\-params Name=[name]\fR .PP add a new target with [id]. If [id] is zero, the lowest free [id] is allocated automatically to the new target. .PP \&\fB\-\-op delete \-\-tid=[id]\fR .PP delete specific target with [id]. The target must have no active sessions. .PP \&\fB\-\-op show \-\-tid=[id]\fR .PP show target parameters of target with [id]. .PP \&\fB\-\-op show \-\-tid=[id] \-\-sid=[sid]\fR .PP show iSCSI parameters in effect for session [sid]. If [sid] is \*(L"0\*(R" (zero), the configured parameters will be displayed. .PP \&\fB\-\-op new \-\-tid=[id] \-\-lun=[lun] \-\-params Path=[path]\fR .PP add a new logical unit with [lun] to specific target with [id]. The logical unit is offered to the initiators. [path] must be block device files (including \s-1LVM\s0 and \s-1RAID\s0 devices) or regular files. .PP \&\fB\-\-op delete \-\-tid=[id] \-\-lun=[lun]\fR .PP delete specific logical unit with [lun] that the target with [id] has. .PP \&\fB\-\-op delete \-\-tid=[id] \-\-sid=[sid] \-\-cid=[cid]\fR .PP delete specific connection with [cid] in a session with [sid] that the target with [id] has. If the session has no connections after the operation, the session will be deleted automatically. .PP \&\fB\-\-op delete\fR .PP stop all activity. .PP \&\fB\-\-op update \-\-tid=[id] \-\-params=key1=value1,key2=value2,...\fR .PP change iSCSI \s-1IET\s0 target parameters of specific target with [id]. You can use parameters in ietd.conf as a key. .PP \&\fB\-\-op update \-\-tid=[id] \-\-redirect=[destination]\fR .PP set a temporary target redirection for target [id]. [destination] MUST be in the form :port where :port is optional and if not specified port 3260 is assumed. IPv6 addresses must be surrounded by square brackets, e.g. []:port. If the destination is empty then any existing redirection is cleared. .PP \&\fB\-\-op new \-\-tid=[id] \-\-user \-\-params=[user]=[name],Password=[pass]\fR .PP add a new account with [pass] for specific target. [user] could be [IncomingUser] or [OutgoingUser]. If you don't specify a target (omit \-\-tid option), you add a new account for discovery sessions. .PP \&\fB\-\-op show \-\-tid=[id] \-\-user\fR .PP show a list of CHAP accounts. If \-\-tid is omitted or [id] is \*(L"0\*(R" (zero), discovery accounts are displayed. .PP \&\fB\-\-op show \-\-tid=[id] \-\-user \-\-params=[user]=[name]\fR .PP show CHAP account information for the account specified by [name]. [user] can be [IncomingUser] or [OutgoingUser]. If \-\-tid is omitted or [id] is \*(L"0\*(R" (zero), [name] is supposed to be a discovery account name. .PP \&\fB\-\-op delete \-\-tid=[id] \-\-user \-\-params=[user]=[name]\fR .PP delete specific account having [name] of specific target. [user] could be [IncomingUser] or [OutgoingUser]. If you don't specify a target (omit \-\-tid option), you delete the account for discovery sessions. .PP \&\fB\-\-version\fR .PP display version and exit .PP \&\fB\-\-help\fR .PP display a list of available options and exits .SH "KNOWN ISSUES" .IX Header "KNOWN ISSUES" .IP "\(bu" 4 ietdadm doesn't return any human-readable error message, only error codes. .IP "\(bu" 4 ietadm doesn't modify or read the /etc/ietd.conf ietd configuration file. .IP "\(bu" 4 ietadm can't use target names or aliases, only the tid found in /proc/net/iet/volumes. .IP "\(bu" 4 /proc/net/iet/session may list inactive sessions if the initiator doesn't logout properly. .PP Report bugs to . .SH "FILES" .IX Header "FILES" /proc/net/iet/session, /proc/net/iet/volumes .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIietd\fR\|(8),\fIietd.conf\fR\|(5) .\"man page written by Emmanuel Florac sid.id64 .RE "need to shutdown target %s" .RS Value(s): target->name .RE .B Errors "unable to create server socket (%s)!" .RS Value(s): strerror(errno) .RE "unable to bind server socket (%s)!" .RS Value(s): strerror(errno) .RE "unable to listen to server socket (%s)!" .RS Value(s): strerror(errno) .RE "error reading /proc/iscsi/iscsi (%d)" .RS Value(s): errno .RE "unable to create pid file" "starting daemon failed" "unable to lock pid file" "unable to open %s! (%s)" .RS Value(s): PROC_DEVICEDIR, strerror(errno) .RE "unable to open %s! (%s)" .RS Value(s): PROC_TARGETDIR, strerror(errno) .RE .B Debug Messages Level 0 "unknown user %s" .RS Value(s): value .RE "login by %s failed" .RS Value(s): user->name .RE "succesfull login by %s" .RS Value(s): user->name .RE "connection request from %s" .RS Value(s): inet_ntoa(name.sin_addr) .RE "connection closed" "lun %u:%u already configured" .RS Value(s): target->id, tlun->lun .RE .B Debug Messages Level 1 "conn_take_fd: %u %u %u %Lx %d" .RS Value(s): conn->cid, conn->stat_sn, conn->exp_stat_sn, conn->sid.id64, fd .RE "exp_cmd_sn: %d,%d" .RS Value(s): conn->exp_cmd_sn, req->cmd_sn .RE "Login request (security negotiation): %d" .RS Value(s): conn->state .RE "Login request (operational negotiation): %d" .RS Value(s): conn->state .RE "Text request: %d" .RS Value(s): conn->state .RE "close conn %u session %Lx target %u" .RS Value(s): conn_id, session_id, target_id .RE "session_find_name: %s,%#Lx" .RS Value(s): iname, sid.id64 .RE "session_find_id: %#Lx" .RS Value(s): sid .RE "session_create: %#Lx" .RS Value(s): session->sid.id64 .RE "session_close: %#Lx" .RS Value(s): session->sid.id64 .RE "session_remove: %#Lx" .RS Value(s): session->sid.id64 .RE "active target %d: %s" .RS Value(s): id, name .RE "removing target %d" .RS Value(s): id .RE "creaing target %u: %s" .RS Value(s): target_next_id, p .RE "attaching %u to %u:%u" .RS Value(s): tlun->device->id, target->id, tlun->lun .RE "target_find_name: %s" .RS Value(s): name .RE "target_find_id: %u" .RS Value(s): id .RE "target_remove: %u,%s" .RS Value(s): target->id, target->name .RE .B Debug Messages Level 2 "%s %.16s" .RS Value(s): line, buf .RE "BHS: (%p)" .RS Value(s): buf .RE "AHS: (%p)" .RS Value(s): buf .RE "Data: (%p)" .RS Value(s): buf .RE .SH KNOWN ISSUES Task attributes is incomplete (all tasks are treated as if they have the SIMPLE attribute.) SCSI task management is incomplete. NOP-In is incomplete. The target never sends a NOP-In of its own accord. Header and Data Digest in a discovery session are not implemented. Out-of-memory situation leads to the system crash (There are still some out-of-memory bugs.) .SH "SEE ALSO" .BR ietd.conf (5) iscsitarget-1.4.20.3+svn502/doc/manpages/ietd.conf.5000066400000000000000000000330511233224651300216130ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii ietd.conf.5 .\" .TH "IETD.CONF" "5" "27 July 2005" "A. Lehmann, M. Zhang and A. Redlich" "File formats" .SH "NAME" /etc/ietd.conf \- configuration for iSCSI Enterprise Target Daemon .SH "SYNOPSIS" /etc/ietd.conf .SH "DESCRIPTION" /etc/ietd.conf contains configuration information for the .B ietd (8) command. This is the place, where you configure your iSCSI targets and daemon defaults. .P Only lines starting with `#' are ignored. Putting '#' in the middle of a line is disallowed. A line may be extended across multiple lines by making the last character a backslash. .P The "Yes" and "No" for parameter values are case sensitive. The parameter names are case insensitive. .P The file consists of a global part and zero or more "Target" stanzas. Everything until the first target definition belongs to the global configuration. Here is an example: IncomingUser joe secret .br OutgoingUser jack secret2 Target iqn.2001\-04.com.example:storage.disk2.sys1.xyz IncomingUser jim othersecret OutgoingUser james yetanothersecret Lun 0 Path=/dev/sdc,Type=fileio Lun 1 Blocks=10000,BlockSize=4096,Type=nullio Alias Test HeaderDigest None DataDigest None MaxConnections 1 MaxSessions 0 InitialR2T Yes ImmediateData No MaxRecvDataSegmentLength 8192 MaxXmitDataSegmentLength 8192 MaxBurstLength 262144 FirstBurstLength 65536 DefaultTime2Wait 2 DefaultTime2Retain 0 MaxOutstandingR2T 8 NOPInterval 0 NOPTimeout 0 DataPDUInOrder Yes DataSequenceInOrder Yes ErrorRecoveryLevel 0 .P Stanzas start with the word "Target" and the target name. This name must be a globally unique name, as defined by the iSCSI standard : the "iSCSI Qualified Name". The daemon brings the targets up in the order listed. .SH "GLOBAL OPTIONS" Global Options are case sensitive. .TP .B [IncomingUser ] The .I and .I used during discovery sessions to authenticate iSCSI initiators. Several of those can be specified for discovery. If no .B IncomingUser is specified, any initiator is allowed to open a discovery session. .RS HINT: RFC 3720 requires .I to be 12 characters long. This is enforced e.g. by MS Initiator. .RE .TP .B [OutgoingUser ] The .I and .I used during discovery sessions to authenticate the target to initiators. Only one outgoing .I / combination may be specified. .RS HINT: RFC 3720 requires .I to be 12 characters long. This is enforced e.g. by MS Initiator. .RE .TP .B Target iqn..[:] A target definition and the target name. The targets name (the .B iSCSI Qualified Name ) must be a globally unique name (as defined by the iSCSI standard) and has to start with .I iqn followed by a single dot. The EUI\-64 form is not supported. .I is the date (year and month) at which the domain is valid. This has to be followed by a single dot and the reversed domain name. The optional .I \- which is freely selectable \- has to be separated by a single colon. For further details please check the iSCSI spec. Here is an example: Target iqn.2004\-07.com.example.host:storage.disk2.sys1.xyz .SH "TARGET OPTIONS" Target options are also case sensitive. .TP .B [IncomingUser ] The .I and .I used to authenticate the iSCSI initiators to this target. It may be different from the username and password in section GLOBAL OPTIONS, which is used for discovery. If you omit the .B IncomingUser Option, connections are allowed without authentication. A .I has to be provided, if there is a .I given. Specifying several different .B IncomingUser accounts is supported. .TP .B [OutgoingUser ] The .I and .I used to authenticate this iSCSI target to initiators. Only one .B OutgoingUser per target is supported. It may be different from the username and password in section GLOBAL OPTIONS, which is used for discovery. A .I has to be provided, if there is a .I given. .TP .nf .B Lun Type=(fileio|blockio),Path=[,ScsiId=][,ScsiSN=][,IOMode=(wb|ro|wt)][,BlockSize=] .TP .B Lun Type=nullio[,Blocks=][,BlockSize=] .fi .RS Parameters after should not contain any blank space characters except the first blank space after is needed. .P The value of .I can be from 0 to 16384. The first .I defined MUST be 0. .P In .I fileio mode (default), it defines a mapping between a "Logical Unit Number" .I and a given device .I , which can be any block device (including regular block devices like hdX and sdX and virtual block devices like LVM and Software RAID devices) or regular files. .P In .I blockio mode, it defines a mapping between a "Logical Unit Number" .I and a given block device .I . This mode will perform direct block i/o with the device, bypassing page-cache for all operations. This allows for efficient handling of non-aligned sector transfers (virtualized environments) and large block transfers (media servers). This mode works ideally with high-end storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. .P A .I ScsiId can be specified to assign a unique SCSI ID (VPD 0x83) to an iSCSI volume. This is used by initiator hosts to uniquely identify a SCSI volume. This is necessary with multipath\-aware hosts accessing the same .I through multiple iSCSI sessions. The .I must not exceed 16 characters. .P Also a .I ScsiSN can be specified to assign a unique serial number (VPD 0x80) to an iSCSI volume. This is used by certain initiator hosts (e.g. VMware ESX) to uniquely identify a SCSI volume. This is essential when used in conjunction with VMware ESX hosts accessing the same .I through multiple iSCSI sessions. The .I must not exceed 16 characters. .P By default LUNs are writable, employing write-through caching. By setting .I IOMode to "ro" a LUN can be set to read only mode. Setting .I IOMode to "wb" will enable write-back caching of a LUN. Setting .I IOMode to "wt" is only symbolic as that is the default behavior. .P .B NOTE: .I IOMode "wb" is ignored when employing blockio as it performs no caching. .P .B WARNING: IOMode=wb could lead to serious data loss from an unexpected system failure (power loss, system crash). Use at your own risk! .P You can specify a logical .I BlockSize for an iSCSI volume. This .I must be one of (512, 1024, 2048, 4096). If .I BlockSize isn't specified the default is 512 bytes for fileio, and the logical block size of the lower level device for blockio (which typically is 512 bytes). .P .B NOTE: For blockio, you CANNOT specify a .I BlockSize less than the lower level device's logical block size which is the minimum size the device can handle. An error will be reported and the LUN will fail to attach if you try. You CAN use fileio though to export a LUN with a .I BlockSize less than the lower level device's logical block size. .P .B WARNING: Once your data is written at a given BlockSize you cannot change this BlockSize without risking corruption of your existing data. .P In .I nullio mode, it defines a mapping between a "Logical Unit Number" .I and an unnamed virtual device with a specified number of .I Blocks of .I BlockSize bytes. If .I Blocks is not specified then it will default to 64GB's worth given the curent .I BlockSize which defaults to 512 bytes. This is ONLY useful for performance measurement purposes. All writes to this virtual device will be discarded and all reads will return random data. .P .B WARNING: By sending random kernel memory over the IP network you can potentially expose sensitive information. .RE .TP .B [Alias ] This assigns an optional .I to the target. .TP .B [HeaderDigest ] Optional. If set to "CRC32C" and the initiator is configured accordingly, the integrity of an iSCSI PDU's header segments will be protected by a CRC32C checksum. The default is "None". Note that header digests are not supported during discovery sessions. .TP .B [DataDigest ] Optional. If set to "CRC32C" and the initiator is configured accordingly, the integrity of an iSCSI PDU's data segment will be protected by a CRC32C checksum. The default is "None". Note that data digests are not supported during discovery sessions. .TP .B [MaxConnections ] Optional. The number of connections within a session. Has to be set to "1" (in words: one), which is also the default since MC/S is not supported. .TP .B [MaxSessions ] Optional. The maximum number of sessions for this target. If this is set to 0 (wich is the default) there is no explicit session limit. .TP .B [InitialR2T ] Optional. If set to "Yes" (default), the initiator has to wait for the target to solicit SCSI data before sending it. Setting it to "No" allows the initiator to send a burst of .B FirstBurstLength bytes unsolicited right after and/or (depending on the setting of .B ImmediateData ) together with the command. Thus setting it to "No" may improve performance. .TP .B [ImmediateData ] Optional. This allows the initiator to append unsolicited data to a command. To achieve better performance, this should be set to "Yes". The default is "No". .TP .B [MaxRecvDataSegmentLength ] Optional. Sets the maximum data segment length that can be received. The .I should be set to multiples of PAGE_SIZE. Currently the maximum supported value is 64 * PAGE_SIZE, e.g. 262144 if PAGE_SIZE is 4kB. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is 8192. .TP .B [MaxXmitDataSegmentLength ] Optional. Sets the maximum data segment length that can be sent. The .I actually used is the minimum of .B MaxXmitDataSegmentLength and the .B MaxRecvDataSegmentLength announced by the initiator. The .I should be set to multiples of PAGE_SIZE. Currently the maximum supported value is 64 * PAGE_SIZE, e.g. 262144 if PAGE_SIZE is 4kB. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is 8192. .TP .B [MaxBurstLength ] Optional. Sets the maximum amount of either unsolicited or solicited data the initiator may send in a single burst. Any amount of data exceeding this value must be explicitly solicited by the target. The .I should be set to multiples of PAGE_SIZE. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is 262144. .TP .B [FirstBurstLength ] Optional. Sets the amount of unsolicited data the initiator may transmit in the first burst of a transfer either with and/or right after the command, depending on the settings of .B InitialR2T and .B ImmediateData . .I should be set to multiples of PAGE_SIZE. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is 65536. .TP .B [DefaultTime2Wait ] Currently not implemented, but can be used to set how long initiators wait before logging back in after a connection is logged out or dropped. .TP .B [DefaultTime2Retain ] Currently we only support 0 which means sessions are not retained after the last connection is logged out or dropped. .TP .B [MaxOutstandingR2T ] Optional. Controls the maximum number of data transfers the target may request at once, each of up to .B MaxBurstLength bytes. The default is 1. .TP .B [DataPDUInOrder ] Optional. Has to be set to "Yes" \- which is also the default. .TP .B [DataSequenceInOrder ] Optional. Has to be set to "Yes" \- which is also the default. .TP .B [ErrorRecoveryLevel ] Optional. Has to be set to "0" (in words: zero), which is also the default. .TP .B [NOPInterval ] Optional. If .I value is non-zero, the initiator will be "ping"ed during phases of inactivity (i.e. no data transfers) every .I value seconds to verify the connection is still alive. If the initiator fails to respond within .B NOPTimeout seconds, the connection will be closed. .TP .B [NOPTimeout ] Optional. If a non-zero .B NOPInterval is used to periodically "ping" the initiator during phases of inactivity (i.e. no data transfers), the initiator must respond within .I value seconds, otherwise the connection will be closed. If .I value is set to zero or if it exceeds .B NOPInterval , it will be set to .B NOPInterval. .TP .B [Wthreads ] Optional. The iSCSI target employs several threads to perform the actual block I/O to the device. Depending on your hardware and your (expected) workload, the number of these threads may be carefully adjusted. The default value of 8 should be sufficient for most purposes. .TP .B [QueuedCommands ] Optional. This parameter defines a window of commands an initiator may send and that will be buffered by the target. Depending on your hardware and your (expected) workload, the .I value may be carefully adjusted. The default value of 32 should be sufficient for most purposes. .SH "KNOWN BUGS/LIMITATIONS" Currently (as of 0.4.11) not all iSCSI target parameters are used. Header and data digests are not supported during discovery sessions. .SH "SEE ALSO" .B ietd (8) .TP You should have a look at .B RFC 3720 for all the glory details. iscsitarget-1.4.20.3+svn502/etc/000077500000000000000000000000001233224651300160655ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/etc/ietd.conf000066400000000000000000000076021233224651300176660ustar00rootroot00000000000000# Example iscsi target configuration # # Everything until the first target definition belongs # to the global configuration. # # "iSNSServer" is the iSNS server you want your portal to register # with. # # "iSNSAccessControl" is for enabling initiator access control # through the iSNS server. # # "IncomingUser" specifies credentials the initiator has to provide - # several of these are supported. If mutual CHAP shall be employed, # "OutgoingUser" specifies the user/pass combination the target will # provide - only one is supported. # # Leave them alone (keep them commented out) if you don't want to use # authentication for discovery sessions. #iSNSServer 192.168.1.16 #iSNSAccessControl No #IncomingUser joe secret #OutgoingUser jack 12charsecret # Targets definitions start with "Target" and the target name. # The target name must be a globally unique name, the iSCSI # standard defines the "iSCSI Qualified Name" as follows: # # iqn.yyyy-mm.[:identifier] # # "yyyy-mm" is the date at which the domain is valid and the identifier # is freely selectable. For further details please check the iSCSI spec. #Target iqn.2001-04.com.example:storage.disk2.sys1.xyz # CHAP Users # # The same rules as for discovery users apply here. # # Don't set them if you don't want to use CHAP authentication. # #IncomingUser joe secret #OutgoingUser jim 12charpasswd # # Logical Unit definition # # Block devices, regular files (fileio only), LVM, and RAID # can be offered to the initiators as a block device. # # Lun numbers MUST start with zero (each target needs a Lun 0) # #Lun 0 Path=/dev/sdc,Type=fileio,ScsiId=xyz,ScsiSN=xyz # # Alias name for this target (Not Used) # #Alias Test # # Various iSCSI parameters # (not all are used right now, see also iSCSI spec for details) # # Outgoing SCSI data (initiator to target user data or command # parameters) is sent as either solicited data or unsolicited data. # Solicited data is sent in response to R2T PDUs. Unsolicited data # can be sent as part of an iSCSI command PDU sequence # ("Immediate Data") or as a separate iSCSI data PDU sequence. # #MaxConnections 1 # Number of connections/session # We only support 1 #MaxSessions 0 # Number of sessions/target # 0 = no explicit limit #InitialR2T Yes # Wait first for R2T # Yes = no unsolicited data #ImmediateData Yes # Data can accompany command # Yes = cmnd/data in same PDU #MaxRecvDataSegmentLength 8192 # Max data per PDU to receive #MaxXmitDataSegmentLength 8192 # Max data per PDU to transmit #MaxBurstLength 262144 # Max data per sequence (R2T) #FirstBurstLength 65536 # Max unsolicited data sequence #DefaultTime2Wait 2 # Secs to wait for ini to logout # also secs for ini to wait # before logging back in # Not implemented, but settable #DefaultTime2Retain 0 # Secs keep session after logout # We only support 0 #MaxOutstandingR2T 1 # Max outstanding R2Ts per cmnd #DataPDUInOrder Yes # Data in PDUs is ordered # We only support ordered #DataSequenceInOrder Yes # PDUs in sequence are ordered # We only support ordered #ErrorRecoveryLevel 0 # We only support level 0 #HeaderDigest None,CRC32C # PDU header checksum algo list # None or CRC32C # If only one is set then the # initiator must agree to it # or the connection will fail #DataDigest None,CRC32C # PDU data checksum algo list # Same as above #MaxSessions 0 # Maximum number of sessions to # this target - 0 = unlimited #NOPInterval 0 # Send a NOP-In ping each after # that many seconds if the conn # is otherwise idle - 0 = off #NOPTimeout 0 # Wait that many seconds for a # response on a NOP-In ping # If 0 or > NOPInterval, NOPInterval # is used! # # Various target parameters # #Wthreads 8 # Number of IO threads #QueuedCommands 32 # Number of queued commands iscsitarget-1.4.20.3+svn502/etc/initd/000077500000000000000000000000001233224651300171745ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/etc/initd/initd000066400000000000000000000007001233224651300202230ustar00rootroot00000000000000#!/bin/sh # # Start the iSCSI Enterprise Target. # PATH=/sbin:/bin:/usr/sbin:/usr/bin OPTIONS="" if [ -f /etc/sysconfig/iscsi-target ]; then . /etc/sysconfig/iscsi-target fi start_server() { modprobe -q crc32c modprobe iscsi_trgt /usr/sbin/ietd $OPTIONS } stop_server() { ietadm --op delete killall ietd } case "$1" in start) start_server ;; stop) stop_server ;; *) echo "Usage: {start|stop}" >&2 exit 1 ;; esac exit 0 iscsitarget-1.4.20.3+svn502/etc/initd/initd.debian000066400000000000000000000023121233224651300214450ustar00rootroot00000000000000#!/bin/sh # # chkconfig: - 39 35 # description: Starts and stops the iSCSI target # debianized start-stop script PATH=/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/ietd OPTIONS="" PIDFILE=/var/run/ietd.pid if [ -f /lib/init/vars.sh ]; then . /lib/init/vars.sh fi if [ -f /lib/lsb/init-functions ]; then . /lib/lsb/init-functions fi if [ -f /etc/sysconfig/iscsi-target ]; then . /etc/sysconfig/iscsi-target fi RETVAL=0 ietd_start() { log_daemon_msg "Starting iSCSI Target" "ietd" modprobe -q crc32c modprobe iscsi_trgt start-stop-daemon --start --exec $DAEMON --pidfile $PIDFILE -- $OPTIONS log_end_msg $? } ietd_stop() { log_daemon_msg "Stopping iSCSI Target" "ietd" ietadm --op delete start-stop-daemon --stop --exec $DAEMON --pidfile $PIDFILE rm -f $PIDFILE log_end_msg $? } ietd_status() { PID=`pidof ietd` if [ $PID ]; then echo "iSCSI Target (pid $PID) is running..." else echo "iSCSI Target is stopped." exit 1 fi } case "$1" in start) ietd_start ;; stop) ietd_stop ;; restart) ietd_stop sleep 1 ietd_start ;; status) ietd_status ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit 0 iscsitarget-1.4.20.3+svn502/etc/initd/initd.gentoo000066400000000000000000000011671233224651300215250ustar00rootroot00000000000000#!/sbin/runscript # # Start the iSCSI Enterprise Target. # # To define start-up options, create a config file called # /etc/conf.d/iscsi-target, in it define OPTIONS="...". depend() { use net need localmount after bootmisc } start() { ebegin "Starting iSCSI Target" modprobe -q crc32c modprobe iscsi_trgt start-stop-daemon --start --exec /usr/sbin/ietd --pidfile /var/run/ietd.pid -- $OPTIONS eend $? } stop() { ebegin "Stopping iSCSI Target" ietadm --op delete start-stop-daemon --stop --exec /usr/sbin/ietd --pidfile /var/run/ietd.pid rm -f /var/run/iscsi_trgt.pid eend $? } restart() { stop sleep 1 start } iscsitarget-1.4.20.3+svn502/etc/initd/initd.redhat000066400000000000000000000026261233224651300215020ustar00rootroot00000000000000#!/bin/sh # # chkconfig: - 39 35 # description: Starts and stops the iSCSI target # # pidfile: /var/run/ietd.pid # config: /etc/ietd.conf PATH=/sbin:/bin:/usr/sbin:/usr/bin OPTIONS="" # Source function library. if [ -f /etc/init.d/functions ] ; then . /etc/init.d/functions elif [ -f /etc/rc.d/init.d/functions ] ; then . /etc/rc.d/init.d/functions else exit 0 fi if [ -f /etc/sysconfig/iscsi-target ]; then . /etc/sysconfig/iscsi-target fi RETVAL=0 start() { echo -n "Starting iSCSI Target: " modprobe -q crc32c modprobe iscsi_trgt daemon /usr/sbin/ietd $OPTIONS RETVAL=$? if [ $RETVAL -eq 0 ]; then echo_success else echo_failure fi echo return $RETVAL } stop() { echo -n "Stopping iSCSI Target: " ietadm --op delete killall ietd RETVAL=$? if [ $RETVAL -eq 0 ]; then echo_success else echo_failure fi echo return $RETVAL } restart() { stop sleep 1 start } condrestart() { PID=`pidofproc ietd` if [ $PID ]; then restart fi } status() { PID=`pidofproc ietd` if [ ! $PID ]; then echo "iSCSI Target stopped" exit 1 else echo "iSCSI Target (pid $PID) is running..." fi } case "$1" in start) start ;; stop) stop ;; restart) restart ;; condrestart) condrestart ;; status) status ;; *) echo $"Usage: $0 {start|stop|restart|condrestart|status}" exit 1 esac exit 0 iscsitarget-1.4.20.3+svn502/etc/initiators.allow000066400000000000000000000017631233224651300213210ustar00rootroot00000000000000# The semantics are: # # * Without any entries no initiator can see or connect to any target. # # * By default we allow all initiators to connect to all targets with # the "ALL ALL" entry. # # * Override which initiators can connect to which targets by putting an # entry for a given target followed by a comma separated list of # initiators, either IP address or IQN name (basic regex patterns for # IQN names are accepted leave out ^ and $ specials as they are implicit), # separate the two with whitespace. # # * Entry processing stops at the "ALL" target entry if present. # # # Some examples # #iqn.2001-04.com.example:storage.disk1.sys1.xyz 192.168.0.0/16, .*:mscs1-[1-4]\.example\.com #iqn.2001-04.com.example:storage.disk1.sys2.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], [3ffe:505:2:1::]/64, 192.168.22.0/24 #iqn.2001-04.com.example:storage.disk1.sys3.xyz ALL #iqn.2001-04.com.example:storage.disk1.sys4.xyz 192.168.22.3, iqn\.1998-01\.com\.vmware:.*\.example\.com # ALL 192.168.0.0/16 ALL ALL iscsitarget-1.4.20.3+svn502/etc/initiators.deny000066400000000000000000000011041233224651300211270ustar00rootroot00000000000000# The semantics are: # * By default, every initiator can see and connect to all targets. # # * Deny for some or every initiator access to one or all targets, # by adding a line to this file. # # * Then allow some named initiators access to selected targets, # by adding lines to initiators.allow # # Some examples #iqn.2001-04.com.example:storage.disk1.sys1.xyz ALL #iqn.2001-04.com.example:storage.disk1.sys2.xyz 192.168.12.2, 192.168.3.0/24, 192.167.1.16/28 #iqn.2001-04.com.example:storage.disk1.sys4.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], [3ffe:505:2:1::]/64 #ALL ALL iscsitarget-1.4.20.3+svn502/etc/targets.allow000066400000000000000000000015401233224651300205760ustar00rootroot00000000000000# The semantics are: # # * Without any entries no target will accept connections or be discovered # on any address. # # * By default we allow all targets to accept connections and to be # discovered on any address with the "ALL ALL" entry. # # * Override which targets will accept connections on and be discovered on # which addresses by putting an entry for a given target followed by a # comma separated list of IP addresses, separate the two with whitespace. # # * Entry processing stops at the "ALL" target entry if present. # # # Some examples # #iqn.2001-04.com.example:storage.disk1.sys1.xyz 192.168.0.0/16 #iqn.2001-04.com.example:storage.disk1.sys2.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], 192.168.22.24 #iqn.2001-04.com.example:storage.disk1.sys3.xyz ALL #iqn.2001-04.com.example:storage.disk1.sys4.xyz 192.168.22.3 # ALL 192.168.0.0/16 ALL ALL iscsitarget-1.4.20.3+svn502/include/000077500000000000000000000000001233224651300167355ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/include/iet_u.h000066400000000000000000000056011233224651300202150ustar00rootroot00000000000000#ifndef _IET_U_H #define _IET_U_H #define IET_VERSION_STRING "trunk" /* The maximum length of 223 bytes in the RFC. */ #define ISCSI_NAME_LEN 256 #define ISCSI_ARGS_LEN 2048 #define ISCSI_LISTEN_PORT 3260 #define SCSI_ID_LEN 16 #define SCSI_SN_LEN (SCSI_ID_LEN * 2) #ifndef aligned_u64 #define aligned_u64 unsigned long long __attribute__((aligned(8))) #endif struct module_info { char version[128]; }; struct target_info { u32 tid; char name[ISCSI_NAME_LEN]; }; struct volume_info { u32 tid; u32 lun; aligned_u64 args_ptr; u32 args_len; }; struct session_info { u32 tid; aligned_u64 sid; char initiator_name[ISCSI_NAME_LEN]; u32 exp_cmd_sn; u32 max_cmd_sn; }; #define DIGEST_ALL (DIGEST_NONE | DIGEST_CRC32C) #define DIGEST_NONE (1 << 0) #define DIGEST_CRC32C (1 << 1) struct conn_info { u32 tid; aligned_u64 sid; u32 cid; u32 stat_sn; u32 exp_stat_sn; int header_digest; int data_digest; int fd; }; enum { key_initial_r2t, key_immediate_data, key_max_connections, key_max_recv_data_length, key_max_xmit_data_length, key_max_burst_length, key_first_burst_length, key_default_wait_time, key_default_retain_time, key_max_outstanding_r2t, key_data_pdu_inorder, key_data_sequence_inorder, key_error_recovery_level, key_header_digest, key_data_digest, key_ofmarker, key_ifmarker, key_ofmarkint, key_ifmarkint, session_key_last, }; enum { key_wthreads, key_target_type, key_queued_cmnds, key_nop_interval, key_nop_timeout, target_key_last, }; enum { key_session, key_target, }; struct iscsi_param_info { u32 tid; aligned_u64 sid; u32 param_type; u32 partial; u32 session_param[session_key_last]; u32 target_param[target_key_last]; }; enum iet_event_state { E_CONN_CLOSE, }; struct iet_event { u32 tid; aligned_u64 sid; u32 cid; u32 state; }; #define DEFAULT_NR_WTHREADS 8 #define MIN_NR_WTHREADS 1 #define MAX_NR_WTHREADS 128 #define DEFAULT_NR_QUEUED_CMNDS 32 #define MIN_NR_QUEUED_CMNDS 1 #define MAX_NR_QUEUED_CMNDS 256 #define DEFAULT_NOP_INTERVAL 0 #define MIN_NOP_INTERVAL 0 #define MAX_NOP_INTERVAL 90 #define DEFAULT_NOP_TIMEOUT 0 #define MIN_NOP_TIMEOUT 0 #define MAX_NOP_TIMEOUT 90 #define NETLINK_IET 21 #define GET_MODULE_INFO _IOW('i', 20, struct module_info) #define ADD_TARGET _IOWR('i', 21, struct target_info) #define DEL_TARGET _IOW('i', 22, struct target_info) #define ADD_VOLUME _IOW('i', 24, struct volume_info) #define DEL_VOLUME _IOW('i', 25, struct volume_info) #define ADD_SESSION _IOW('i', 26, struct session_info) #define DEL_SESSION _IOW('i', 27, struct session_info) #define GET_SESSION_INFO _IOWR('i', 28, struct session_info) #define ADD_CONN _IOW('i', 29, struct conn_info) #define DEL_CONN _IOW('i', 30, struct conn_info) #define GET_CONN_INFO _IOWR('i', 31, struct conn_info) #define ISCSI_PARAM_SET _IOW('i', 32, struct iscsi_param_info) #define ISCSI_PARAM_GET _IOWR('i', 33, struct iscsi_param_info) #endif iscsitarget-1.4.20.3+svn502/iscsitarget.spec000066400000000000000000000324071233224651300205150ustar00rootroot00000000000000## ## Global Package Definitions ## ## IET Release %define iet_version trunk ## Package Revision %define revision 1 ## Build Options (modify to tune your build) # Build DKMS kernel module # # Two passes through rpmbuild are required, once for the userland # binary package and another time with --target=noarch for the # kernel dkms package. %define dkms 0 # Build weak modules (KABI tracking) if supported # # If it isn't supported it will figure that out below %define weak 1 # Build from SVN repository %define svn 0 %define svn_rev HEAD %define svn_repo https://iscsitarget.svn.sourceforge.net/svnroot/iscsitarget ## Platform Definitions (you shouldn't need to modify these) # Determine distribution %define is_suse !%(test -e /etc/SuSE-release; echo $?) %define is_fedora !%(test -e /etc/fedora-release; echo $?) %define is_redhat !%(test -e /etc/redhat-release; echo $?) %define is_mandrake !%(test -e /etc/mandrake-release; echo $?) %define is_mandriva !%(test -e /etc/mandriva-release; echo $?) # Define kernel version information %{!?kernel: %define kernel %(uname -r)} %define kver %(echo %{kernel} | sed -e 's/default//' -e 's/pae//i' -e 's/xen//' -e 's/smp//' -e 's/bigmem//' -e 's/hugemem//' -e 's/enterprise//' -e 's/-$//') %define krel %(echo %{kver} | sed -e 's/-/_/g') %define ktype %(echo kernel-%{kernel} | sed -e 's/%{kver}//' -e 's/--/-/' -e 's/-$//') %define moddir /lib/modules/%{kernel} # Set location of tools %{!?__chkconfig: %define __chkconfig /sbin/chkconfig} %{!?__depmod: %define __depmod /sbin/depmod} %{!?__dkms: %define __dkms /usr/sbin/dkms} %{!?__find: %define __find /usr/bin/find} %{!?__modprobe: %define __modprobe /sbin/modprobe} %{!?__service: %define __service /sbin/service} %{!?__svn: %define __svn /usr/bin/svn} %if %is_suse %{!?__weak_modules: %define __weak_modules /usr/lib/module-init-tools/weak-modules} %else %{!?__weak_modules: %define __weak_modules /sbin/weak-modules} %endif # Subversion build information %if %svn %if !%(test "%{iet_version}" = "trunk"; echo $?) %define svn_url %{svn_repo}/trunk %define svn_ver %(%{__svn} info -r %{svn_rev} --non-interactive %{svn_url} | awk '{if ($1 == "Revision:") {print "r"$2}}') %else %define svn_url %{svn_repo}/tags/%{iet_version} %endif %endif # Build weak module (KABI Tracking) on platforms that support it %if %weak %define weak !%(test -x %{__weak_modules}; echo $?) %endif # Basic regex filters for unwanted dependencies (used for weak modules) %if %weak %define pro_filter "" %if %is_redhat %define req_filter "\\(fsync_bdev\\|sync_page_range\\|vfs_write\\|get_io_context\\|copy_io_context\\|put_io_context\\)" %else %define req_filter "" %endif %endif # Define build user %define user %(whoami) ## ## Userland Package ## ## Information Summary: iSCSI Enterprise Target Name: iscsitarget Version: %{?svn_ver: %{iet_version}_%{svn_ver}}%{!?svn_ver: %{iet_version}} Release: %{?revision: %{revision}}%{!?revision: 1} License: GPL Group: System Environment/Daemons URL: http://sourceforge.net/projects/iscsitarget/ Packager: IET development team ## Source files %if !%svn Source0: %{name}-%{version}.tar.gz %endif ## Patches #Patch0: %{name}-%{version}-example.p ## Install Requirements Requires: %{name}-kmod = %{version} ## Build Requirements BuildRequires: kernel >= 2.6 BuildRequires: gcc, make, patch, binutils, /usr/bin/install %if %is_suse BuildRequires: kernel-source = %{kver} %else BuildRequires: %{ktype}-devel = %{kver} %endif %if %svn BuildRequires: subversion %endif ## Build Definitions BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%{user} ## Description %description iSCSI Enterprise Target ## ## Kernel Module Package ## %if %dkms %ifarch noarch %package -n dkms-%{name} ## Information Summary: iSCSI Enterprise Target kernel module Group: System Environment/Kernel Release: %{release} ## Install Requirements Requires: dkms >= 2, gcc, make, patch, binutils %if %is_suse Requires: kernel-source %else Requires: %{ktype}-devel %endif ## Install Provides Provides: kernel-modules Provides: %{name}-kmod = %{version} ## Install Conflicts Conflicts: kmod-%{name} ## Description %description -n dkms-%{name} iSCSI Enterprise Target kernel module %endif %else %package -n kmod-%{name} ## Information Summary: iSCSI Enterprise Target kernel module Group: System Environment/Kernel Release: %{release}_%{krel} ## Install Requirements %if %weak Requires: %{ktype} Requires(post): %{__weak_modules} Requires(postun): %{__weak_modules} %else Requires: %{ktype} = %{kver} %endif Requires(post): %{__depmod} Requires(postun): %{__depmod} ## Install Provides %if !%weak Provides: kernel-modules = %{kver} %endif Provides: %{name}-kmod = %{version} ## Install Conflicts Conflicts: dkms-%{name} ## Description %description -n kmod-%{name} iSCSI Enterprise Target kernel module %endif ## ## Package Creation ## ## Preparation %prep ## Setup %if %svn %setup -q -D -T -c -n %{name}-%{version} if [ ! -f include/iet_u.h ]; then %{__svn} export --force -r %{svn_rev} --non-interactive -q %{svn_url} . %{__sed} -i -e "s/\(#define IET_VERSION_STRING\).*/\1\t\"%{version}\"/" include/iet_u.h # Patches to apply to SVN #%patch0 -p0 fi %else %setup -q -n %{name}-%{version} # Patches to apply to release #%patch0 -p0 %endif ## Build %build %{__make} distclean %ifnarch noarch %if %dkms %{__make} usr %else %{__make} %endif %endif ## Installation %install %{__rm} -rf %{buildroot} %ifnarch noarch %if %dkms %{__make} install-usr install-doc install-etc DISTDIR=%{buildroot} DESTDIR=%{buildroot} %else %{__make} install-usr install-doc install-etc install-kernel DISTDIR=%{buildroot} DESTDIR=%{buildroot} %{__rm} -f %{buildroot}/lib/modules/%{kernel}/modules.* %if !%(test -d %{buildroot}%{moddir}/extra/iscsi; echo $?) %define module %{moddir}/extra/iscsi/iscsi_trgt.ko %else %define module %{moddir}/kernel/iscsi/iscsi_trgt.ko %endif %endif %if %is_redhat || %is_fedora if [ -d %{buildroot}/etc/init.d ]; then %{__mkdir} %{buildroot}/etc/rc.d %{__mv} %{buildroot}/etc/init.d %{buildroot}/etc/rc.d fi %endif %if %(test -d %{buildroot}/etc/iet; echo $?) %define ietd_conf %{_sysconfdir}/iet/ietd.conf %define initiators_allow %{_sysconfdir}/iet/initiators.allow %define targets_allow %{_sysconfdir}/iet/targets.allow %else %define ietd_conf %{_sysconfdir}/ietd.conf %define initiators_allow %{_sysconfdir}/initiators.allow %define targets_allow %{_sysconfdir}/initiators.deny %endif %elseif %dkms %{__mkdir} -p %{buildroot}/usr/src/%{name}-%{version} %{__cp} -r COPYING dkms.conf include kernel patches %{buildroot}/usr/src/%{name}-%{version} %{__sed} -i -e "s/\(PACKAGE_VERSION=\).*/\1\"%{version}\"/" %{buildroot}/usr/src/%{name}-%{version}/dkms.conf %endif # Ugly hack to filter out unwanted dependencies %if %weak %global _use_internal_dependency_generator 0 %if !%(test -n "%{pro_filter}"; echo $?) %define iet_provides %{_tmppath}/iet_provides-%{user} %{__cat} << EOF > %{iet_provides} %{__find_provides} "\$@" | %{__grep} -v %{pro_filter} exit 0 EOF %{__chmod} 755 %{iet_provides} %define __find_provides %{iet_provides} %endif %if !%(test -n "%{req_filter}"; echo $?) %define iet_requires %{_tmppath}/iet_requires-%{user} %{__cat} << EOF > %{iet_requires} %{__find_requires} "\$@" | %{__grep} -v %{req_filter} exit 0 EOF %{__chmod} 755 %{iet_requires} %define __find_requires %{iet_requires} %endif %endif ## Cleaning %clean %{__rm} -rf %{buildroot} %if %{?iet_provides:1}%{!?iet_provides:0} %{__rm} -f %{iet_provides} %endif %if %{?iet_requires:1}%{!?iet_requires:0} %{__rm} -f %{iet_requires} %endif ## Post-Install Script %ifnarch noarch %post %if %is_suse %{__ln} -s %{_initrddir}/iscsi-target %{_sbindir}/rciscsi-target %endif %{__chkconfig} --add iscsi-target %endif ## Pre-Uninstall Script %ifnarch noarch %preun if [ "$1" = 0 ]; then %{__service} iscsi-target stop &>/dev/null %{__chkconfig} --del iscsi-target &>/dev/null fi %if %is_suse %{__rm} -f %{_sbindir}/rciscsi-target %endif %endif ## Post-Uninstall Script %ifnarch noarch %postun if [ "$1" != 0 ]; then %{__service} iscsi-target condrestart &>/dev/null fi %endif ## Post-Install Script (Kernel Module) %if %dkms %ifarch noarch %post -n dkms-%{name} %{__dkms} add -m %{name} -v %{version} --rpm_safe_upgrade %{__dkms} build -m %{name} -v %{version} %{__dkms} install -m %{name} -v %{version} --force %endif %else %post -n kmod-%{name} %{__depmod} %{kernel} -a %if %weak if [ -x %{__weak_modules} ]; then echo %{module} | %{__weak_modules} --add-modules fi %endif %endif ## Pre-Uninstall Script (Kernel Module) %if %dkms %ifarch noarch %preun -n dkms-%{name} %{__dkms} remove -m %{name} -v %{version} --all --rpm_safe_upgrade %endif %else %preun -n kmod-%{name} %{__modprobe} -r -q --set-version %{kernel} iscsi_trgt %if %weak if [ -x %{__weak_modules} ]; then echo %{module} | %{__weak_modules} --remove-modules fi %endif %endif ## Post-Uninstall Script (Kernel Module) %if !%dkms %postun -n kmod-%{name} %{__depmod} %{kernel} -a %endif ## File Catalog %ifnarch noarch %files %defattr(-, root, root) %{_sbindir} %{_mandir}/man? %{_initrddir} %config(noreplace) %{ietd_conf} %config(noreplace) %{initiators_allow} %config(noreplace) %{targets_allow} %doc %{_docdir} %endif ## File Catalog (Kernel Module) %if %dkms %ifarch noarch %files -n dkms-%{name} %defattr(-, root, root) /usr/src/%{name}-%{version} %endif %else %files -n kmod-%{name} %defattr(-, root, root) /lib/modules/%{kernel} %endif %changelog * Mon Jan 10 2011 Ross Walker - 1.4.20.3 - removed stray RPM build dependency on openssl * Tue May 11 2010 Ross Walker - 1.4.20.2 - fixed recursive KERNELSRC define problem on SLES - updated SVN building to handle tagged builds - updated iet_version for next release - fixed install and file catalogs to work with previous versions * Tue Mar 30 2010 Ross Walker - 1.4.20 - Changed DKMS module name to dkms-iscsitarget from kmod-iscsitarget - Added Provides: kernel-modules to DKMS module so multiple versions can be managed by DKMS at once. - Added conflicts to dkms and kmod for each other so only one type can be installed at a time. - Added --rpm_safe_upgrade to DKMS add/remove for RPM-DKMS sync - Added --force to DKMS install in case version changed, but code didn't * Mon Mar 29 2010 Ross Walker - 1.4.20 - Updated SVN build defines. - Added weak build define to manually choose KABI tracking or per-kernel - Fixed issue with upgrading KABI tracking module by removing Provides: kernel-modules to weak builds, kept for per-kernel builds if you install weak built module, all per-kernels are uninstalled, you cannot install a per-kernel while a weak built is installed. * Mon Mar 08 2010 Ross Walker - 1.4.19 - removed openssl-devel build requirement * Wed Oct 14 2009 Ross Walker - 1.4.18 - Added more macros for better cross-distro support - Modified Matt's update for better Redhat-SuSE compatibility * Fri Oct 09 2009 Matthew Wild - 1.4.18 - Added openSuSE specific configuration - Tidied up files section for init.d|rc.d - run depmod with -a rather than -A option * Wed Sep 25 2009 Ross Walker - 0.4.17-244 - SuSE puts weak-modules under /usr/lib/module-init-tools - Kernel module now located in /lib/modules//extra/iscsi * Wed Sep 25 2009 Ross Walker - 0.4.17-242 - Added ability to build weak modules for platforms that support them - Cleaned up logic a little - Made safe for multi-user builds - Redacted old spec file maintainer's email address * Wed Sep 22 2009 Ross Walker - 0.4.17-236 - Updated file catalog for new config directory * Wed Sep 09 2009 Ross Walker - 0.4.17-226 - Added ability to build directly from subversion repo - Added ability to build dkms kernel module * Mon Nov 10 2008 Ross Walker - 0.4.17-177 - Changed kernel-module naming to kmod - Updated versioning * Fri Feb 16 2007 Ross Walker - 0.4.14-96 - Reworked spec file for latest release - Commented and cleaned up sections - Added additional documents to %files * Mon Nov 21 2005 Bastiaan Bakker - 0.4.13-0.1266.1 - upstream snapshot 1266 - added condrestart patch - stop and start service on update or removal * Sun Nov 13 2005 Bastiaan Bakker - 0.4.13-0.1264.2 - run %post and %preun for kernel package, not main package * Sun Nov 13 2005 Bastiaan Bakker - 0.4.13-0.1264.1 - updated to snapshot 1264 * Thu Nov 03 2005 Bastiaan Bakker - 0.4.12-6 - added openssl-devel build requirement - removed '.ko' extension in modprobe command * Wed Nov 02 2005 Bastiaan Bakker - 0.4.12-5 - fixed kernel-devel BuildRequires * Fri Sep 23 2005 Bastiaan Bakker - 0.4.12-4 - fixed modprobe -r 'FATAL' message - run depmod with correct kernel version * Fri Sep 23 2005 Bastiaan Bakker - 0.4.12-3 - added config files - set kernel module file permissions to 744 - fixed provides/requires of kernel module - removed BuildArch restriction * Thu Sep 22 2005 Bastiaan Bakker - 0.4.12-2 - create separate subpackage for kernel module - include man pages - added kernel compatibility patch for kernels < 2.6.11 * Wed Aug 03 2005 Bastiaan Bakker - First version. iscsitarget-1.4.20.3+svn502/kernel/000077500000000000000000000000001233224651300165725ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/kernel/Makefile000066400000000000000000000010761233224651300202360ustar00rootroot00000000000000# # Makefile for the Linux kernel device drivers. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (not a .c file). # # Note 2! The CFLAGS definitions are now in the main makefile. EXTRA_CFLAGS += -I$(src)/../include obj-m += iscsi_trgt.o iscsi_trgt-objs := tio.o iscsi.o nthread.o wthread.o config.o digest.o \ conn.o session.o target.o volume.o iotype.o \ file-io.o null-io.o target_disk.o event.o param.o \ block-io.o ua.o persist.o iscsitarget-1.4.20.3+svn502/kernel/block-io.c000066400000000000000000000162151233224651300204420ustar00rootroot00000000000000/* * Target device block I/O. * * Based on file I/O driver from FUJITA Tomonori * (C) 2004 - 2005 FUJITA Tomonori * (C) 2006 Andre Brinkmann * (C) 2007 Ross Walker * (C) 2007 Ming Zhang * This code is licenced under the GPL. */ #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" struct blockio_data { char *path; struct block_device *bdev; }; struct tio_work { atomic_t error; atomic_t bios_remaining; struct completion tio_complete; }; static void blockio_bio_endio(struct bio *bio, int error) { struct tio_work *tio_work = bio->bi_private; error = test_bit(BIO_UPTODATE, &bio->bi_flags) ? error : -EIO; if (error) atomic_set(&tio_work->error, error); /* If last bio signal completion */ if (atomic_dec_and_test(&tio_work->bios_remaining)) complete(&tio_work->tio_complete); bio_put(bio); } /* * Blockio_make_request(): The function translates an iscsi-request into * a number of requests to the corresponding block device. */ static int blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw) { struct blockio_data *bio_data = volume->private; struct request_queue *bdev_q = bdev_get_queue(bio_data->bdev); struct tio_work *tio_work; struct bio *tio_bio = NULL, *bio = NULL, *biotail = NULL; struct blk_plug plug; u32 size = tio->size; u32 tio_index = 0; int max_pages = 1; int err = 0; loff_t ppos = tio->offset; /* Calculate max_pages for bio_alloc (memory saver) */ if (bdev_q) max_pages = bio_get_nr_vecs(bio_data->bdev); tio_work = kzalloc(sizeof (*tio_work), GFP_KERNEL); if (!tio_work) return -ENOMEM; atomic_set(&tio_work->error, 0); atomic_set(&tio_work->bios_remaining, 0); init_completion(&tio_work->tio_complete); /* Main processing loop, allocate and fill all bios */ while (size && tio_index < tio->pg_cnt) { bio = bio_alloc(GFP_KERNEL, min(max_pages, BIO_MAX_PAGES)); if (!bio) { err = -ENOMEM; goto out; } /* bi_sector is ALWAYS in units of 512 bytes */ bio->bi_iter.bi_sector = ppos >> 9; bio->bi_bdev = bio_data->bdev; bio->bi_end_io = blockio_bio_endio; bio->bi_private = tio_work; if (tio_bio) biotail = biotail->bi_next = bio; else tio_bio = biotail = bio; atomic_inc(&tio_work->bios_remaining); /* Loop for filling bio */ while (size && tio_index < tio->pg_cnt) { unsigned int bytes = PAGE_SIZE; if (bytes > size) bytes = size; if (!bio_add_page(bio, tio->pvec[tio_index], bytes, 0)) break; size -= bytes; ppos += bytes; tio_index++; } } blk_start_plug(&plug); /* Walk the list, submitting bios 1 by 1 */ while (tio_bio) { bio = tio_bio; tio_bio = tio_bio->bi_next; bio->bi_next = NULL; submit_bio(rw, bio); } blk_finish_plug(&plug); wait_for_completion(&tio_work->tio_complete); err = atomic_read(&tio_work->error); kfree(tio_work); return err; out: while (tio_bio) { bio = tio_bio; tio_bio = tio_bio->bi_next; bio_put(bio); } kfree(tio_work); return err; } static int blockio_open_path(struct iet_volume *volume, const char *path) { struct blockio_data *bio_data = volume->private; struct block_device *bdev; int flags = FMODE_EXCL | FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); int err = 0; bio_data->path = kstrdup(path, GFP_KERNEL); if (!bio_data->path) return -ENOMEM; bdev = blkdev_get_by_path(path, flags, THIS_MODULE); if (IS_ERR(bdev)) { err = PTR_ERR(bdev); eprintk("Can't open device %s, error %d\n", path, err); bio_data->bdev = NULL; } else { bio_data->bdev = bdev; fsync_bdev(bio_data->bdev); } return err; } /* Create an enumeration of our accepted actions */ enum { opt_path, opt_ignore, opt_err, }; /* Create a match table using our action enums and their matching options */ static match_table_t tokens = { {opt_path, "path=%s"}, {opt_ignore, "scsiid=%s"}, {opt_ignore, "scsisn=%s"}, {opt_ignore, "type=%s"}, {opt_ignore, "iomode=%s"}, {opt_ignore, "blocksize=%s"}, {opt_err, NULL}, }; static int parse_blockio_params(struct iet_volume *volume, char *params) { struct blockio_data *info = volume->private; int err = 0; char *p, *q; /* Loop through parameters separated by commas, look up our * parameter in match table, return enumeration and arguments * select case based on the returned enum and run the action */ while ((p = strsep(¶ms, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case opt_path: if (info->path) { iprintk("Target %s, LUN %u: " "duplicate \"Path\" param\n", volume->target->name, volume->lun); err = -EINVAL; goto out; } if (!(q = match_strdup(&args[0]))) { err = -ENOMEM; goto out; } err = blockio_open_path(volume, q); kfree(q); if (err < 0) goto out; break; case opt_ignore: break; default: iprintk("Target %s, LUN %u: unknown param %s\n", volume->target->name, volume->lun, p); return -EINVAL; } } if (!info->path) { iprintk("Target %s, LUN %u: missing \"Path\" param\n", volume->target->name, volume->lun); err = -EINVAL; } out: return err; } static void blockio_detach(struct iet_volume *volume) { struct blockio_data *bio_data = volume->private; int flags = FMODE_EXCL | FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); if (bio_data->bdev) blkdev_put(bio_data->bdev, flags); kfree(bio_data->path); kfree(volume->private); } static int blockio_attach(struct iet_volume *volume, char *args) { struct blockio_data *bio_data; int err = 0; if (volume->private) { eprintk("Lun %u already attached on Target %s \n", volume->lun, volume->target->name); return -EBUSY; } bio_data = kzalloc(sizeof (*bio_data), GFP_KERNEL); if (!bio_data) return -ENOMEM; volume->private = bio_data; err = parse_blockio_params(volume, args); if (!err) { /* see Documentation/ABI/testing/sysfs-block */ unsigned bsz = bdev_logical_block_size(bio_data->bdev); if (!volume->blk_shift) volume->blk_shift = blksize_bits(bsz); else if (volume->blk_shift < blksize_bits(bsz)) { eprintk("Specified block size (%u) smaller than " "device %s logical block size (%u)\n", (1 << volume->blk_shift), bio_data->path, bsz); err = -EINVAL; } } if (err < 0) { eprintk("Error attaching Lun %u to Target %s \n", volume->lun, volume->target->name); goto out; } volume->blk_cnt = bio_data->bdev->bd_inode->i_size >> volume->blk_shift; /* Offer neither write nor read caching */ ClearLURCache(volume); ClearLUWCache(volume); out: if (err < 0) blockio_detach(volume); return err; } static void blockio_show(struct iet_volume *volume, struct seq_file *seq) { struct blockio_data *bio_data = volume->private; /* Used to display blockio volume info in /proc/net/iet/volumes */ seq_printf(seq, " path:%s\n", bio_data->path); } struct iotype blockio = { .name = "blockio", .attach = blockio_attach, .make_request = blockio_make_request, .detach = blockio_detach, .show = blockio_show, }; iscsitarget-1.4.20.3+svn502/kernel/compat.h000066400000000000000000000012031233224651300202220ustar00rootroot00000000000000/* * Kernel compatibility routines * * Copyright (C) 2008 Ross Walker * * Released under the terms of the GNU GPL v2.0. */ #ifndef __IET_COMPAT_H__ #define __IET_COMPAT_H__ #include #ifndef DECLARE_COMPLETION_ONSTACK #define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work) #endif #ifndef is_power_of_2 #define is_power_of_2(n) (n != 0 && ((n & (n - 1)) == 0)) #endif #ifndef log2 #define log2(n) ((sizeof(n) <= 4) ? (fls(n) - 1) : (fls64(n) - 1)) #endif #ifndef roundup_pow_of_two #define roundup_pow_of_two(n) (1UL << fls_long(n - 1)) #endif #endif /* __IET_COMPAT_H__ */ iscsitarget-1.4.20.3+svn502/kernel/config.c000066400000000000000000000147011233224651300202060ustar00rootroot00000000000000/* * (C) 2004 - 2005 FUJITA Tomonori * * This code is licenced under the GPL. */ #include #include "iscsi.h" #include "iscsi_dbg.h" static DEFINE_MUTEX(ioctl_mutex); struct proc_entries { const char *name; struct file_operations *fops; }; static struct proc_entries iet_proc_entries[] = { {"volume", &volume_seq_fops}, {"session", &session_seq_fops}, }; static struct proc_dir_entry *proc_iet_dir; void iet_procfs_exit(void) { int i; if (!proc_iet_dir) return; for (i = 0; i < ARRAY_SIZE(iet_proc_entries); i++) remove_proc_entry(iet_proc_entries[i].name, proc_iet_dir); proc_remove(proc_iet_dir); } int iet_procfs_init(void) { int i; struct proc_dir_entry *ent; if (!(proc_iet_dir = proc_mkdir("iet", init_net.proc_net))) goto err; for (i = 0; i < ARRAY_SIZE(iet_proc_entries); i++) { ent = proc_create(iet_proc_entries[i].name, 0, proc_iet_dir, iet_proc_entries[i].fops); if (!ent) goto err; } return 0; err: if (proc_iet_dir) iet_procfs_exit(); return -ENOMEM; } static int get_module_info(unsigned long ptr) { struct module_info info; int err; snprintf(info.version, sizeof(info.version), "%s", IET_VERSION_STRING); err = copy_to_user((void *) ptr, &info, sizeof(info)); if (err) return -EFAULT; return 0; } static int get_conn_info(struct iscsi_target *target, unsigned long ptr) { struct iscsi_session *session; struct iscsi_conn *conn; struct conn_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; session = session_lookup(target, info.sid); if (!session) return -ENOENT; conn = conn_lookup(session, info.cid); if (!conn) return -ENOENT; info.cid = conn->cid; info.stat_sn = conn->stat_sn; info.exp_stat_sn = conn->exp_stat_sn; err = copy_to_user((void *) ptr, &info, sizeof(info)); if (err) return -EFAULT; return 0; } static int add_conn(struct iscsi_target *target, unsigned long ptr) { struct iscsi_session *session; struct conn_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; session = session_lookup(target, info.sid); if (!session) return -ENOENT; return conn_add(session, &info); } static int del_conn(struct iscsi_target *target, unsigned long ptr) { struct iscsi_session *session; struct conn_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; session = session_lookup(target, info.sid); if (!session) return -ENOENT; return conn_del(session, &info); } static int get_session_info(struct iscsi_target *target, unsigned long ptr) { struct iscsi_session *session; struct session_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; session = session_lookup(target, info.sid); if (!session) return -ENOENT; info.exp_cmd_sn = session->exp_cmd_sn; info.max_cmd_sn = session->max_cmd_sn; err = copy_to_user((void *) ptr, &info, sizeof(info)); if (err) return -EFAULT; return 0; } static int add_session(struct iscsi_target *target, unsigned long ptr) { struct session_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; return session_add(target, &info); } static int del_session(struct iscsi_target *target, unsigned long ptr) { struct session_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; return session_del(target, info.sid); } static int add_volume(struct iscsi_target *target, unsigned long ptr) { struct volume_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; return volume_add(target, &info); } static int del_volume(struct iscsi_target *target, unsigned long ptr) { struct volume_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; return iscsi_volume_del(target, &info); } static int iscsi_param_config(struct iscsi_target *target, unsigned long ptr, int set) { struct iscsi_param_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; err = iscsi_param_set(target, &info, set); if (err < 0 || set) return err; err = copy_to_user((void *) ptr, &info, sizeof(info)); if (err) return -EFAULT; return 0; } static int add_target(unsigned long ptr) { struct target_info info; int err; err = copy_from_user(&info, (void *) ptr, sizeof(info)); if (err) return -EFAULT; err = target_add(&info); if (err < 0) return err; err = copy_to_user((void *) ptr, &info, sizeof(info)); if (err) return -EFAULT; return 0; } static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct iscsi_target *target = NULL; long err; u32 id; err = mutex_lock_interruptible(&ioctl_mutex); if (err < 0) return err; if (cmd == GET_MODULE_INFO) { err = get_module_info(arg); goto done; } if (cmd == ADD_TARGET) { err = add_target(arg); goto done; } err = get_user(id, (u32 *) arg); if (err < 0) goto done; /* locking handled in target_del */ if (cmd == DEL_TARGET) { err = target_del(id); goto done; } target = target_lookup_by_id(id); if (!target) { err = -ENOENT; goto done; } err = target_lock(target, 1); if (err < 0) goto done; switch (cmd) { case ADD_VOLUME: err = add_volume(target, arg); break; case DEL_VOLUME: err = del_volume(target, arg); break; case ADD_SESSION: err = add_session(target, arg); break; case DEL_SESSION: err = del_session(target, arg); break; case GET_SESSION_INFO: err = get_session_info(target, arg); break; case ISCSI_PARAM_SET: err = iscsi_param_config(target, arg, 1); break; case ISCSI_PARAM_GET: err = iscsi_param_config(target, arg, 0); break; case ADD_CONN: err = add_conn(target, arg); break; case DEL_CONN: err = del_conn(target, arg); break; case GET_CONN_INFO: err = get_conn_info(target, arg); break; default: eprintk("invalid ioctl cmd %x\n", cmd); err = -EINVAL; } target_unlock(target); done: mutex_unlock(&ioctl_mutex); return err; } static int release(struct inode *i __attribute__((unused)), struct file *f __attribute__((unused))) { mutex_lock(&ioctl_mutex); target_del_all(); mutex_unlock(&ioctl_mutex); return 0; } struct file_operations ctr_fops = { .owner = THIS_MODULE, .unlocked_ioctl = ioctl, .compat_ioctl = ioctl, .release = release }; iscsitarget-1.4.20.3+svn502/kernel/conn.c000066400000000000000000000143051233224651300176760ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "digest.h" static void print_conn_state(char *p, size_t size, unsigned long state) { if (test_bit(CONN_ACTIVE, &state)) snprintf(p, size, "%s", "active"); else if (test_bit(CONN_CLOSING, &state)) snprintf(p, size, "%s", "closing"); else snprintf(p, size, "%s", "unknown"); } static void print_digest_state(char *p, size_t size, unsigned long flags) { if (DIGEST_NONE & flags) snprintf(p, size, "%s", "none"); else if (DIGEST_CRC32C & flags) snprintf(p, size, "%s", "crc32c"); else snprintf(p, size, "%s", "unknown"); } void conn_info_show(struct seq_file *seq, struct iscsi_session *session) { struct iscsi_conn *conn; struct sock *sk; char buf[64]; list_for_each_entry(conn, &session->conn_list, list) { sk = conn->sock->sk; switch (sk->sk_family) { case AF_INET: snprintf(buf, sizeof(buf), "%pI4", &inet_sk(sk)->inet_daddr); break; case AF_INET6: snprintf(buf, sizeof(buf), "[%pI6]", &(sk)->sk_v6_daddr); break; default: break; } seq_printf(seq, "\t\tcid:%u ip:%s ", conn->cid, buf); print_conn_state(buf, sizeof(buf), conn->state); seq_printf(seq, "state:%s ", buf); print_digest_state(buf, sizeof(buf), conn->hdigest_type); seq_printf(seq, "hd:%s ", buf); print_digest_state(buf, sizeof(buf), conn->ddigest_type); seq_printf(seq, "dd:%s\n", buf); } } struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid) { struct iscsi_conn *conn; list_for_each_entry(conn, &session->conn_list, list) { if (conn->cid == cid) return conn; } return NULL; } static void iet_state_change(struct sock *sk) { struct iscsi_conn *conn = sk->sk_user_data; struct iscsi_target *target = conn->session->target; if (sk->sk_state != TCP_ESTABLISHED) conn_close(conn); else nthread_wakeup(target); target->nthread_info.old_state_change(sk); } static void iet_data_ready(struct sock *sk, int len) { struct iscsi_conn *conn = sk->sk_user_data; struct iscsi_target *target = conn->session->target; nthread_wakeup(target); target->nthread_info.old_data_ready(sk, len); } /* * @locking: grabs the target's nthread_lock to protect it from races with * set_conn_wspace_wait() */ static void iet_write_space(struct sock *sk) { struct iscsi_conn *conn = sk->sk_user_data; struct network_thread_info *info = &conn->session->target->nthread_info; spin_lock_bh(&info->nthread_lock); if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && test_bit(CONN_WSPACE_WAIT, &conn->state)) { clear_bit(CONN_WSPACE_WAIT, &conn->state); __nthread_wakeup(info); } spin_unlock_bh(&info->nthread_lock); info->old_write_space(sk); } static void iet_socket_bind(struct iscsi_conn *conn) { int opt = 1; mm_segment_t oldfs; struct iscsi_session *session = conn->session; struct iscsi_target *target = session->target; dprintk(D_GENERIC, "%llu\n", (unsigned long long) session->sid); conn->sock = SOCKET_I(conn->file->f_dentry->d_inode); conn->sock->sk->sk_user_data = conn; write_lock_bh(&conn->sock->sk->sk_callback_lock); target->nthread_info.old_state_change = conn->sock->sk->sk_state_change; conn->sock->sk->sk_state_change = iet_state_change; target->nthread_info.old_data_ready = conn->sock->sk->sk_data_ready; conn->sock->sk->sk_data_ready = iet_data_ready; target->nthread_info.old_write_space = conn->sock->sk->sk_write_space; conn->sock->sk->sk_write_space = iet_write_space; write_unlock_bh(&conn->sock->sk->sk_callback_lock); oldfs = get_fs(); set_fs(get_ds()); conn->sock->ops->setsockopt(conn->sock, SOL_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)); set_fs(oldfs); } int conn_free(struct iscsi_conn *conn) { dprintk(D_GENERIC, "%p %#Lx %u\n", conn->session, (unsigned long long) conn->session->sid, conn->cid); assert(atomic_read(&conn->nr_cmnds) == 0); assert(list_empty(&conn->pdu_list)); assert(list_empty(&conn->write_list)); list_del(&conn->list); list_del(&conn->poll_list); del_timer_sync(&conn->nop_timer); digest_cleanup(conn); kfree(conn); return 0; } static int iet_conn_alloc(struct iscsi_session *session, struct conn_info *info) { struct iscsi_conn *conn; dprintk(D_SETUP, "%#Lx:%u\n", (unsigned long long) session->sid, info->cid); conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) return -ENOMEM; conn->session = session; conn->cid = info->cid; conn->stat_sn = info->stat_sn; conn->exp_stat_sn = info->exp_stat_sn; conn->hdigest_type = info->header_digest; conn->ddigest_type = info->data_digest; if (digest_init(conn) < 0) { kfree(conn); return -ENOMEM; } spin_lock_init(&conn->list_lock); atomic_set(&conn->nr_cmnds, 0); atomic_set(&conn->nr_busy_cmnds, 0); INIT_LIST_HEAD(&conn->pdu_list); INIT_LIST_HEAD(&conn->write_list); INIT_LIST_HEAD(&conn->poll_list); init_timer(&conn->nop_timer); list_add(&conn->list, &session->conn_list); set_bit(CONN_ACTIVE, &conn->state); conn->file = fget(info->fd); iet_socket_bind(conn); list_add(&conn->poll_list, &session->target->nthread_info.active_conns); nthread_wakeup(conn->session->target); return 0; } void conn_close(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd; struct iscsi_session *session = conn->session; if (test_and_clear_bit(CONN_ACTIVE, &conn->state)) set_bit(CONN_CLOSING, &conn->state); spin_lock_bh(&conn->list_lock); list_for_each_entry(cmnd, &conn->pdu_list, conn_list) { set_cmnd_tmfabort(cmnd); if (cmnd->lun) { ua_establish_for_session(session, cmnd->lun->lun, 0x47, 0x7f); iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, 0x6e, 0x0); } } spin_unlock_bh(&conn->list_lock); nthread_wakeup(conn->session->target); } int conn_add(struct iscsi_session *session, struct conn_info *info) { struct iscsi_conn *conn; int err; conn = conn_lookup(session, info->cid); if (conn) conn_close(conn); err = iet_conn_alloc(session, info); return err; } int conn_del(struct iscsi_session *session, struct conn_info *info) { struct iscsi_conn *conn; int err = -EEXIST; conn = conn_lookup(session, info->cid); if (!conn) return err; conn_close(conn); return 0; } iscsitarget-1.4.20.3+svn502/kernel/digest.c000066400000000000000000000147121233224651300202220ustar00rootroot00000000000000/* * iSCSI digest handling. * (C) 2004 - 2006 Xiranet Communications GmbH * This code is licensed under the GPL. */ #include #include "iscsi.h" #include "digest.h" #include "iscsi_dbg.h" void digest_alg_available(unsigned int *val) { if (*val & DIGEST_CRC32C && !crypto_has_alg("crc32c", 0, CRYPTO_ALG_ASYNC)) { printk("CRC32C digest algorithm not available in kernel\n"); *val |= ~DIGEST_CRC32C; } } /** * initialize support for digest calculation. * * digest_init - * @conn: ptr to connection to make use of digests * * @return: 0 on success, < 0 on error */ int digest_init(struct iscsi_conn *conn) { int err = 0; if (!(conn->hdigest_type & DIGEST_ALL)) conn->hdigest_type = DIGEST_NONE; if (!(conn->ddigest_type & DIGEST_ALL)) conn->ddigest_type = DIGEST_NONE; if (conn->hdigest_type & DIGEST_CRC32C || conn->ddigest_type & DIGEST_CRC32C) { conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0, CRYPTO_ALG_ASYNC); conn->rx_hash.flags = 0; if (IS_ERR(conn->rx_hash.tfm)) { conn->rx_hash.tfm = NULL; err = -ENOMEM; goto out; } conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, CRYPTO_ALG_ASYNC); conn->tx_hash.flags = 0; if (IS_ERR(conn->tx_hash.tfm)) { conn->tx_hash.tfm = NULL; err = -ENOMEM; goto out; } } out: if (err) digest_cleanup(conn); return err; } /** * free resources used for digest calculation. * * digest_cleanup - * @conn: ptr to connection that made use of digests */ void digest_cleanup(struct iscsi_conn *conn) { if (conn->tx_hash.tfm) crypto_free_hash(conn->tx_hash.tfm); if (conn->rx_hash.tfm) crypto_free_hash(conn->rx_hash.tfm); } /** * debug handling of header digest errors: * simulates a digest error after n PDUs / every n-th PDU of type * HDIGEST_ERR_CORRUPT_PDU_TYPE. */ static inline void __dbg_simulate_header_digest_error(struct iscsi_cmnd *cmnd) { #define HDIGEST_ERR_AFTER_N_CMNDS 1000 #define HDIGEST_ERR_ONLY_ONCE 1 #define HDIGEST_ERR_CORRUPT_PDU_TYPE ISCSI_OP_SCSI_CMD #define HDIGEST_ERR_CORRUPT_PDU_WITH_DATA_ONLY 0 static int num_cmnds = 0; static int num_errs = 0; if (cmnd_opcode(cmnd) == HDIGEST_ERR_CORRUPT_PDU_TYPE) { if (HDIGEST_ERR_CORRUPT_PDU_WITH_DATA_ONLY) { if (cmnd->pdu.datasize) num_cmnds++; } else num_cmnds++; } if ((num_cmnds == HDIGEST_ERR_AFTER_N_CMNDS) && (!(HDIGEST_ERR_ONLY_ONCE && num_errs))) { printk("*** Faking header digest error ***\n"); printk("\tcmnd: 0x%x, itt 0x%x, sn 0x%x\n", cmnd_opcode(cmnd), be32_to_cpu(cmnd->pdu.bhs.itt), be32_to_cpu(cmnd->pdu.bhs.sn)); cmnd->hdigest = ~cmnd->hdigest; /* make things even worse by manipulating header fields */ cmnd->pdu.datasize += 8; num_errs++; num_cmnds = 0; } return; } /** * debug handling of data digest errors: * simulates a digest error after n PDUs / every n-th PDU of type * DDIGEST_ERR_CORRUPT_PDU_TYPE. */ static inline void __dbg_simulate_data_digest_error(struct iscsi_cmnd *cmnd) { #define DDIGEST_ERR_AFTER_N_CMNDS 50 #define DDIGEST_ERR_ONLY_ONCE 1 #define DDIGEST_ERR_CORRUPT_PDU_TYPE ISCSI_OP_SCSI_DATA_OUT #define DDIGEST_ERR_CORRUPT_UNSOL_DATA_ONLY 0 static int num_cmnds = 0; static int num_errs = 0; if ((cmnd->pdu.datasize) && (cmnd_opcode(cmnd) == DDIGEST_ERR_CORRUPT_PDU_TYPE)) { switch (cmnd_opcode(cmnd)) { case ISCSI_OP_SCSI_DATA_OUT: if ((DDIGEST_ERR_CORRUPT_UNSOL_DATA_ONLY) && (cmnd->pdu.bhs.ttt != ISCSI_RESERVED_TAG)) break; default: num_cmnds++; } } if ((num_cmnds == DDIGEST_ERR_AFTER_N_CMNDS) && (!(DDIGEST_ERR_ONLY_ONCE && num_errs)) && (cmnd->pdu.datasize) && (!cmnd->conn->read_overflow)) { printk("*** Faking data digest error: ***"); printk("\tcmnd 0x%x, itt 0x%x, sn 0x%x\n", cmnd_opcode(cmnd), be32_to_cpu(cmnd->pdu.bhs.itt), be32_to_cpu(cmnd->pdu.bhs.sn)); cmnd->ddigest = ~cmnd->ddigest; num_errs++; num_cmnds = 0; } } static void digest_header(struct hash_desc *hash, struct iscsi_pdu *pdu, u8 *crc) { struct scatterlist sg[2]; unsigned int nbytes = sizeof(struct iscsi_hdr); sg_init_table(sg, pdu->ahssize ? 2 : 1); sg_set_buf(&sg[0], &pdu->bhs, nbytes); if (pdu->ahssize) { sg_set_buf(&sg[1], pdu->ahs, pdu->ahssize); nbytes += pdu->ahssize; } crypto_hash_init(hash); crypto_hash_update(hash, sg, nbytes); crypto_hash_final(hash, crc); } int digest_rx_header(struct iscsi_cmnd *cmnd) { u32 crc; digest_header(&cmnd->conn->rx_hash, &cmnd->pdu, (u8 *) &crc); if (crc != cmnd->hdigest) return -EIO; return 0; } void digest_tx_header(struct iscsi_cmnd *cmnd) { digest_header(&cmnd->conn->tx_hash, &cmnd->pdu, (u8 *) &cmnd->hdigest); } static void digest_data(struct hash_desc *hash, struct iscsi_cmnd *cmnd, struct tio *tio, u32 offset, u8 *crc) { struct scatterlist *sg = cmnd->conn->hash_sg; u32 size, length, npages; int i, idx; unsigned int nbytes; size = cmnd->pdu.datasize; nbytes = size = (size + 3) & ~3; npages = size >> PAGE_CACHE_SHIFT; idx = offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; BUG_ON(idx + npages > tio->pg_cnt); BUG_ON(npages > ISCSI_CONN_IOV_MAX); sg_init_table(sg, ARRAY_SIZE(cmnd->conn->hash_sg)); crypto_hash_init(hash); for (i = 0; size > 0; i++) { length = min_t(u32, PAGE_CACHE_SIZE - offset, size); sg_set_page(&sg[i], tio->pvec[idx + i], length, offset); size -= length; offset = 0; } sg_mark_end(&sg[i - 1]); crypto_hash_update(hash, sg, nbytes); crypto_hash_final(hash, crc); } int digest_rx_data(struct iscsi_cmnd *cmnd) { struct tio *tio; struct iscsi_cmnd *scsi_cmnd; struct iscsi_data_out_hdr *req; u32 offset, crc; switch (cmnd_opcode(cmnd)) { case ISCSI_OP_SCSI_REJECT: case ISCSI_OP_PDU_REJECT: case ISCSI_OP_DATA_REJECT: return 0; case ISCSI_OP_SCSI_DATA_OUT: scsi_cmnd = cmnd->req; req = (struct iscsi_data_out_hdr *) &cmnd->pdu.bhs; tio = scsi_cmnd->tio; offset = be32_to_cpu(req->buffer_offset); break; default: tio = cmnd->tio; offset = 0; } digest_data(&cmnd->conn->rx_hash, cmnd, tio, offset, (u8 *) &crc); if (!cmnd->conn->read_overflow && (cmnd_opcode(cmnd) != ISCSI_OP_PDU_REJECT)) { if (crc != cmnd->ddigest) return -EIO; } return 0; } void digest_tx_data(struct iscsi_cmnd *cmnd) { struct tio *tio = cmnd->tio; struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; assert(tio); digest_data(&cmnd->conn->tx_hash, cmnd, tio, be32_to_cpu(req->buffer_offset), (u8 *) &cmnd->ddigest); } iscsitarget-1.4.20.3+svn502/kernel/digest.h000066400000000000000000000011221233224651300202160ustar00rootroot00000000000000/* * iSCSI digest handling. * (C) 2004 Xiranet Communications GmbH * This code is licensed under the GPL. */ #ifndef __IET_DIGEST_H__ #define __IET_DIGEST_H__ extern void digest_alg_available(unsigned int *val); extern int digest_init(struct iscsi_conn *conn); extern void digest_cleanup(struct iscsi_conn *conn); extern int digest_rx_header(struct iscsi_cmnd *cmnd); extern int digest_rx_data(struct iscsi_cmnd *cmnd); extern void digest_tx_header(struct iscsi_cmnd *cmnd); extern void digest_tx_data(struct iscsi_cmnd *cmnd); #endif /* __IET_DIGEST_H__ */ iscsitarget-1.4.20.3+svn502/kernel/event.c000066400000000000000000000033351233224651300200630ustar00rootroot00000000000000/* * Event notification code. * (C) 2005 FUJITA Tomonori * This code is licenced under the GPL. * * Some functions are based on audit code. */ #include #include #include "iet_u.h" #include "iscsi_dbg.h" static struct sock *nl; static u32 ietd_pid; static void event_recv_skb(struct sk_buff *skb) { struct nlmsghdr *nlh; u32 rlen; while (skb->len >= NLMSG_SPACE(0)) { nlh = (struct nlmsghdr *)skb->data; if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) break; rlen = NLMSG_ALIGN(nlh->nlmsg_len); if (rlen > skb->len) rlen = skb->len; ietd_pid = NETLINK_CB(skb).portid; WARN_ON(ietd_pid == 0); if (nlh->nlmsg_flags & NLM_F_ACK) netlink_ack(skb, nlh, 0); skb_pull(skb, rlen); } } static int notify(void *data, int len, int gfp_mask) { struct sk_buff *skb; struct nlmsghdr *nlh; static u32 seq = 0; int payload = NLMSG_SPACE(len); if (!(skb = alloc_skb(payload, gfp_mask))) return -ENOMEM; WARN_ON(ietd_pid == 0); nlh = __nlmsg_put(skb, ietd_pid, seq++, NLMSG_DONE, payload - sizeof(*nlh), 0); memcpy(NLMSG_DATA(nlh), data, len); return netlink_unicast(nl, skb, ietd_pid, 0); } int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic) { int err; struct iet_event event; event.tid = tid; event.sid = sid; event.cid = cid; event.state = state; err = notify(&event, sizeof(struct iet_event), 0); return err; } int event_init(void) { struct netlink_kernel_cfg cfg = { .groups = 1, .input = event_recv_skb, .cb_mutex = NULL, .bind = NULL, }; nl = netlink_kernel_create(&init_net, NETLINK_IET, &cfg); if (!nl) return -ENOMEM; else return 0; } void event_exit(void) { netlink_kernel_release(nl); } iscsitarget-1.4.20.3+svn502/kernel/file-io.c000066400000000000000000000114101233224651300202570ustar00rootroot00000000000000/* * Target device file I/O. * (C) 2004 - 2005 FUJITA Tomonori * This code is licenced under the GPL. */ #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" struct fileio_data { char *path; struct file *filp; }; static int fileio_make_request(struct iet_volume *lu, struct tio *tio, int rw) { struct fileio_data *p = lu->private; struct file *filp; mm_segment_t oldfs; struct page *page; loff_t ppos; char *buf; int i, err = 0; u32 count, size, ret; assert(p); filp = p->filp; size = tio->size; ppos = tio->offset; for (i = 0; i < tio->pg_cnt && size; i++) { page = tio->pvec[i]; assert(page); buf = page_address(page); count = min_t(u32, PAGE_CACHE_SIZE, size); oldfs = get_fs(); set_fs(get_ds()); if (rw == READ) ret = vfs_read(filp, buf, count, &ppos); else ret = vfs_write(filp, buf, count, &ppos); set_fs(oldfs); if (ret != count) { eprintk("I/O error %u, %ld\n", count, (long) ret); err = -EIO; break; } size -= count; } if (!err) { assert(!size); } return err; } static int fileio_sync(struct iet_volume *lu, struct tio *tio) { struct fileio_data *p = lu->private; struct inode *inode = p->filp->f_dentry->d_inode; struct address_space *mapping = inode->i_mapping; loff_t ppos, count; int res; if (tio) { ppos = tio->offset; count = tio->size; } else { ppos = 0; count = lu->blk_cnt << lu->blk_shift; } res = filemap_write_and_wait_range(mapping, ppos, ppos + count - 1); if (res) { eprintk("I/O error: syncing pages failed: %d\n", res); return -EIO; } else return 0; } static int open_path(struct iet_volume *volume, const char *path) { int err = 0; struct fileio_data *info = volume->private; struct file *filp; mm_segment_t oldfs; int flags; info->path = kstrdup(path, GFP_KERNEL); if (!info->path) return -ENOMEM; oldfs = get_fs(); set_fs(get_ds()); flags = (LUReadonly(volume) ? O_RDONLY : O_RDWR) | O_LARGEFILE; filp = filp_open(path, flags, 0); set_fs(oldfs); if (IS_ERR(filp)) { err = PTR_ERR(filp); eprintk("Can't open %s %d\n", path, err); info->filp = NULL; } else info->filp = filp; return err; } enum { opt_path, opt_ignore, opt_err, }; static match_table_t tokens = { {opt_path, "path=%s"}, {opt_ignore, "scsiid=%s"}, {opt_ignore, "scsisn=%s"}, {opt_ignore, "type=%s"}, {opt_ignore, "iomode=%s"}, {opt_ignore, "blocksize=%s"}, {opt_err, NULL}, }; static int parse_fileio_params(struct iet_volume *volume, char *params) { struct fileio_data *info = volume->private; int err = 0; char *p, *q; while ((p = strsep(¶ms, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case opt_path: if (info->path) { iprintk("Target %s, LUN %u: " "duplicate \"Path\" param\n", volume->target->name, volume->lun); err = -EINVAL; goto out; } if (!(q = match_strdup(&args[0]))) { err = -ENOMEM; goto out; } err = open_path(volume, q); kfree(q); if (err < 0) goto out; break; case opt_ignore: break; default: iprintk("Target %s, LUN %u: unknown param %s\n", volume->target->name, volume->lun, p); return -EINVAL; } } if (!info->path) { iprintk("Target %s, LUN %u: missing \"Path\" param\n", volume->target->name, volume->lun); err = -EINVAL; } out: return err; } static void fileio_detach(struct iet_volume *lu) { struct fileio_data *p = lu->private; kfree(p->path); if (p->filp) filp_close(p->filp, NULL); kfree(p); lu->private = NULL; } static int fileio_attach(struct iet_volume *lu, char *args) { int err = 0; struct fileio_data *p; struct inode *inode; if (lu->private) { printk("already attached ? %d\n", lu->lun); return -EBUSY; } p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM; lu->private = p; if ((err = parse_fileio_params(lu, args)) < 0) { eprintk("%d\n", err); goto out; } inode = p->filp->f_dentry->d_inode; if (S_ISREG(inode->i_mode)) ; else if (S_ISBLK(inode->i_mode)) inode = inode->i_bdev->bd_inode; else { err = -EINVAL; goto out; } if (!lu->blk_shift) lu->blk_shift = blksize_bits(IET_DEF_BLOCK_SIZE); lu->blk_cnt = inode->i_size >> lu->blk_shift; /* we're using the page cache */ SetLURCache(lu); out: if (err < 0) fileio_detach(lu); return err; } static void fileio_show(struct iet_volume *lu, struct seq_file *seq) { struct fileio_data *p = lu->private; seq_printf(seq, " path:%s\n", p->path); } struct iotype fileio = { .name = "fileio", .attach = fileio_attach, .make_request = fileio_make_request, .sync = fileio_sync, .detach = fileio_detach, .show = fileio_show, }; iscsitarget-1.4.20.3+svn502/kernel/iotype.c000066400000000000000000000033211233224651300202460ustar00rootroot00000000000000/* * Manager for various I/O types. * (C) 2004 - 2005 FUJITA Tomonori * This code is licenced under the GPL. */ #include "iscsi.h" #include "iotype.h" #include "iscsi_dbg.h" static LIST_HEAD(iotypes); static DEFINE_RWLOCK(iotypes_lock); static struct iotype *find_iotype(const char *name) { struct iotype *iot = NULL; list_for_each_entry(iot, &iotypes, iot_list) { if (strcmp(iot->name, name) == 0) return iot; } return NULL; } struct iotype *get_iotype(const char *name) { struct iotype *iot; read_lock(&iotypes_lock); iot = find_iotype(name); read_unlock(&iotypes_lock); return iot; } void put_iotype(struct iotype *iot) { if (!iot) return; return; } static int register_iotype(struct iotype *iot) { int err = 0; struct iotype *p; write_lock(&iotypes_lock); p = find_iotype(iot->name); if (p) err = -EBUSY; else list_add_tail(&iot->iot_list, &iotypes); write_unlock(&iotypes_lock); return err; } static int unregister_iotype(struct iotype *iot) { int err = 0; struct iotype *p; write_lock(&iotypes_lock); p = find_iotype(iot->name); if (p && p == iot) list_del_init(&iot->iot_list); else err = -EINVAL; write_unlock(&iotypes_lock); return err; } struct iotype *iotype_array[] = { &fileio, &blockio, &nullio, }; int iotype_init(void) { int i, err; for (i = 0; i < ARRAY_SIZE(iotype_array); i++) { if (!(err = register_iotype(iotype_array[i]))) iprintk("Registered io type %s\n", iotype_array[i]->name); else { eprintk("Failed to register io type %s\n", iotype_array[i]->name); break; } } return err; } void iotype_exit(void) { int i; for (i = 0; i < ARRAY_SIZE(iotype_array); i++) unregister_iotype(iotype_array[i]); } iscsitarget-1.4.20.3+svn502/kernel/iotype.h000066400000000000000000000020251233224651300202530ustar00rootroot00000000000000/* * (C) 2004 - 2005 FUJITA Tomonori * This code is licenced under the GPL. */ #include #include "iscsi.h" #ifndef __IOTYPE_H__ #define __IOTYPE_H__ struct iotype { const char *name; struct list_head iot_list; int (*attach)(struct iet_volume *dev, char *args); int (*make_request)(struct iet_volume *dev, struct tio *tio, int rw); int (*sync)(struct iet_volume *dev, struct tio *tio); void (*detach)(struct iet_volume *dev); void (*show)(struct iet_volume *dev, struct seq_file *seq); }; extern struct iotype fileio; extern struct iotype nullio; extern struct iotype blockio; extern int iotype_init(void); extern void iotype_exit(void); /* For option parameter parsing. * This is slightly iet specific: we only tolower() up to the first '='. * Note that this changes *c _in place_, but our parsing * routines copy the input to a scratch page before parsing anyways. */ static inline void iet_strtolower(char *c) { if (!c) return; for (; *c && *c != '='; c++) *c = tolower(*c); } #endif iscsitarget-1.4.20.3+svn502/kernel/iscsi.c000066400000000000000000001417021233224651300200550ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * Copyright (C) 2008 Arne Redlich * * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" unsigned long debug_enable_flags; unsigned long worker_thread_pool_size; static struct kmem_cache *iscsi_cmnd_cache; static u8 dummy_data[PAGE_SIZE]; static int ctr_major; static char ctr_name[] = "ietctl"; extern struct file_operations ctr_fops; static u32 cmnd_write_size(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd); if (hdr->flags & ISCSI_CMD_WRITE) return be32_to_cpu(hdr->data_length); return 0; } static u32 cmnd_read_size(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd); if (hdr->flags & ISCSI_CMD_READ) { struct iscsi_rlength_ahdr *ahdr = (struct iscsi_rlength_ahdr *)cmnd->pdu.ahs; if (!(hdr->flags & ISCSI_CMD_WRITE)) return be32_to_cpu(hdr->data_length); if (ahdr && ahdr->ahstype == ISCSI_AHSTYPE_RLENGTH) return be32_to_cpu(ahdr->read_length); } return 0; } static void iscsi_device_queue_cmnd(struct iscsi_cmnd *cmnd) { set_cmnd_waitio(cmnd); wthread_queue(cmnd); } static void iscsi_scsi_queuecmnd(struct iscsi_cmnd *cmnd) { struct iscsi_queue *queue = &cmnd->lun->queue; dprintk(D_GENERIC, "%p\n", cmnd); if ((cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) != ISCSI_CMD_UNTAGGED && (cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) != ISCSI_CMD_SIMPLE) { cmnd->pdu.bhs.flags &= ~ISCSI_CMD_ATTR_MASK; cmnd->pdu.bhs.flags |= ISCSI_CMD_UNTAGGED; } spin_lock(&queue->queue_lock); set_cmnd_queued(cmnd); switch (cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) { case ISCSI_CMD_UNTAGGED: case ISCSI_CMD_SIMPLE: if (!list_empty(&queue->wait_list) || queue->ordered_cmnd) goto pending; queue->active_cnt++; break; default: BUG(); } spin_unlock(&queue->queue_lock); iscsi_device_queue_cmnd(cmnd); return; pending: assert(list_empty(&cmnd->list)); list_add_tail(&cmnd->list, &queue->wait_list); spin_unlock(&queue->queue_lock); return; } static void iscsi_scsi_dequeuecmnd(struct iscsi_cmnd *cmnd) { struct iscsi_queue *queue; if (!cmnd->lun) return; queue = &cmnd->lun->queue; spin_lock(&queue->queue_lock); switch (cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) { case ISCSI_CMD_UNTAGGED: case ISCSI_CMD_SIMPLE: --queue->active_cnt; break; case ISCSI_CMD_ORDERED: case ISCSI_CMD_HEAD_OF_QUEUE: case ISCSI_CMD_ACA: BUG(); default: /* Should the iscsi_scsi_queuecmnd func reject this ? */ break; } while (!list_empty(&queue->wait_list)) { cmnd = list_entry(queue->wait_list.next, struct iscsi_cmnd, list); switch ((cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK)) { case ISCSI_CMD_UNTAGGED: case ISCSI_CMD_SIMPLE: list_del_init(&cmnd->list); queue->active_cnt++; iscsi_device_queue_cmnd(cmnd); break; case ISCSI_CMD_ORDERED: case ISCSI_CMD_HEAD_OF_QUEUE: case ISCSI_CMD_ACA: BUG(); } } spin_unlock(&queue->queue_lock); return; } /** * create a new command. * * iscsi_cmnd_create - * @conn: ptr to connection (for i/o) * * @return ptr to command or NULL */ struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, int req) { struct iscsi_cmnd *cmnd; /* TODO: async interface is necessary ? */ cmnd = kmem_cache_alloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL); memset(cmnd, 0, sizeof(*cmnd)); INIT_LIST_HEAD(&cmnd->list); INIT_LIST_HEAD(&cmnd->pdu_list); INIT_LIST_HEAD(&cmnd->conn_list); INIT_LIST_HEAD(&cmnd->hash_list); cmnd->conn = conn; spin_lock_bh(&conn->list_lock); atomic_inc(&conn->nr_cmnds); if (req) list_add_tail(&cmnd->conn_list, &conn->pdu_list); spin_unlock_bh(&conn->list_lock); cmnd->tio = NULL; dprintk(D_GENERIC, "%p:%p\n", conn, cmnd); return cmnd; } /** * create a new command used as response. * * iscsi_cmnd_create_rsp_cmnd - * @cmnd: ptr to request command * * @return ptr to response command or NULL */ static struct iscsi_cmnd *iscsi_cmnd_create_rsp_cmnd(struct iscsi_cmnd *cmnd, int final) { struct iscsi_cmnd *rsp = cmnd_alloc(cmnd->conn, 0); if (final) set_cmnd_final(rsp); list_add_tail(&rsp->pdu_list, &cmnd->pdu_list); rsp->req = cmnd; return rsp; } static struct iscsi_cmnd *get_rsp_cmnd(struct iscsi_cmnd *req) { return list_entry(req->pdu_list.prev, struct iscsi_cmnd, pdu_list); } static void iscsi_cmnds_init_write(struct list_head *send) { struct iscsi_cmnd *cmnd = list_entry(send->next, struct iscsi_cmnd, list); struct iscsi_conn *conn = cmnd->conn; struct list_head *pos, *next; spin_lock_bh(&conn->list_lock); list_for_each_safe(pos, next, send) { cmnd = list_entry(pos, struct iscsi_cmnd, list); dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd)); list_del_init(&cmnd->list); assert(conn == cmnd->conn); list_add_tail(&cmnd->list, &conn->write_list); } spin_unlock_bh(&conn->list_lock); nthread_wakeup(conn->session->target); } static void iscsi_cmnd_init_write(struct iscsi_cmnd *cmnd) { LIST_HEAD(head); if (!list_empty(&cmnd->list)) { eprintk("%x %x %x %x %lx %u %u %u %u %u %u %u %u %d %d\n", cmnd_itt(cmnd), cmnd_ttt(cmnd), cmnd_opcode(cmnd), cmnd_scsicode(cmnd), cmnd->flags, cmnd->r2t_sn, cmnd->r2t_length, cmnd->exp_offset, cmnd->is_unsolicited_data, cmnd->target_task_tag, cmnd->outstanding_r2t, cmnd->hdigest, cmnd->ddigest, list_empty(&cmnd->pdu_list), list_empty(&cmnd->hash_list)); assert(list_empty(&cmnd->list)); } list_add(&cmnd->list, &head); iscsi_cmnds_init_write(&head); } static void do_send_data_rsp(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; struct iscsi_cmnd *data_cmnd; struct tio *tio = cmnd->tio; struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); struct iscsi_data_in_hdr *rsp; u32 pdusize, expsize, scsisize, size, offset, sn; LIST_HEAD(send); dprintk(D_GENERIC, "%p\n", cmnd); BUG_ON(!cmnd->tio); pdusize = conn->session->param.max_xmit_data_length; expsize = cmnd_read_size(cmnd); size = min(expsize, tio->size); offset = 0; sn = 0; while (1) { data_cmnd = iscsi_cmnd_create_rsp_cmnd(cmnd, size <= pdusize); tio_get(tio); data_cmnd->tio = tio; rsp = (struct iscsi_data_in_hdr *)&data_cmnd->pdu.bhs; rsp->opcode = ISCSI_OP_SCSI_DATA_IN; rsp->itt = req->itt; rsp->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); rsp->buffer_offset = offset; rsp->data_sn = cpu_to_be32(sn); if (size <= pdusize) { data_cmnd->pdu.datasize = size; rsp->flags = ISCSI_FLG_FINAL | ISCSI_FLG_STATUS; scsisize = tio->size; if (scsisize < expsize) { rsp->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; size = expsize - scsisize; } else if (scsisize > expsize) { rsp->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW; size = scsisize - expsize; } else size = 0; rsp->residual_count = cpu_to_be32(size); list_add_tail(&data_cmnd->list, &send); break; } data_cmnd->pdu.datasize = pdusize; size -= pdusize; offset += pdusize; sn++; list_add_tail(&data_cmnd->list, &send); } iscsi_cmnds_init_write(&send); } static struct iscsi_cmnd *create_scsi_rsp(struct iscsi_cmnd *req) { struct iscsi_cmnd *rsp; struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); struct iscsi_scsi_rsp_hdr *rsp_hdr; struct iscsi_sense_data *sense; rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_SCSI_RSP; rsp_hdr->flags = ISCSI_FLG_FINAL; rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED; rsp_hdr->cmd_status = req->status; rsp_hdr->itt = req_hdr->itt; if (req->status == SAM_STAT_CHECK_CONDITION) { assert(!rsp->tio); rsp->tio = tio_alloc(1); sense = (struct iscsi_sense_data *) page_address(rsp->tio->pvec[0]); assert(sense); clear_page(sense); sense->length = cpu_to_be16(IET_SENSE_BUF_SIZE); memcpy(sense->data, req->sense_buf, IET_SENSE_BUF_SIZE); rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + IET_SENSE_BUF_SIZE; tio_set(rsp->tio, (rsp->pdu.datasize + 3) & -4, 0); } return rsp; } void iscsi_cmnd_set_sense(struct iscsi_cmnd *cmnd, u8 sense_key, u8 asc, u8 ascq) { cmnd->status = SAM_STAT_CHECK_CONDITION; cmnd->sense_buf[0] = 0xf0; cmnd->sense_buf[2] = sense_key; cmnd->sense_buf[7] = 6; // Additional sense length cmnd->sense_buf[12] = asc; cmnd->sense_buf[13] = ascq; /* Call to ACA/UAI handler */ } static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req, u8 sense_key, u8 asc, u8 ascq) { iscsi_cmnd_set_sense(req, sense_key, asc, ascq); return create_scsi_rsp(req); } void send_scsi_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *)) { struct iscsi_cmnd *rsp; struct iscsi_scsi_rsp_hdr *rsp_hdr; u32 size; func(req); rsp = create_scsi_rsp(req); switch (req->status) { case SAM_STAT_GOOD: case SAM_STAT_RESERVATION_CONFLICT: rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs; if ((size = cmnd_read_size(req)) != 0) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(size); } break; default: break; } iscsi_cmnd_init_write(rsp); } void send_data_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *)) { func(req); if (req->status == SAM_STAT_GOOD && req->tio && req->tio->size) do_send_data_rsp(req); else { struct iscsi_cmnd *rsp = create_scsi_rsp(req); struct iscsi_scsi_rsp_hdr *rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs; const u32 exp_size = cmnd_read_size(req); const u32 size = req->tio ? req->tio->size : 0; if (exp_size > size) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(exp_size - size); } else if (exp_size < size) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW; rsp_hdr->residual_count = cpu_to_be32(size - exp_size); } iscsi_cmnd_init_write(rsp); } } /** * Free a command. * Also frees the additional header. * * iscsi_cmnd_remove - * @cmnd: ptr to command */ static void iscsi_cmnd_remove(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn; if (!cmnd) return; if (cmnd_timer_active(cmnd)) { clear_cmnd_timer_active(cmnd); del_timer_sync(&cmnd->timer); } dprintk(D_GENERIC, "%p\n", cmnd); conn = cmnd->conn; kfree(cmnd->pdu.ahs); if (!list_empty(&cmnd->list)) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); eprintk("cmnd %p still on some list?, %x, %x, %x, %x, %x, %x, %x %lx\n", cmnd, req->opcode, req->scb[0], req->flags, req->itt, be32_to_cpu(req->data_length), req->cmd_sn, be32_to_cpu(cmnd->pdu.datasize), conn->state); if (cmnd->req) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd->req); eprintk("%p %x %u\n", req, req->opcode, req->scb[0]); } dump_stack(); BUG(); } list_del(&cmnd->list); spin_lock_bh(&conn->list_lock); atomic_dec(&conn->nr_cmnds); list_del(&cmnd->conn_list); spin_unlock_bh(&conn->list_lock); if (cmnd->tio) tio_put(cmnd->tio); kmem_cache_free(iscsi_cmnd_cache, cmnd); } static void cmnd_skip_pdu(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; struct tio *tio = cmnd->tio; char *addr; u32 size; int i; eprintk("%x %x %x %u\n", cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd_hdr(cmnd)->scb[0], cmnd->pdu.datasize); if (!(size = cmnd->pdu.datasize)) return; if (tio) assert(tio->pg_cnt > 0); else tio = cmnd->tio = tio_alloc(1); addr = page_address(tio->pvec[0]); assert(addr); size = (size + 3) & -4; conn->read_size = size; for (i = 0; size > PAGE_CACHE_SIZE; i++, size -= PAGE_CACHE_SIZE) { assert(i < ISCSI_CONN_IOV_MAX); conn->read_iov[i].iov_base = addr; conn->read_iov[i].iov_len = PAGE_CACHE_SIZE; } conn->read_iov[i].iov_base = addr; conn->read_iov[i].iov_len = size; conn->read_msg.msg_iov = conn->read_iov; conn->read_msg.msg_iovlen = ++i; } static void iscsi_cmnd_reject(struct iscsi_cmnd *req, int reason) { struct iscsi_cmnd *rsp; struct iscsi_reject_hdr *rsp_hdr; struct tio *tio; char *addr; rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); rsp_hdr = (struct iscsi_reject_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_REJECT; rsp_hdr->ffffffff = ISCSI_RESERVED_TAG; rsp_hdr->reason = reason; rsp->tio = tio = tio_alloc(1); addr = page_address(tio->pvec[0]); clear_page(addr); memcpy(addr, &req->pdu.bhs, sizeof(struct iscsi_hdr)); tio->size = rsp->pdu.datasize = sizeof(struct iscsi_hdr); cmnd_skip_pdu(req); req->pdu.bhs.opcode = ISCSI_OP_PDU_REJECT; } static void cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn) { struct iscsi_conn *conn = cmnd->conn; struct iscsi_session *sess = conn->session; sess->max_cmd_sn = sess->exp_cmd_sn + sess->max_queued_cmnds; if (set_stat_sn) cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn++); cmnd->pdu.bhs.exp_sn = cpu_to_be32(sess->exp_cmd_sn); cmnd->pdu.bhs.max_sn = cpu_to_be32(sess->max_cmd_sn); } static void update_stat_sn(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; u32 exp_stat_sn; cmnd->pdu.bhs.exp_sn = exp_stat_sn = be32_to_cpu(cmnd->pdu.bhs.exp_sn); dprintk(D_GENERIC, "%x,%x\n", cmnd_opcode(cmnd), exp_stat_sn); if ((int)(exp_stat_sn - conn->exp_stat_sn) > 0 && (int)(exp_stat_sn - conn->stat_sn) <= 0) { // free pdu resources cmnd->conn->exp_stat_sn = exp_stat_sn; } } static int check_cmd_sn(struct iscsi_cmnd *cmnd) { struct iscsi_session *session = cmnd->conn->session; u32 cmd_sn; cmnd->pdu.bhs.sn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.sn); dprintk(D_GENERIC, "cmd_sn(%u) exp_cmd_sn(%u) max_cmd_sn(%u)\n", cmd_sn, session->exp_cmd_sn, session->max_cmd_sn); if (between(cmd_sn, session->exp_cmd_sn, session->max_cmd_sn)) return 0; else if (cmnd_immediate(cmnd)) return 0; eprintk("sequence error: cmd_sn(%u) exp_cmd_sn(%u) max_cmd_sn(%u)\n", cmd_sn, session->exp_cmd_sn, session->max_cmd_sn); set_cmnd_tmfabort(cmnd); return -ISCSI_REASON_PROTOCOL_ERROR; } static struct iscsi_cmnd *__cmnd_find_hash(struct iscsi_session *session, u32 itt, u32 ttt) { struct list_head *head; struct iscsi_cmnd *cmnd; head = &session->cmnd_hash[cmnd_hashfn(itt)]; list_for_each_entry(cmnd, head, hash_list) { if (cmnd->pdu.bhs.itt == itt) { if ((ttt != ISCSI_RESERVED_TAG) && (ttt != cmnd->target_task_tag)) continue; return cmnd; } } return NULL; } static struct iscsi_cmnd *cmnd_find_hash(struct iscsi_session *session, u32 itt, u32 ttt) { struct iscsi_cmnd *cmnd; spin_lock(&session->cmnd_hash_lock); cmnd = __cmnd_find_hash(session, itt, ttt); spin_unlock(&session->cmnd_hash_lock); return cmnd; } static int cmnd_insert_hash_ttt(struct iscsi_cmnd *cmnd, u32 ttt) { struct iscsi_session *session = cmnd->conn->session; struct iscsi_cmnd *tmp; struct list_head *head; int err = 0; u32 itt = cmnd->pdu.bhs.itt; head = &session->cmnd_hash[cmnd_hashfn(itt)]; spin_lock(&session->cmnd_hash_lock); tmp = __cmnd_find_hash(session, itt, ttt); if (!tmp) { list_add_tail(&cmnd->hash_list, head); set_cmnd_hashed(cmnd); } else err = -ISCSI_REASON_TASK_IN_PROGRESS; spin_unlock(&session->cmnd_hash_lock); return err; } static int cmnd_insert_hash(struct iscsi_cmnd *cmnd) { int err; dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd->pdu.bhs.itt); if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG) return -ISCSI_REASON_PROTOCOL_ERROR; err = cmnd_insert_hash_ttt(cmnd, ISCSI_RESERVED_TAG); if (!err) { err = check_cmd_sn(cmnd); if (!err) update_stat_sn(cmnd); } else if (!cmnd_immediate(cmnd)) set_cmnd_tmfabort(cmnd); return err; } static void __cmnd_remove_hash(struct iscsi_cmnd *cmnd) { list_del(&cmnd->hash_list); } static void cmnd_remove_hash(struct iscsi_cmnd *cmnd) { struct iscsi_session *session = cmnd->conn->session; struct iscsi_cmnd *tmp; spin_lock(&session->cmnd_hash_lock); tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt, cmnd->target_task_tag); if (tmp && tmp == cmnd) __cmnd_remove_hash(tmp); else eprintk("%p:%x not found\n", cmnd, cmnd_itt(cmnd)); spin_unlock(&session->cmnd_hash_lock); } static void cmnd_skip_data(struct iscsi_cmnd *req) { struct iscsi_cmnd *rsp; struct iscsi_scsi_rsp_hdr *rsp_hdr; u32 size; rsp = get_rsp_cmnd(req); rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs; if (cmnd_opcode(rsp) != ISCSI_OP_SCSI_RSP) { eprintk("unexpected response command %u\n", cmnd_opcode(rsp)); return; } size = cmnd_write_size(req); if (size) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(size); } size = cmnd_read_size(req); if (size) { if (cmnd_hdr(req)->flags & ISCSI_CMD_WRITE) { rsp_hdr->flags |= ISCSI_FLG_BIRESIDUAL_UNDERFLOW; rsp_hdr->bi_residual_count = cpu_to_be32(size); } else { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(size); } } req->pdu.bhs.opcode = (req->pdu.bhs.opcode & ~ISCSI_OPCODE_MASK) | ISCSI_OP_SCSI_REJECT; cmnd_skip_pdu(req); } static int cmnd_recv_pdu(struct iscsi_conn *conn, struct tio *tio, u32 offset, u32 size) { int idx, i; char *addr; dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); if (!size) return 0; if (!(offset + size <= tio->pg_cnt * PAGE_CACHE_SIZE)) { eprintk("bad offset: o:%u s:%u total:%lu", offset, size, tio->pg_cnt * PAGE_CACHE_SIZE); return -EIO; } idx = offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; conn->read_msg.msg_iov = conn->read_iov; conn->read_size = size = (size + 3) & -4; conn->read_overflow = 0; i = 0; while (1) { assert(tio->pvec[idx]); addr = page_address(tio->pvec[idx]); assert(addr); conn->read_iov[i].iov_base = addr + offset; if (offset + size <= PAGE_CACHE_SIZE) { conn->read_iov[i].iov_len = size; conn->read_msg.msg_iovlen = ++i; break; } conn->read_iov[i].iov_len = PAGE_CACHE_SIZE - offset; size -= conn->read_iov[i].iov_len; offset = 0; if (++i >= ISCSI_CONN_IOV_MAX) { conn->read_msg.msg_iovlen = i; conn->read_overflow = size; conn->read_size -= size; break; } idx++; } return 0; } static void set_offset_and_length(const struct iet_volume *lu, const u8 *cmd, loff_t *off, u32 *len) { assert(lu); switch (cmd[0]) { case READ_6: case WRITE_6: *off = ((cmd[1] & 0x1f) << 16) + (cmd[2] << 8) + cmd[3]; *len = cmd[4]; if (!*len) *len = 256; break; case READ_10: case WRITE_10: case WRITE_VERIFY: *off = (u32)cmd[2] << 24 | (u32)cmd[3] << 16 | (u32)cmd[4] << 8 | (u32)cmd[5]; *len = (cmd[7] << 8) + cmd[8]; break; case READ_16: case WRITE_16: case WRITE_SAME_16: *off = (u64)cmd[2] << 56 | (u64)cmd[3] << 48 | (u64)cmd[4] << 40 | (u64)cmd[5] << 32 | (u64)cmd[6] << 24 | (u64)cmd[7] << 16 | (u64)cmd[8] << 8 | (u64)cmd[9]; *len = (u32)cmd[10] << 24 | (u32)cmd[11] << 16 | (u32)cmd[12] << 8 | (u32)cmd[13]; break; case PERSISTENT_RESERVE_OUT: { const struct persistent_reserve_out *pr_out = (const struct persistent_reserve_out *)cmd; *off = 0; *len = be32_to_cpu(pr_out->parameter_list_length); return; } default: BUG(); } *off <<= lu->blk_shift; *len <<= lu->blk_shift; } u32 translate_lun(u16 *data) { u8 *p = (u8 *) data; u32 lun = ~0U; switch (*p >> 6) { case 0: lun = p[1]; break; case 1: lun = (0x3f & p[0]) << 8 | p[1]; break; case 2: case 3: default: eprintk("%u %u %u %u\n", data[0], data[1], data[2], data[3]); break; } return lun; } static void send_r2t(struct iscsi_cmnd *req) { struct iscsi_sess_param *param = &req->conn->session->param; struct iscsi_cmnd *rsp; struct iscsi_r2t_hdr *rsp_hdr; u32 offset, burst; LIST_HEAD(send); if (req->outstanding_r2t >= param->max_outstanding_r2t) return; burst = param->max_burst_length; offset = cmnd_write_size(req) - req->r2t_length; while (req->r2t_length) { rsp = iscsi_cmnd_create_rsp_cmnd(req, 0); rsp->pdu.bhs.ttt = req->target_task_tag; rsp_hdr = (struct iscsi_r2t_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_R2T; rsp_hdr->flags = ISCSI_FLG_FINAL; memcpy(rsp_hdr->lun, cmnd_hdr(req)->lun, 8); rsp_hdr->itt = cmnd_hdr(req)->itt; rsp_hdr->r2t_sn = cpu_to_be32(req->r2t_sn++); rsp_hdr->buffer_offset = cpu_to_be32(offset); if (req->r2t_length > burst) { rsp_hdr->data_length = cpu_to_be32(burst); req->r2t_length -= burst; offset += burst; } else { rsp_hdr->data_length = cpu_to_be32(req->r2t_length); req->r2t_length = 0; } dprintk(D_WRITE, "%x %u %u %u %u\n", cmnd_itt(req), be32_to_cpu(rsp_hdr->data_length), be32_to_cpu(rsp_hdr->buffer_offset), be32_to_cpu(rsp_hdr->r2t_sn), req->outstanding_r2t); list_add_tail(&rsp->list, &send); if (++req->outstanding_r2t >= param->max_outstanding_r2t) break; } if (!list_empty(&send)) iscsi_cmnds_init_write(&send); } static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd) { assert(!(cmnd->r2t_length || cmnd->outstanding_r2t)); if (cmnd->lun) { iscsi_scsi_queuecmnd(cmnd); } else { iscsi_device_queue_cmnd(cmnd); } } static int nop_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) { u32 size, tmp; int i, err = 0; if (cmnd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG)) { cmnd->req = cmnd_find_hash(conn->session, cmnd->pdu.bhs.itt, cmnd->pdu.bhs.ttt); if (!cmnd->req) { /* * We didn't request this NOP-Out (by sending a * NOP-In, see 10.18.2 of the RFC) or our fake NOP-Out * timed out. */ eprintk("initiator bug %x\n", cmnd_itt(cmnd)); err = -ISCSI_REASON_PROTOCOL_ERROR; goto out; } del_timer_sync(&cmnd->req->timer); clear_cmnd_timer_active(cmnd->req); dprintk(D_GENERIC, "NOP-Out: %p, ttt %x, timer %p\n", cmnd->req, cmnd_ttt(cmnd->req), &cmnd->req->timer); } if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) { if (!cmnd_immediate(cmnd)) eprintk("%s\n", "initiator bug!"); err = check_cmd_sn(cmnd); if (err) goto out; update_stat_sn(cmnd); } else if ((err = cmnd_insert_hash(cmnd)) < 0) { eprintk("ignore this request %x\n", cmnd_itt(cmnd)); goto out; } if ((size = cmnd->pdu.datasize)) { size = (size + 3) & -4; conn->read_msg.msg_iov = conn->read_iov; if (cmnd->pdu.bhs.itt != cpu_to_be32(ISCSI_RESERVED_TAG)) { struct tio *tio; int pg_cnt = get_pgcnt(size); assert(pg_cnt < ISCSI_CONN_IOV_MAX); cmnd->tio = tio = tio_alloc(pg_cnt); tio_set(tio, size, 0); for (i = 0; i < pg_cnt; i++) { conn->read_iov[i].iov_base = page_address(tio->pvec[i]); tmp = min_t(u32, size, PAGE_CACHE_SIZE); conn->read_iov[i].iov_len = tmp; conn->read_size += tmp; size -= tmp; } } else { for (i = 0; i < ISCSI_CONN_IOV_MAX; i++) { conn->read_iov[i].iov_base = dummy_data; tmp = min_t(u32, size, sizeof(dummy_data)); conn->read_iov[i].iov_len = tmp; conn->read_size += tmp; size -= tmp; } } assert(!size); conn->read_overflow = size; conn->read_msg.msg_iovlen = i; } out: return err; } static u32 get_next_ttt(struct iscsi_session *session) { u32 ttt; if (session->next_ttt == ISCSI_RESERVED_TAG) session->next_ttt++; ttt = session->next_ttt++; return cpu_to_be32(ttt); } static void scsi_cmnd_start(struct iscsi_conn *conn, struct iscsi_cmnd *req) { struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); dprintk(D_GENERIC, "scsi command: %02x\n", req_hdr->scb[0]); req->lun = volume_get(conn->session->target, translate_lun(req_hdr->lun)); if (!req->lun) { switch (req_hdr->scb[0]) { case INQUIRY: case REPORT_LUNS: break; default: eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ILLEGAL_REQUEST, 0x25, 0x0); cmnd_skip_data(req); goto out; } } else set_cmnd_lunit(req); switch (req_hdr->scb[0]) { case SERVICE_ACTION_IN: if ((req_hdr->scb[1] & 0x1f) != 0x10) goto error; case INQUIRY: case REPORT_LUNS: case TEST_UNIT_READY: case SYNCHRONIZE_CACHE: case VERIFY: case VERIFY_16: case START_STOP: case READ_CAPACITY: case MODE_SENSE: case REQUEST_SENSE: case RESERVE: case RELEASE: case PERSISTENT_RESERVE_IN: { if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { /* unexpected unsolicited data */ eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); cmnd_skip_data(req); } break; } case READ_6: case READ_10: case READ_16: { loff_t offset; u32 length; if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { /* unexpected unsolicited data */ eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc); cmnd_skip_data(req); break; } set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); req->tio = tio_alloc(get_pgcnt(length)); tio_set(req->tio, length, offset); break; } case PERSISTENT_RESERVE_OUT: case WRITE_6: case WRITE_10: case WRITE_16: case WRITE_VERIFY: case WRITE_SAME_16: { struct iscsi_sess_param *param = &conn->session->param; loff_t offset; u32 length; req->exp_offset = req->pdu.datasize; req->r2t_length = cmnd_write_size(req) - req->pdu.datasize; req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL); req->target_task_tag = get_next_ttt(conn->session); /* the readonly check needs to go */ if (req_hdr->scb[0] != PERSISTENT_RESERVE_OUT && LUReadonly(req->lun)) { create_sense_rsp(req, DATA_PROTECT, 0x27, 0x0); cmnd_skip_data(req); break; } if (!param->immediate_data && req->pdu.datasize) eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); if (param->initial_r2t && !(req_hdr->flags & ISCSI_CMD_FINAL)) eprintk("%x %x\n", cmnd_itt(req), req_hdr->scb[0]); if (req_hdr->scb[0] == WRITE_VERIFY && req_hdr->scb[1] & 0x02) eprintk("Verification is ignored %x\n", cmnd_itt(req)); set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); if (req_hdr->scb[0] == WRITE_SAME_16) { dprintk(D_VAAI, "WRITE_SAME_16: D: %u L:%u O:%llu\n", cmnd_write_size(req), length, offset); if (unlikely(cmnd_write_size(req) > PAGE_CACHE_SIZE)) { eprintk("WRITE_SAME data is bigger than page size" ", this is unsupported: D:%u P:%lu\n", cmnd_write_size(req), PAGE_CACHE_SIZE); goto error; } if (unlikely(length % cmnd_write_size(req) != 0)) { eprintk("WRITE_SAME data size error: L:%u D:%u\n", length, cmnd_write_size(req)); goto error; } if (unlikely((req_hdr->scb[1] & 0x06) != 0)) eprintk("WRITE_SAME LB/PBDATA ignored: %x\n", req_hdr->scb[1]); req->tio = tio_alloc(1); tio_set(req->tio, length, offset); } else { if (unlikely(cmnd_write_size(req) != length)) eprintk("%x %u %u\n", cmnd_itt(req), cmnd_write_size(req), length); req->tio = tio_alloc(get_pgcnt(length)); tio_set(req->tio, length, offset); } if (req->pdu.datasize) { if (cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize) < 0) assert(0); } break; } error: default: eprintk("Unsupported %x\n", req_hdr->scb[0]); create_sense_rsp(req, ILLEGAL_REQUEST, 0x20, 0x0); cmnd_skip_data(req); break; } out: return; } static void data_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) { struct iscsi_sess_param *param = &conn->session->param; struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; struct iscsi_cmnd *scsi_cmnd = NULL; u32 offset = be32_to_cpu(req->buffer_offset); update_stat_sn(cmnd); cmnd->req = scsi_cmnd = cmnd_find_hash(conn->session, req->itt, req->ttt); if (!scsi_cmnd) { eprintk("unable to find scsi task %x %x\n", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_data; } if (param->data_pdu_inorder && offset != scsi_cmnd->exp_offset) { eprintk("invalid data offset %x %u %u\n", cmnd_itt(scsi_cmnd), offset, cmnd->exp_offset); goto skip_data; } if (offset + cmnd->pdu.datasize > cmnd_write_size(scsi_cmnd)) { eprintk("invalid data length %x %u %u\n", cmnd_itt(scsi_cmnd),(offset + cmnd->pdu.datasize), cmnd_write_size(scsi_cmnd)); goto skip_data; } if (scsi_cmnd->is_unsolicited_data) { if (offset + cmnd->pdu.datasize > param->first_burst_length) { eprintk("unsolicited data > first burst length %x %x\n", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_data; } } else { if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { eprintk("unexpected unsolicited data %x %x\n", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_data; } } dprintk(D_WRITE, "%u %p %p %p %u %u\n", req->ttt, cmnd, scsi_cmnd, scsi_cmnd->tio, offset, cmnd->pdu.datasize); if (cmnd_recv_pdu(conn, scsi_cmnd->tio, offset, cmnd->pdu.datasize) < 0) goto skip_data; if (scsi_cmnd->is_unsolicited_data) scsi_cmnd->r2t_length -= cmnd->pdu.datasize; scsi_cmnd->exp_offset += cmnd->pdu.datasize; return; skip_data: cmnd->pdu.bhs.opcode = ISCSI_OP_DATA_REJECT; cmnd_skip_pdu(cmnd); return; } static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd); static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) { struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *) &cmnd->pdu.bhs; struct iscsi_cmnd *scsi_cmnd; u32 offset; assert(cmnd); scsi_cmnd = cmnd->req; assert(scsi_cmnd); if (conn->read_overflow) { eprintk("connection read overflow %x %u\n", cmnd_itt(cmnd), conn->read_overflow); assert(scsi_cmnd->tio); offset = be32_to_cpu(req->buffer_offset); offset += cmnd->pdu.datasize - conn->read_overflow; if (cmnd_recv_pdu(conn, scsi_cmnd->tio, offset, conn->read_overflow) < 0) assert(0); return; } if (req->flags & ISCSI_FLG_FINAL) { if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) scsi_cmnd->is_unsolicited_data = 0; else scsi_cmnd->outstanding_r2t--; if (scsi_cmnd->outstanding_r2t == 0) assert(list_empty(&scsi_cmnd->pdu_list)); iscsi_session_push_cmnd(scsi_cmnd); } iscsi_cmnd_remove(cmnd); return; } void __cmnd_abort(struct iscsi_cmnd *cmnd) { if (cmnd_rxstart(cmnd)) set_cmnd_tmfabort(cmnd); if (cmnd_waitio(cmnd)) return; if (cmnd->conn->read_cmnd != cmnd) cmnd_release(cmnd, 1); } static int cmnd_abort(struct iscsi_session *session, struct iscsi_cmnd *req) { struct iscsi_task_mgt_hdr *req_hdr = (struct iscsi_task_mgt_hdr *)&req->pdu.bhs; struct iscsi_cmnd *cmnd; u32 min_cmd_sn = req_hdr->cmd_sn - session->max_queued_cmnds; req_hdr->ref_cmd_sn = be32_to_cpu(req_hdr->ref_cmd_sn); dprintk(D_GENERIC, "cmd_sn(%u) ref_cmd_sn(%u) min_cmd_sn(%u) rtt(%x)" " lun(%d) cid(%u)\n", req_hdr->cmd_sn, req_hdr->ref_cmd_sn, min_cmd_sn, req_hdr->rtt, translate_lun(req_hdr->lun), req->conn->cid); if (after(req_hdr->ref_cmd_sn, req_hdr->cmd_sn)) return ISCSI_RESPONSE_FUNCTION_REJECTED; if (!(cmnd = cmnd_find_hash(session, req_hdr->rtt, ISCSI_RESERVED_TAG))) { if (between(req_hdr->ref_cmd_sn, min_cmd_sn, req_hdr->cmd_sn)) return ISCSI_RESPONSE_FUNCTION_COMPLETE; else return ISCSI_RESPONSE_UNKNOWN_TASK; } dprintk(D_GENERIC, "itt(%x) opcode(%x) scsicode(%x) lun(%d) cid(%u)\n", cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd_scsicode(cmnd), translate_lun(cmnd_hdr(cmnd)->lun), cmnd->conn->cid); if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_TASK_MGT_MSG) return ISCSI_RESPONSE_FUNCTION_REJECTED; if (translate_lun(cmnd_hdr(cmnd)->lun) != translate_lun(req_hdr->lun)) return ISCSI_RESPONSE_FUNCTION_REJECTED; if (cmnd->conn && test_bit(CONN_ACTIVE, &cmnd->conn->state)) { if (cmnd->conn->cid != req->conn->cid) return ISCSI_RESPONSE_FUNCTION_REJECTED; } else { /* Switch cmnd connection allegiance */ } __cmnd_abort(cmnd); return ISCSI_RESPONSE_FUNCTION_COMPLETE; } static int target_reset(struct iscsi_cmnd *req, u32 lun, int all) { struct iscsi_target *target = req->conn->session->target; struct iscsi_session *session; struct iscsi_conn *conn; struct iscsi_cmnd *cmnd, *tmp; struct iet_volume *volume; list_for_each_entry(session, &target->session_list, list) { list_for_each_entry(conn, &session->conn_list, list) { list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { if (cmnd == req) continue; if (all) __cmnd_abort(cmnd); else if (translate_lun(cmnd_hdr(cmnd)->lun) == lun) __cmnd_abort(cmnd); } } } list_for_each_entry(volume, &target->volumes, list) { if (all || volume->lun == lun) { /* force release */ volume_release(volume, 0, 1); /* power-on, reset, or bus device reset occurred */ ua_establish_for_all_sessions(target, volume->lun, 0x29, 0x0); } } return 0; } static void task_set_abort(struct iscsi_cmnd *req) { struct iscsi_session *session = req->conn->session; struct iscsi_conn *conn; struct iscsi_cmnd *cmnd, *tmp; list_for_each_entry(conn, &session->conn_list, list) { list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { if (translate_lun(cmnd_hdr(cmnd)->lun) != translate_lun(cmnd_hdr(req)->lun)) continue; if (before(cmnd_hdr(cmnd)->cmd_sn, cmnd_hdr(req)->cmd_sn)) __cmnd_abort(cmnd); } } } static inline char *tmf_desc(int fun) { static char *tmf_desc[] = { "Unknown Function", "Abort Task", "Abort Task Set", "Clear ACA", "Clear Task Set", "Logical Unit Reset", "Target Warm Reset", "Target Cold Reset", "Task Reassign", }; if ((fun < ISCSI_FUNCTION_ABORT_TASK) || (fun > ISCSI_FUNCTION_TASK_REASSIGN)) fun = 0; return tmf_desc[fun]; } static inline char *rsp_desc(int rsp) { static char *rsp_desc[] = { "Function Complete", "Unknown Task", "Unknown LUN", "Task Allegiant", "Failover Unsupported", "Function Unsupported", "No Authorization", "Function Rejected", "Unknown Response", }; if (((rsp < ISCSI_RESPONSE_FUNCTION_COMPLETE) || (rsp > ISCSI_RESPONSE_NO_AUTHORIZATION)) && (rsp != ISCSI_RESPONSE_FUNCTION_REJECTED)) rsp = 8; else if (rsp == ISCSI_RESPONSE_FUNCTION_REJECTED) rsp = 7; return rsp_desc[rsp]; } static void execute_task_management(struct iscsi_cmnd *req) { struct iscsi_conn *conn = req->conn; struct iscsi_session *session = conn->session; struct iscsi_target *target = session->target; struct iscsi_cmnd *rsp; struct iscsi_task_mgt_hdr *req_hdr = (struct iscsi_task_mgt_hdr *)&req->pdu.bhs; struct iscsi_task_rsp_hdr *rsp_hdr; u32 lun; int function = req_hdr->function & ISCSI_FUNCTION_MASK; rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); rsp_hdr = (struct iscsi_task_rsp_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_SCSI_TASK_MGT_RSP; rsp_hdr->flags = ISCSI_FLG_FINAL; rsp_hdr->itt = req_hdr->itt; rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_COMPLETE; switch (function) { case ISCSI_FUNCTION_ABORT_TASK: case ISCSI_FUNCTION_ABORT_TASK_SET: case ISCSI_FUNCTION_CLEAR_ACA: case ISCSI_FUNCTION_CLEAR_TASK_SET: case ISCSI_FUNCTION_LOGICAL_UNIT_RESET: lun = translate_lun(req_hdr->lun); if (!volume_lookup(target, lun)) { rsp_hdr->response = ISCSI_RESPONSE_UNKNOWN_LUN; goto out; } } switch (function) { case ISCSI_FUNCTION_ABORT_TASK: rsp_hdr->response = cmnd_abort(conn->session, req); break; case ISCSI_FUNCTION_ABORT_TASK_SET: task_set_abort(req); break; case ISCSI_FUNCTION_CLEAR_ACA: rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_UNSUPPORTED; break; case ISCSI_FUNCTION_CLEAR_TASK_SET: rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_UNSUPPORTED; break; case ISCSI_FUNCTION_LOGICAL_UNIT_RESET: target_reset(req, translate_lun(req_hdr->lun), 0); break; case ISCSI_FUNCTION_TARGET_WARM_RESET: case ISCSI_FUNCTION_TARGET_COLD_RESET: target_reset(req, 0, 1); if (function == ISCSI_FUNCTION_TARGET_COLD_RESET) set_cmnd_close(rsp); break; case ISCSI_FUNCTION_TASK_REASSIGN: rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_UNSUPPORTED; break; default: rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_REJECTED; break; } out: iprintk("%s (%02x) issued on tid:%d lun:%d by sid:%llu (%s)\n", tmf_desc(function), function, target->tid, translate_lun(req_hdr->lun), session->sid, rsp_desc(rsp_hdr->response)); iscsi_cmnd_init_write(rsp); } static void nop_hdr_setup(struct iscsi_hdr *hdr, u8 opcode, __be32 itt, __be32 ttt) { hdr->opcode = opcode; hdr->flags = ISCSI_FLG_FINAL; hdr->itt = itt; hdr->ttt = ttt; } static void nop_out_exec(struct iscsi_cmnd *req) { struct iscsi_cmnd *rsp; if (cmnd_itt(req) != cpu_to_be32(ISCSI_RESERVED_TAG)) { rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); nop_hdr_setup(&rsp->pdu.bhs, ISCSI_OP_NOP_IN, req->pdu.bhs.itt, cpu_to_be32(ISCSI_RESERVED_TAG)); if (req->pdu.datasize) assert(req->tio); else assert(!req->tio); if (req->tio) { tio_get(req->tio); rsp->tio = req->tio; } assert(get_pgcnt(req->pdu.datasize) < ISCSI_CONN_IOV_MAX); rsp->pdu.datasize = req->pdu.datasize; iscsi_cmnd_init_write(rsp); } else { if (req->req) { dprintk(D_GENERIC, "releasing NOP-Out %p, ttt %x; " "removing NOP-In %p, ttt %x\n", req->req, cmnd_ttt(req->req), req, cmnd_ttt(req)); cmnd_release(req->req, 0); } iscsi_cmnd_remove(req); } } static void nop_in_timeout(unsigned long data) { struct iscsi_cmnd *req = (struct iscsi_cmnd *)data; printk(KERN_INFO "NOP-In ping timed out - closing sid:cid %llu:%u\n", req->conn->session->sid, req->conn->cid); clear_cmnd_timer_active(req); conn_close(req->conn); } /* create a fake NOP-Out req and treat the NOP-In as our rsp to it */ void send_nop_in(struct iscsi_conn *conn) { struct iscsi_cmnd *req = cmnd_alloc(conn, 1); struct iscsi_cmnd *rsp = iscsi_cmnd_create_rsp_cmnd(req, 0); req->target_task_tag = get_next_ttt(conn->session); nop_hdr_setup(&req->pdu.bhs, ISCSI_OP_NOP_OUT, cpu_to_be32(ISCSI_RESERVED_TAG), req->target_task_tag); nop_hdr_setup(&rsp->pdu.bhs, ISCSI_OP_NOP_IN, cpu_to_be32(ISCSI_RESERVED_TAG), req->target_task_tag); dprintk(D_GENERIC, "NOP-Out: %p, ttt %x, timer %p; " "NOP-In: %p, ttt %x;\n", req, cmnd_ttt(req), &req->timer, rsp, cmnd_ttt(rsp)); init_timer(&req->timer); req->timer.data = (unsigned long)req; req->timer.function = nop_in_timeout; if (cmnd_insert_hash_ttt(req, req->target_task_tag)) { eprintk("%s\n", "failed to insert fake NOP-Out into hash table"); cmnd_release(rsp, 0); cmnd_release(req, 0); } else iscsi_cmnd_init_write(rsp); } static void nop_in_tx_end(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; u32 t; if (cmnd->pdu.bhs.ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) return; /* * NOP-In ping issued by the target. * FIXME: Sanitize the NOP timeout earlier, during configuration */ t = conn->session->target->trgt_param.nop_timeout; if (!t || t > conn->session->target->trgt_param.nop_interval) { eprintk("Adjusting NOPTimeout of tid %u from %u to %u " "(== NOPInterval)\n", conn->session->target->tid, t, conn->session->target->trgt_param.nop_interval); t = conn->session->target->trgt_param.nop_interval; conn->session->target->trgt_param.nop_timeout = t; } dprintk(D_GENERIC, "NOP-In %p, %x: timer %p\n", cmnd, cmnd_ttt(cmnd), &cmnd->req->timer); set_cmnd_timer_active(cmnd->req); mod_timer(&cmnd->req->timer, jiffies + HZ * t); } static void logout_exec(struct iscsi_cmnd *req) { struct iscsi_logout_req_hdr *req_hdr; struct iscsi_cmnd *rsp; struct iscsi_logout_rsp_hdr *rsp_hdr; struct iscsi_conn *conn; u8 reason; req_hdr = (struct iscsi_logout_req_hdr *)&req->pdu.bhs; rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); rsp_hdr = (struct iscsi_logout_rsp_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_LOGOUT_RSP; rsp_hdr->flags = ISCSI_FLG_FINAL; rsp_hdr->itt = req_hdr->itt; reason = req_hdr->flags & ISCSI_FUNCTION_MASK; if (reason == ISCSI_LOGOUT_SESSION) set_cmnd_closeit(rsp); else if (reason == ISCSI_LOGOUT_CONNECTION) { if (req_hdr->cid != req->conn->cid) { conn = conn_lookup(req->conn->session, req_hdr->cid); if (!conn) rsp_hdr->response = 1; else if (test_bit(CONN_ACTIVE, &conn->state)) rsp_hdr->response = 3; else { /* end time2wait timer for conn */ } } else set_cmnd_close(rsp); } else if (reason == ISCSI_LOGOUT_CONNECTION_RECOVER) rsp_hdr->response = 2; else /* protocol error */ conn_close(req->conn); iscsi_cmnd_init_write(rsp); } static void iscsi_cmnd_exec(struct iscsi_cmnd *cmnd) { dprintk(D_GENERIC, "%p,%x,%u\n", cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn); switch (cmnd_opcode(cmnd)) { case ISCSI_OP_NOP_OUT: nop_out_exec(cmnd); break; case ISCSI_OP_SCSI_CMD: scsi_cmnd_exec(cmnd); break; case ISCSI_OP_SCSI_TASK_MGT_MSG: execute_task_management(cmnd); break; case ISCSI_OP_LOGOUT_CMD: logout_exec(cmnd); break; case ISCSI_OP_SCSI_REJECT: iscsi_cmnd_init_write(get_rsp_cmnd(cmnd)); break; case ISCSI_OP_TEXT_CMD: case ISCSI_OP_SNACK_CMD: break; default: eprintk("unexpected cmnd op %x\n", cmnd_opcode(cmnd)); break; } } static void __cmnd_send_pdu(struct iscsi_conn *conn, struct tio *tio, u32 offset, u32 size) { dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); assert(offset + size <= tio->pg_cnt * PAGE_CACHE_SIZE); conn->write_tcmnd = tio; conn->write_offset = offset; conn->write_size += size; } static void cmnd_send_pdu(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) { u32 size; struct tio *tio; if (!cmnd->pdu.datasize) return; size = (cmnd->pdu.datasize + 3) & -4; tio = cmnd->tio; assert(tio); assert(tio->size == size); __cmnd_send_pdu(conn, tio, 0, size); } static void set_cork(struct socket *sock, int on) { int opt = on; mm_segment_t oldfs; oldfs = get_fs(); set_fs(get_ds()); sock->ops->setsockopt(sock, SOL_TCP, TCP_CORK, (void *)&opt, sizeof(opt)); set_fs(oldfs); } void cmnd_release(struct iscsi_cmnd *cmnd, int force) { struct iscsi_cmnd *req, *rsp; int is_last = 0; if (!cmnd) return; /* eprintk("%x %lx %d\n", cmnd_opcode(cmnd), cmnd->flags, force); */ req = cmnd->req; is_last = cmnd_final(cmnd); if (force) { while (!list_empty(&cmnd->pdu_list)) { rsp = list_entry(cmnd->pdu_list.next, struct iscsi_cmnd, pdu_list); list_del_init(&rsp->list); list_del(&rsp->pdu_list); iscsi_cmnd_remove(rsp); } list_del_init(&cmnd->list); } else if (cmnd_queued(cmnd)) iscsi_scsi_dequeuecmnd(cmnd); if (cmnd_hashed(cmnd)) cmnd_remove_hash(cmnd); if (cmnd_lunit(cmnd)) { assert(cmnd->lun); volume_put(cmnd->lun); } list_del_init(&cmnd->pdu_list); iscsi_cmnd_remove(cmnd); if (is_last) { assert(!force); assert(req); cmnd_release(req, 0); } return; } void cmnd_tx_start(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; struct iovec *iop; dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd)); assert(cmnd); iscsi_cmnd_set_length(&cmnd->pdu); set_cork(conn->sock, 1); conn->write_iop = iop = conn->write_iov; iop->iov_base = &cmnd->pdu.bhs; iop->iov_len = sizeof(cmnd->pdu.bhs); iop++; conn->write_size = sizeof(cmnd->pdu.bhs); switch (cmnd_opcode(cmnd)) { case ISCSI_OP_NOP_IN: if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG) { /* NOP-In ping generated by us. Don't advance StatSN. */ cmnd_set_sn(cmnd, 0); cmnd_set_sn(cmnd->req, 0); cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn); cmnd->req->pdu.bhs.sn = cpu_to_be32(conn->stat_sn); } else cmnd_set_sn(cmnd, 1); cmnd_send_pdu(conn, cmnd); break; case ISCSI_OP_SCSI_RSP: cmnd_set_sn(cmnd, 1); cmnd_send_pdu(conn, cmnd); break; case ISCSI_OP_SCSI_TASK_MGT_RSP: cmnd_set_sn(cmnd, 1); break; case ISCSI_OP_TEXT_RSP: cmnd_set_sn(cmnd, 1); break; case ISCSI_OP_SCSI_DATA_IN: { struct iscsi_data_in_hdr *rsp = (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs; u32 offset; cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLG_FINAL) ? 1 : 0); offset = rsp->buffer_offset; rsp->buffer_offset = cpu_to_be32(offset); __cmnd_send_pdu(conn, cmnd->tio, offset, cmnd->pdu.datasize); break; } case ISCSI_OP_LOGOUT_RSP: cmnd_set_sn(cmnd, 1); break; case ISCSI_OP_R2T: cmnd_set_sn(cmnd, 0); cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn); break; case ISCSI_OP_ASYNC_MSG: cmnd_set_sn(cmnd, 1); break; case ISCSI_OP_REJECT: cmnd_set_sn(cmnd, 1); cmnd_send_pdu(conn, cmnd); break; default: eprintk("unexpected cmnd op %x\n", cmnd_opcode(cmnd)); break; } iop->iov_len = 0; // move this? conn->write_size = (conn->write_size + 3) & -4; iscsi_dump_pdu(&cmnd->pdu); } void cmnd_tx_end(struct iscsi_cmnd *cmnd) { struct iscsi_session *session = cmnd->conn->session; struct iscsi_conn *conn = cmnd->conn; dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd)); switch (cmnd_opcode(cmnd)) { case ISCSI_OP_NOP_IN: nop_in_tx_end(cmnd); break; case ISCSI_OP_SCSI_RSP: case ISCSI_OP_SCSI_TASK_MGT_RSP: case ISCSI_OP_TEXT_RSP: case ISCSI_OP_R2T: case ISCSI_OP_ASYNC_MSG: case ISCSI_OP_REJECT: case ISCSI_OP_SCSI_DATA_IN: case ISCSI_OP_LOGOUT_RSP: break; default: eprintk("unexpected cmnd op %x\n", cmnd_opcode(cmnd)); assert(0); break; } if (cmnd_close(cmnd)) conn_close(conn); else if (cmnd_closeit(cmnd)) list_for_each_entry(conn, &session->conn_list, list) conn_close(conn); list_del_init(&cmnd->list); set_cork(cmnd->conn->sock, 0); } /** * Push the command for execution. * This functions reorders the commands. * Called from the read thread. * * iscsi_session_push_cmnd - * @cmnd: ptr to command */ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd) { struct iscsi_session *session = cmnd->conn->session; struct list_head *entry; u32 cmd_sn; if (cmnd->r2t_length) { if (!cmnd->is_unsolicited_data) send_r2t(cmnd); return; } if (cmnd->outstanding_r2t) return; dprintk(D_GENERIC, "%p:%x %u,%u\n", cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn, session->exp_cmd_sn); if (cmnd_immediate(cmnd)) { iscsi_cmnd_exec(cmnd); return; } cmd_sn = cmnd->pdu.bhs.sn; if (cmd_sn == session->exp_cmd_sn) { while (1) { session->exp_cmd_sn = ++cmd_sn; iscsi_cmnd_exec(cmnd); if (list_empty(&session->pending_list)) break; cmnd = list_entry(session->pending_list.next, struct iscsi_cmnd, list); if (cmnd->pdu.bhs.sn != cmd_sn) break; list_del_init(&cmnd->list); clear_cmnd_pending(cmnd); } } else { set_cmnd_pending(cmnd); list_for_each(entry, &session->pending_list) { struct iscsi_cmnd *tmp = list_entry(entry, struct iscsi_cmnd, list); if (before(cmd_sn, tmp->pdu.bhs.sn)) break; } assert(list_empty(&cmnd->list)); list_add_tail(&cmnd->list, entry); } } static int check_segment_length(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; struct iscsi_sess_param *param = &conn->session->param; if (cmnd->pdu.datasize > param->max_recv_data_length) { eprintk("data too long %x %u %u\n", cmnd_itt(cmnd), cmnd->pdu.datasize, param->max_recv_data_length); conn_close(conn); return -EINVAL; } return 0; } void cmnd_rx_start(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; int err = 0; iscsi_dump_pdu(&cmnd->pdu); set_cmnd_rxstart(cmnd); if (check_segment_length(cmnd) < 0) return; switch (cmnd_opcode(cmnd)) { case ISCSI_OP_NOP_OUT: err = nop_out_start(conn, cmnd); break; case ISCSI_OP_SCSI_CMD: if (!(err = cmnd_insert_hash(cmnd))) scsi_cmnd_start(conn, cmnd); break; case ISCSI_OP_SCSI_TASK_MGT_MSG: err = cmnd_insert_hash(cmnd); break; case ISCSI_OP_SCSI_DATA_OUT: data_out_start(conn, cmnd); break; case ISCSI_OP_LOGOUT_CMD: err = cmnd_insert_hash(cmnd); break; case ISCSI_OP_TEXT_CMD: case ISCSI_OP_SNACK_CMD: err = -ISCSI_REASON_UNSUPPORTED_COMMAND; break; default: err = -ISCSI_REASON_UNSUPPORTED_COMMAND; break; } if (err < 0) { eprintk("%x %x %d\n", cmnd_opcode(cmnd), cmnd_itt(cmnd), err); iscsi_cmnd_reject(cmnd, -err); } } void cmnd_rx_end(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; if (cmnd_tmfabort(cmnd)) { cmnd_release(cmnd, 1); return; } dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd)); switch (cmnd_opcode(cmnd)) { case ISCSI_OP_SCSI_REJECT: case ISCSI_OP_NOP_OUT: case ISCSI_OP_SCSI_CMD: case ISCSI_OP_SCSI_TASK_MGT_MSG: case ISCSI_OP_TEXT_CMD: case ISCSI_OP_LOGOUT_CMD: iscsi_session_push_cmnd(cmnd); break; case ISCSI_OP_SCSI_DATA_OUT: data_out_end(conn, cmnd); break; case ISCSI_OP_SNACK_CMD: break; case ISCSI_OP_PDU_REJECT: iscsi_cmnd_init_write(get_rsp_cmnd(cmnd)); break; case ISCSI_OP_DATA_REJECT: cmnd_release(cmnd, 0); break; default: eprintk("unexpected cmnd op %x\n", cmnd_opcode(cmnd)); BUG(); break; } } static void iscsi_exit(void) { wthread_module_exit(); unregister_chrdev(ctr_major, ctr_name); iet_procfs_exit(); event_exit(); tio_exit(); iotype_exit(); ua_exit(); if (iscsi_cmnd_cache) kmem_cache_destroy(iscsi_cmnd_cache); } static int iscsi_init(void) { int err = -ENOMEM; printk("iSCSI Enterprise Target Software - version %s\n", IET_VERSION_STRING); if ((ctr_major = register_chrdev(0, ctr_name, &ctr_fops)) < 0) { eprintk("failed to register the control device %d\n", ctr_major); return ctr_major; } if ((err = iet_procfs_init()) < 0) goto err; if ((err = event_init()) < 0) goto err; iscsi_cmnd_cache = KMEM_CACHE(iscsi_cmnd, 0); if (!iscsi_cmnd_cache) goto err; err = ua_init(); if (err < 0) goto err; if ((err = tio_init()) < 0) goto err; if ((err = iotype_init()) < 0) goto err; if ((err = wthread_module_init()) < 0) goto err; return 0; err: iscsi_exit(); return err; } module_param(worker_thread_pool_size, ulong, S_IRUGO); MODULE_PARM_DESC(worker_thread_pool_size, "Size of the worker thread pool " "(0 = dedicated threads per target (default))"); module_param(debug_enable_flags, ulong, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug_enable_flags, "debug bitmask, low bits (0 ... 8) used, see iscsi_dbg.h"); module_init(iscsi_init); module_exit(iscsi_exit); MODULE_VERSION(IET_VERSION_STRING); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("iSCSI Enterprise Target"); MODULE_AUTHOR("IET development team "); iscsitarget-1.4.20.3+svn502/kernel/iscsi.h000066400000000000000000000344101233224651300200570ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * Copyright (C) 2008 Arne Redlich * * Released under the terms of the GNU GPL v2.0. */ #ifndef __ISCSI_H__ #define __ISCSI_H__ #include #include #include #include #include #include #include #include #include #include "iscsi_hdr.h" #include "iet_u.h" #include "compat.h" #include "persist.h" #define IET_SENSE_BUF_SIZE 18 struct iscsi_sess_param { int initial_r2t; int immediate_data; int max_connections; int max_recv_data_length; int max_xmit_data_length; int max_burst_length; int first_burst_length; int default_wait_time; int default_retain_time; int max_outstanding_r2t; int data_pdu_inorder; int data_sequence_inorder; int error_recovery_level; int header_digest; int data_digest; int ofmarker; int ifmarker; int ofmarkint; int ifmarkint; }; struct iscsi_trgt_param { int wthreads; int target_type; int queued_cmnds; int nop_interval; int nop_timeout; }; /* target io */ struct tio { loff_t offset; /* byte offset on target */ u32 size; /* total io bytes */ u32 pg_cnt; /* total page count */ struct page **pvec; /* array of pages holding data */ atomic_t count; /* ref count */ }; struct tio_iterator { struct tio *tio; u32 size; u32 pg_idx; u32 pg_off; }; struct network_thread_info { struct task_struct *task; unsigned long flags; struct list_head active_conns; spinlock_t nthread_lock; void (*old_state_change)(struct sock *); void (*old_data_ready)(struct sock *, int); void (*old_write_space)(struct sock *); }; struct worker_thread_info; struct worker_thread { struct task_struct *w_task; struct list_head w_list; struct worker_thread_info *w_info; }; struct worker_thread_info { spinlock_t wthread_lock; u32 nr_running_wthreads; struct list_head wthread_list; struct list_head work_queue; wait_queue_head_t wthread_sleep; struct io_context *wthread_ioc; }; struct iscsi_cmnd; struct target_type { int id; int (*execute_cmnd) (struct iscsi_cmnd *); }; enum iscsi_device_state { IDEV_RUNNING, IDEV_DEL, }; struct iscsi_target { struct list_head t_list; u32 tid; char name[ISCSI_NAME_LEN]; struct iscsi_sess_param sess_param; struct iscsi_trgt_param trgt_param; atomic_t nr_volumes; struct list_head volumes; struct list_head session_list; /* Prevents races between add/del session and adding UAs */ spinlock_t session_list_lock; struct network_thread_info nthread_info; /* Points either to own list or global pool */ struct worker_thread_info * wthread_info; struct mutex target_mutex; }; struct iscsi_queue { spinlock_t queue_lock; struct iscsi_cmnd *ordered_cmnd; struct list_head wait_list; int active_cnt; }; struct iet_volume { u32 lun; enum iscsi_device_state l_state; atomic_t l_count; struct iscsi_target *target; struct list_head list; struct iscsi_queue queue; u8 scsi_id[SCSI_ID_LEN]; u8 scsi_sn[SCSI_SN_LEN + 1]; u32 blk_shift; u64 blk_cnt; struct reservation reservation; spinlock_t reserve_lock; unsigned long flags; struct iotype *iotype; void *private; }; enum lu_flags { LU_READONLY, LU_WCACHE, LU_RCACHE, }; #define LUReadonly(lu) test_bit(LU_READONLY, &(lu)->flags) #define SetLUReadonly(lu) set_bit(LU_READONLY, &(lu)->flags) #define LUWCache(lu) test_bit(LU_WCACHE, &(lu)->flags) #define SetLUWCache(lu) set_bit(LU_WCACHE, &(lu)->flags) #define ClearLUWCache(lu) clear_bit(LU_WCACHE, &(lu)->flags) #define LURCache(lu) test_bit(LU_RCACHE, &(lu)->flags) #define SetLURCache(lu) set_bit(LU_RCACHE, &(lu)->flags) #define ClearLURCache(lu) clear_bit(LU_RCACHE, &(lu)->flags) #define IET_HASH_ORDER 8 #define cmnd_hashfn(itt) hash_long((itt), IET_HASH_ORDER) #define UA_HASH_LEN 8 struct iscsi_session { struct list_head list; struct iscsi_target *target; struct completion *done; char *initiator; u64 sid; u32 exp_cmd_sn; u32 max_cmd_sn; struct iscsi_sess_param param; u32 max_queued_cmnds; struct list_head conn_list; struct list_head pending_list; spinlock_t cmnd_hash_lock; struct list_head cmnd_hash[1 << IET_HASH_ORDER]; spinlock_t ua_hash_lock; struct list_head ua_hash[UA_HASH_LEN]; u32 next_ttt; }; enum connection_state_bit { CONN_ACTIVE, CONN_CLOSING, CONN_WSPACE_WAIT, CONN_NEED_NOP_IN, }; #define ISCSI_CONN_IOV_MAX (((256 << 10) >> PAGE_SHIFT) + 1) struct iscsi_conn { struct list_head list; /* list entry in session list */ struct iscsi_session *session; /* owning session */ u16 cid; unsigned long state; u32 stat_sn; u32 exp_stat_sn; int hdigest_type; int ddigest_type; struct list_head poll_list; struct file *file; struct socket *sock; spinlock_t list_lock; atomic_t nr_cmnds; atomic_t nr_busy_cmnds; struct list_head pdu_list; /* in/outcoming pdus */ struct list_head write_list; /* list of data pdus to be sent */ struct timer_list nop_timer; struct iscsi_cmnd *read_cmnd; struct msghdr read_msg; struct iovec read_iov[ISCSI_CONN_IOV_MAX]; u32 read_size; u32 read_overflow; int read_state; struct iscsi_cmnd *write_cmnd; struct iovec write_iov[ISCSI_CONN_IOV_MAX]; struct iovec *write_iop; struct tio *write_tcmnd; u32 write_size; u32 write_offset; int write_state; struct hash_desc rx_hash; struct hash_desc tx_hash; struct scatterlist hash_sg[ISCSI_CONN_IOV_MAX]; }; struct iscsi_pdu { struct iscsi_hdr bhs; void *ahs; unsigned int ahssize; unsigned int datasize; }; typedef void (iet_show_info_t)(struct seq_file *seq, struct iscsi_target *target); struct iscsi_cmnd { struct list_head list; struct list_head conn_list; unsigned long flags; struct iscsi_conn *conn; struct iet_volume *lun; struct iscsi_pdu pdu; struct list_head pdu_list; struct list_head hash_list; struct tio *tio; u8 status; struct timer_list timer; u32 r2t_sn; u32 r2t_length; u32 exp_offset; u32 is_unsolicited_data; u32 target_task_tag; u32 outstanding_r2t; u32 hdigest; u32 ddigest; struct iscsi_cmnd *req; unsigned char sense_buf[IET_SENSE_BUF_SIZE]; }; struct ua_entry { struct list_head entry; struct iscsi_session *session; /* only used for debugging ATM */ u32 lun; u8 asc; u8 ascq; }; #define ISCSI_OP_SCSI_REJECT ISCSI_OP_VENDOR1_CMD #define ISCSI_OP_PDU_REJECT ISCSI_OP_VENDOR2_CMD #define ISCSI_OP_DATA_REJECT ISCSI_OP_VENDOR3_CMD #define ISCSI_OP_SCSI_ABORT ISCSI_OP_VENDOR4_CMD /* iscsi.c */ extern unsigned long worker_thread_pool_size; extern struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *, int); extern void cmnd_rx_start(struct iscsi_cmnd *); extern void cmnd_rx_end(struct iscsi_cmnd *); extern void cmnd_tx_start(struct iscsi_cmnd *); extern void cmnd_tx_end(struct iscsi_cmnd *); extern void cmnd_release(struct iscsi_cmnd *, int); extern void send_data_rsp(struct iscsi_cmnd *, void (*)(struct iscsi_cmnd *)); extern void send_scsi_rsp(struct iscsi_cmnd *, void (*)(struct iscsi_cmnd *)); extern void iscsi_cmnd_set_sense(struct iscsi_cmnd *, u8 sense_key, u8 asc, u8 ascq); extern void send_nop_in(struct iscsi_conn *); extern u32 translate_lun(u16 *data); extern void __cmnd_abort(struct iscsi_cmnd *cmnd); /* conn.c */ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16); extern int conn_add(struct iscsi_session *, struct conn_info *); extern int conn_del(struct iscsi_session *, struct conn_info *); extern int conn_free(struct iscsi_conn *); extern void conn_close(struct iscsi_conn *); extern void conn_info_show(struct seq_file *, struct iscsi_session *); /* nthread.c */ extern int nthread_init(struct iscsi_target *); extern int nthread_start(struct iscsi_target *); extern int nthread_stop(struct iscsi_target *); extern void __nthread_wakeup(struct network_thread_info *); extern void nthread_wakeup(struct iscsi_target *); /* wthread.c */ extern int wthread_init(struct worker_thread_info *info); extern int wthread_start(struct worker_thread_info *info, int wthreads, u32 tid); extern int wthread_stop(struct worker_thread_info *info); extern void wthread_queue(struct iscsi_cmnd *); extern struct target_type *target_type_array[]; extern int wthread_module_init(void); extern void wthread_module_exit(void); extern struct worker_thread_info *worker_thread_pool; /* target.c */ extern int target_lock(struct iscsi_target *, int); extern void target_unlock(struct iscsi_target *); struct iscsi_target *target_lookup_by_id(u32); extern int target_add(struct target_info *); extern int target_del(u32 id); extern void target_del_all(void); extern struct seq_operations iet_seq_op; /* config.c */ extern int iet_procfs_init(void); extern void iet_procfs_exit(void); extern int iet_info_show(struct seq_file *, iet_show_info_t *); /* session.c */ extern struct file_operations session_seq_fops; extern struct iscsi_session *session_lookup(struct iscsi_target *, u64); extern int session_add(struct iscsi_target *, struct session_info *); extern int session_del(struct iscsi_target *, u64); extern void session_abort_tasks(struct iscsi_session *, u32 lun); /* volume.c */ extern struct file_operations volume_seq_fops; extern int volume_add(struct iscsi_target *, struct volume_info *); extern int iscsi_volume_del(struct iscsi_target *, struct volume_info *); extern void iscsi_volume_destroy(struct iet_volume *); extern struct iet_volume *volume_lookup(struct iscsi_target *, u32); extern struct iet_volume *volume_get(struct iscsi_target *, u32); extern void volume_put(struct iet_volume *); extern int volume_reserve(struct iet_volume *volume, u64 sid); extern int volume_release(struct iet_volume *volume, u64 sid, int force); extern int is_volume_reserved(struct iet_volume *volume, u64 sid, u8 *scb); /* tio.c */ extern int tio_init(void); extern void tio_exit(void); extern struct tio *tio_alloc(int); extern void tio_get(struct tio *); extern void tio_put(struct tio *); extern void tio_set(struct tio *, u32, loff_t); extern int tio_read(struct iet_volume *, struct tio *); extern int tio_write(struct iet_volume *, struct tio *); extern int tio_sync(struct iet_volume *, struct tio *); extern void tio_init_iterator(struct tio *tio, struct tio_iterator *iter); extern size_t tio_add_data(struct tio_iterator *iter, const u8 *data, size_t len); /* iotype.c */ extern struct iotype *get_iotype(const char *name); extern void put_iotype(struct iotype *iot); /* params.c */ extern int iscsi_param_set(struct iscsi_target *, struct iscsi_param_info *, int); /* target_disk.c */ extern struct target_type disk_ops; /* event.c */ extern int event_send(u32, u64, u32, u32, int); extern int event_init(void); extern void event_exit(void); /* ua.c */ int ua_init(void); void ua_exit(void); struct ua_entry * ua_get_first(struct iscsi_session *, u32 lun); struct ua_entry * ua_get_match(struct iscsi_session *, u32 lun, u8 asc, u8 ascq); void ua_free(struct ua_entry *); int ua_pending(struct iscsi_session *, u32 lun); void ua_establish_for_session(struct iscsi_session *, u32 lun, u8 asc, u8 ascq); void ua_establish_for_other_sessions(struct iscsi_session *, u32 lun, u8 asc, u8 ascq); void ua_establish_for_all_sessions(struct iscsi_target *, u32 lun, u8 asc, u8 ascq); #define get_pgcnt(size) (((size) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) static inline void iscsi_cmnd_get_length(struct iscsi_pdu *pdu) { #if defined(__BIG_ENDIAN) pdu->ahssize = pdu->bhs.length.ahslength * 4; pdu->datasize = pdu->bhs.length.datalength; #elif defined(__LITTLE_ENDIAN) pdu->ahssize = (pdu->bhs.length & 0xff) * 4; pdu->datasize = be32_to_cpu(pdu->bhs.length & ~0xff); #else #error #endif } static inline void iscsi_cmnd_set_length(struct iscsi_pdu *pdu) { #if defined(__BIG_ENDIAN) pdu->bhs.length.ahslength = pdu->ahssize / 4; pdu->bhs.length.datalength = pdu->datasize; #elif defined(__LITTLE_ENDIAN) pdu->bhs.length = cpu_to_be32(pdu->datasize) | (pdu->ahssize / 4); #else #error #endif } #define cmnd_hdr(cmnd) ((struct iscsi_scsi_cmd_hdr *) (&((cmnd)->pdu.bhs))) #define cmnd_ttt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.ttt) #define cmnd_itt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.itt) #define cmnd_opcode(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OPCODE_MASK) #define cmnd_scsicode(cmnd) cmnd_hdr(cmnd)->scb[0] #define cmnd_immediate(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) #define PAD_TO_4_BYTES(n) \ (((n) + 3) & -4) /* default and maximum scsi level block sizes */ #define IET_DEF_BLOCK_SIZE 512 #define IET_MAX_BLOCK_SIZE 4096 enum cmnd_flags { CMND_hashed, CMND_queued, CMND_final, CMND_waitio, CMND_close, CMND_closeit, CMND_lunit, CMND_pending, CMND_tmfabort, CMND_rxstart, CMND_timer_active, }; #define set_cmnd_hashed(cmnd) set_bit(CMND_hashed, &(cmnd)->flags) #define cmnd_hashed(cmnd) test_bit(CMND_hashed, &(cmnd)->flags) #define set_cmnd_queued(cmnd) set_bit(CMND_queued, &(cmnd)->flags) #define cmnd_queued(cmnd) test_bit(CMND_queued, &(cmnd)->flags) #define set_cmnd_final(cmnd) set_bit(CMND_final, &(cmnd)->flags) #define cmnd_final(cmnd) test_bit(CMND_final, &(cmnd)->flags) #define set_cmnd_waitio(cmnd) set_bit(CMND_waitio, &(cmnd)->flags) #define cmnd_waitio(cmnd) test_bit(CMND_waitio, &(cmnd)->flags) #define set_cmnd_close(cmnd) set_bit(CMND_close, &(cmnd)->flags) #define cmnd_close(cmnd) test_bit(CMND_close, &(cmnd)->flags) #define set_cmnd_closeit(cmnd) set_bit(CMND_closeit, &(cmnd)->flags) #define cmnd_closeit(cmnd) test_bit(CMND_closeit, &(cmnd)->flags) #define set_cmnd_lunit(cmnd) set_bit(CMND_lunit, &(cmnd)->flags) #define cmnd_lunit(cmnd) test_bit(CMND_lunit, &(cmnd)->flags) #define set_cmnd_pending(cmnd) set_bit(CMND_pending, &(cmnd)->flags) #define clear_cmnd_pending(cmnd) clear_bit(CMND_pending, &(cmnd)->flags) #define cmnd_pending(cmnd) test_bit(CMND_pending, &(cmnd)->flags) #define set_cmnd_tmfabort(cmnd) set_bit(CMND_tmfabort, &(cmnd)->flags) #define cmnd_tmfabort(cmnd) test_bit(CMND_tmfabort, &(cmnd)->flags) #define set_cmnd_rxstart(cmnd) set_bit(CMND_rxstart, &(cmnd)->flags) #define cmnd_rxstart(cmnd) test_bit(CMND_rxstart, &(cmnd)->flags) #define set_cmnd_timer_active(cmnd) set_bit(CMND_timer_active, &(cmnd)->flags) #define clear_cmnd_timer_active(cmnd) \ clear_bit(CMND_timer_active, &(cmnd)->flags) #define cmnd_timer_active(cmnd) test_bit(CMND_timer_active, &(cmnd)->flags) #define VENDOR_ID "IET" #define PRODUCT_ID "VIRTUAL-DISK" #define PRODUCT_REV "0" #endif /* __ISCSI_H__ */ iscsitarget-1.4.20.3+svn502/kernel/iscsi_dbg.h000066400000000000000000000070351233224651300206760ustar00rootroot00000000000000#ifndef ISCSI_DBG_H #define ISCSI_DBG_H #define D_SETUP (1UL << 0) #define D_EXIT (1UL << 1) #define D_GENERIC (1UL << 2) #define D_READ (1UL << 3) #define D_WRITE (1UL << 4) #define D_IOD (1UL << 5) #define D_THREAD (1UL << 6) #define D_TASK_MGT (1UL << 7) #define D_IOMODE (1UL << 8) #define D_UAC (1UL << 9) #define D_PR (1UL << 10) #define D_VAAI (1UL << 11) #define D_DATA (D_READ | D_WRITE) extern unsigned long debug_enable_flags; #define PFX "iscsi_trgt: " #define dprintk(debug, fmt, args...) \ do { \ if ((debug) & debug_enable_flags) { \ printk(KERN_DEBUG PFX "%s(%d) " fmt, \ __FUNCTION__, \ __LINE__, \ ##args); \ } \ } while (0) #define dprintk_ua(ua, sess, lun) \ dprintk(D_UAC, "sess %llu, lun %u: %p %x %x\n", \ (sess)->sid, lun, ua, \ (ua) ? (ua)->asc : 0, \ (ua) ? (ua)->ascq : 0) #define dprintk_pr(cmd, fmt, args...) \ dprintk(D_PR, "%#Lx:%hu, lun %u, cmnd %p: " fmt, \ cmnd->conn->session->sid, \ cmnd->conn->cid, \ cmnd->lun->lun, \ cmnd, \ ##args) #define eprintk(fmt, args...) \ do { \ printk(KERN_ERR PFX "%s(%d) " fmt, \ __FUNCTION__, \ __LINE__, \ ##args); \ } while (0) #define iprintk(X...) printk(KERN_INFO PFX X) /* this has to go away - use BUG() and friends instead */ #define assert(p) \ do { \ if (!(p)) { \ printk(KERN_CRIT PFX "BUG at %s:%d assert(%s)\n", \ __FILE__, __LINE__, #p); \ dump_stack(); \ BUG(); \ } \ } while (0) #ifdef D_IOV static inline void iscsi_dump_iov(struct msghdr *msg) { int i; printk(PFX "%p, %d\n", msg->msg_iov, msg->msg_iovlen); for (i = 0; i < min_t(size_t, msg->msg_iovlen, ISCSI_CONN_IOV_MAX); i++) printk(PFX "%d: %p,%d\n", i, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); } #else #define iscsi_dump_iov(x) do {} while (0) #endif #ifdef D_DUMP_PDU static void iscsi_dump_char(int ch) { static unsigned char text[16]; static int i = 0; if (ch < 0) { while ((i % 16) != 0) { printk(" "); text[i] = ' '; i++; if ((i % 16) == 0) printk(" | %.16s |\n", text); else if ((i % 4) == 0) printk(" |"); } i = 0; return; } text[i] = (ch < 0x20 || (ch >= 0x80 && ch <= 0xa0)) ? ' ' : ch; printk(" %02x", ch); i++; if ((i % 16) == 0) { printk(" | %.16s |\n", text); i = 0; } else if ((i % 4) == 0) printk(" |"); } static inline void iscsi_dump_pdu(struct iscsi_pdu *pdu) { unsigned char *buf; int i; buf = (void *)&pdu->bhs; printk(PFX "BHS: (%p,%d)\n", buf, sizeof(pdu->bhs)); for (i = 0; i < sizeof(pdu->bhs); i++) iscsi_dump_char(*buf++); iscsi_dump_char(-1); buf = (void *)pdu->ahs; printk(PFX "AHS: (%p,%d)\n", buf, pdu->ahssize); for (i = 0; i < pdu->ahssize; i++) iscsi_dump_char(*buf++); iscsi_dump_char(-1); printk(PFX "Data: (%d)\n", pdu->datasize); } #else #define iscsi_dump_pdu(x) do {} while (0) #endif #define show_param(param)\ {\ dprintk(D_SETUP, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",\ (param)->initial_r2t,\ (param)->immediate_data,\ (param)->max_connections,\ (param)->max_recv_data_length,\ (param)->max_xmit_data_length,\ (param)->max_burst_length,\ (param)->first_burst_length,\ (param)->default_wait_time,\ (param)->default_retain_time,\ (param)->max_outstanding_r2t,\ (param)->data_pdu_inorder,\ (param)->data_sequence_inorder,\ (param)->error_recovery_level,\ (param)->header_digest,\ (param)->data_digest);\ } #endif iscsitarget-1.4.20.3+svn502/kernel/iscsi_hdr.h000066400000000000000000000242431233224651300207170ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #ifndef __ISCSI_HDR_H__ #define __ISCSI_HDR_H__ #include #include #define ISCSI_VERSION 0 #ifndef __packed #define __packed __attribute__ ((packed)) #endif struct iscsi_hdr { u8 opcode; /* 0 */ u8 flags; u8 spec1[2]; #if defined(__BIG_ENDIAN_BITFIELD) struct { /* 4 */ unsigned ahslength : 8; unsigned datalength : 24; } length; #elif defined(__LITTLE_ENDIAN_BITFIELD) u32 length; /* 4 */ #endif u16 lun[4]; /* 8 */ u32 itt; /* 16 */ u32 ttt; /* 20 */ u32 sn; /* 24 */ u32 exp_sn; /* 28 */ u32 max_sn; /* 32 */ u32 spec3[3]; /* 36 */ } __packed; /* 48 */ /* Opcode encoding bits */ #define ISCSI_OP_RETRY 0x80 #define ISCSI_OP_IMMEDIATE 0x40 #define ISCSI_OPCODE_MASK 0x3F /* Client to Server Message Opcode values */ #define ISCSI_OP_NOP_OUT 0x00 #define ISCSI_OP_SCSI_CMD 0x01 #define ISCSI_OP_SCSI_TASK_MGT_MSG 0x02 #define ISCSI_OP_LOGIN_CMD 0x03 #define ISCSI_OP_TEXT_CMD 0x04 #define ISCSI_OP_SCSI_DATA_OUT 0x05 #define ISCSI_OP_LOGOUT_CMD 0x06 #define ISCSI_OP_SNACK_CMD 0x10 #define ISCSI_OP_VENDOR1_CMD 0x1c #define ISCSI_OP_VENDOR2_CMD 0x1d #define ISCSI_OP_VENDOR3_CMD 0x1e #define ISCSI_OP_VENDOR4_CMD 0x1f /* Server to Client Message Opcode values */ #define ISCSI_OP_NOP_IN 0x20 #define ISCSI_OP_SCSI_RSP 0x21 #define ISCSI_OP_SCSI_TASK_MGT_RSP 0x22 #define ISCSI_OP_LOGIN_RSP 0x23 #define ISCSI_OP_TEXT_RSP 0x24 #define ISCSI_OP_SCSI_DATA_IN 0x25 #define ISCSI_OP_LOGOUT_RSP 0x26 #define ISCSI_OP_R2T 0x31 #define ISCSI_OP_ASYNC_MSG 0x32 #define ISCSI_OP_REJECT 0x3f struct iscsi_ahs_hdr { u16 ahslength; u8 ahstype; } __packed; #define ISCSI_AHSTYPE_CDB 1 #define ISCSI_AHSTYPE_RLENGTH 2 union iscsi_sid { struct { u8 isid[6]; /* Initiator Session ID */ u16 tsih; /* Target Session ID */ } id; u64 id64; } __packed; struct iscsi_scsi_cmd_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 itt; u32 data_length; u32 cmd_sn; u32 exp_stat_sn; u8 scb[16]; } __packed; #define ISCSI_CMD_FINAL 0x80 #define ISCSI_CMD_READ 0x40 #define ISCSI_CMD_WRITE 0x20 #define ISCSI_CMD_ATTR_MASK 0x07 #define ISCSI_CMD_UNTAGGED 0x00 #define ISCSI_CMD_SIMPLE 0x01 #define ISCSI_CMD_ORDERED 0x02 #define ISCSI_CMD_HEAD_OF_QUEUE 0x03 #define ISCSI_CMD_ACA 0x04 struct iscsi_cdb_ahdr { u16 ahslength; u8 ahstype; u8 reserved; u8 cdb[0]; } __packed; struct iscsi_rlength_ahdr { u16 ahslength; u8 ahstype; u8 reserved; u32 read_length; } __packed; struct iscsi_scsi_rsp_hdr { u8 opcode; u8 flags; u8 response; u8 cmd_status; u8 ahslength; u8 datalength[3]; u32 rsvd1[2]; u32 itt; u32 snack; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 exp_data_sn; u32 bi_residual_count; u32 residual_count; } __packed; #define ISCSI_FLG_RESIDUAL_UNDERFLOW 0x02 #define ISCSI_FLG_RESIDUAL_OVERFLOW 0x04 #define ISCSI_FLG_BIRESIDUAL_UNDERFLOW 0x08 #define ISCSI_FLG_BIRESIDUAL_OVERFLOW 0x10 #define ISCSI_RESPONSE_COMMAND_COMPLETED 0x00 #define ISCSI_RESPONSE_TARGET_FAILURE 0x01 struct iscsi_sense_data { u16 length; u8 data[0]; } __packed; struct iscsi_task_mgt_hdr { u8 opcode; u8 function; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 itt; u32 rtt; u32 cmd_sn; u32 exp_stat_sn; u32 ref_cmd_sn; u32 exp_data_sn; u32 rsvd2[2]; } __packed; #define ISCSI_FUNCTION_MASK 0x7f #define ISCSI_FUNCTION_ABORT_TASK 1 #define ISCSI_FUNCTION_ABORT_TASK_SET 2 #define ISCSI_FUNCTION_CLEAR_ACA 3 #define ISCSI_FUNCTION_CLEAR_TASK_SET 4 #define ISCSI_FUNCTION_LOGICAL_UNIT_RESET 5 #define ISCSI_FUNCTION_TARGET_WARM_RESET 6 #define ISCSI_FUNCTION_TARGET_COLD_RESET 7 #define ISCSI_FUNCTION_TASK_REASSIGN 8 struct iscsi_task_rsp_hdr { u8 opcode; u8 flags; u8 response; u8 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 rsvd3; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 rsvd4[3]; } __packed; #define ISCSI_RESPONSE_FUNCTION_COMPLETE 0 #define ISCSI_RESPONSE_UNKNOWN_TASK 1 #define ISCSI_RESPONSE_UNKNOWN_LUN 2 #define ISCSI_RESPONSE_TASK_ALLEGIANT 3 #define ISCSI_RESPONSE_FAILOVER_UNSUPPORTED 4 #define ISCSI_RESPONSE_FUNCTION_UNSUPPORTED 5 #define ISCSI_RESPONSE_NO_AUTHORIZATION 6 #define ISCSI_RESPONSE_FUNCTION_REJECTED 255 struct iscsi_data_out_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 itt; u32 ttt; u32 rsvd2; u32 exp_stat_sn; u32 rsvd3; u32 data_sn; u32 buffer_offset; u32 rsvd4; } __packed; struct iscsi_data_in_hdr { u8 opcode; u8 flags; u8 rsvd1; u8 cmd_status; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 ttt; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 data_sn; u32 buffer_offset; u32 residual_count; } __packed; #define ISCSI_FLG_STATUS 0x01 struct iscsi_r2t_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 itt; u32 ttt; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 r2t_sn; u32 buffer_offset; u32 data_length; } __packed; struct iscsi_async_msg_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 ffffffff; u32 rsvd2; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u8 async_event; u8 async_vcode; u16 param1; u16 param2; u16 param3; u32 rsvd3; } __packed; #define ISCSI_ASYNC_SCSI 0 #define ISCSI_ASYNC_LOGOUT 1 #define ISCSI_ASYNC_DROP_CONNECTION 2 #define ISCSI_ASYNC_DROP_SESSION 3 #define ISCSI_ASYNC_PARAM_REQUEST 4 #define ISCSI_ASYNC_VENDOR 255 struct iscsi_text_req_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 ttt; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd3[4]; } __packed; struct iscsi_text_rsp_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 ttt; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 rsvd3[3]; } __packed; struct iscsi_login_req_hdr { u8 opcode; u8 flags; u8 max_version; /* Max. version supported */ u8 min_version; /* Min. version supported */ u8 ahslength; u8 datalength[3]; union iscsi_sid sid; u32 itt; /* Initiator Task Tag */ u16 cid; /* Connection ID */ u16 rsvd1; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd2[4]; } __packed; struct iscsi_login_rsp_hdr { u8 opcode; u8 flags; u8 max_version; /* Max. version supported */ u8 active_version; /* Active version */ u8 ahslength; u8 datalength[3]; union iscsi_sid sid; u32 itt; /* Initiator Task Tag */ u32 rsvd1; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u8 status_class; /* see Login RSP ststus classes below */ u8 status_detail; /* see Login RSP Status details below */ u8 rsvd2[10]; } __packed; #define ISCSI_FLG_FINAL 0x80 #define ISCSI_FLG_TRANSIT 0x80 #define ISCSI_FLG_CSG_SECURITY 0x00 #define ISCSI_FLG_CSG_LOGIN 0x04 #define ISCSI_FLG_CSG_FULL_FEATURE 0x0c #define ISCSI_FLG_CSG_MASK 0x0c #define ISCSI_FLG_NSG_SECURITY 0x00 #define ISCSI_FLG_NSG_LOGIN 0x01 #define ISCSI_FLG_NSG_FULL_FEATURE 0x03 #define ISCSI_FLG_NSG_MASK 0x03 /* Login Status response classes */ #define ISCSI_STATUS_SUCCESS 0x00 #define ISCSI_STATUS_REDIRECT 0x01 #define ISCSI_STATUS_INITIATOR_ERR 0x02 #define ISCSI_STATUS_TARGET_ERR 0x03 /* Login Status response detail codes */ /* Class-0 (Success) */ #define ISCSI_STATUS_ACCEPT 0x00 /* Class-1 (Redirection) */ #define ISCSI_STATUS_TGT_MOVED_TEMP 0x01 #define ISCSI_STATUS_TGT_MOVED_PERM 0x02 /* Class-2 (Initiator Error) */ #define ISCSI_STATUS_INIT_ERR 0x00 #define ISCSI_STATUS_AUTH_FAILED 0x01 #define ISCSI_STATUS_TGT_FORBIDDEN 0x02 #define ISCSI_STATUS_TGT_NOT_FOUND 0x03 #define ISCSI_STATUS_TGT_REMOVED 0x04 #define ISCSI_STATUS_NO_VERSION 0x05 #define ISCSI_STATUS_TOO_MANY_CONN 0x06 #define ISCSI_STATUS_MISSING_FIELDS 0x07 #define ISCSI_STATUS_CONN_ADD_FAILED 0x08 #define ISCSI_STATUS_INV_SESSION_TYPE 0x09 #define ISCSI_STATUS_SESSION_NOT_FOUND 0x0a #define ISCSI_STATUS_INV_REQ_TYPE 0x0b /* Class-3 (Target Error) */ #define ISCSI_STATUS_TARGET_ERROR 0x00 #define ISCSI_STATUS_SVC_UNAVAILABLE 0x01 #define ISCSI_STATUS_NO_RESOURCES 0x02 struct iscsi_logout_req_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u16 cid; u16 rsvd3; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd4[4]; } __packed; struct iscsi_logout_rsp_hdr { u8 opcode; u8 flags; u8 response; u8 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 rsvd3; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 rsvd4; u16 time2wait; u16 time2retain; u32 rsvd5; } __packed; #define ISCSI_LOGOUT_SESSION 0 #define ISCSI_LOGOUT_CONNECTION 1 #define ISCSI_LOGOUT_CONNECTION_RECOVER 2 struct iscsi_snack_req_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 ttt; u32 rsvd3; u32 exp_stat_sn; u32 rsvd4[2]; u32 beg_run; u32 run_length; } __packed; struct iscsi_reject_hdr { u8 opcode; u8 flags; u8 reason; u8 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 ffffffff; u32 rsvd3; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 data_sn; u32 rsvd4[2]; } __packed; #define ISCSI_REASON_NO_FULL_FEATURE_PHASE 0x01 #define ISCSI_REASON_DATA_DIGEST_ERROR 0x02 #define ISCSI_REASON_DATA_SNACK_REJECT 0x03 #define ISCSI_REASON_PROTOCOL_ERROR 0x04 #define ISCSI_REASON_UNSUPPORTED_COMMAND 0x05 #define ISCSI_REASON_IMMEDIATE_COMMAND_REJECT 0x06 #define ISCSI_REASON_TASK_IN_PROGRESS 0x07 #define ISCSI_REASON_INVALID_SNACK 0x08 #define ISCSI_REASON_NO_BOOKMARK 0x09 #define ISCSI_REASON_BOOKMARK_REJECT 0x0a #define ISCSI_REASON_NEGOTIATION_RESET 0x0b #define ISCSI_REASON_WAITING_LOGOUT 0x0c struct iscsi_nop_out_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 itt; u32 ttt; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd2[4]; } __packed; struct iscsi_nop_in_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u16 lun[4]; u32 itt; u32 ttt; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 rsvd2[3]; } __packed; #define ISCSI_RESERVED_TAG (0xffffffffU) #endif /* __ISCSI_HDR_H__ */ iscsitarget-1.4.20.3+svn502/kernel/nthread.c000066400000000000000000000421461233224651300203720ustar00rootroot00000000000000/* * Network thread. * (C) 2004 - 2005 FUJITA Tomonori * (C) 2008 Arne Redlich * * This code is licenced under the GPL. */ #include #include #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "digest.h" enum daemon_state_bit { D_ACTIVE, D_DATA_READY, }; void __nthread_wakeup(struct network_thread_info *info) { set_bit(D_DATA_READY, &info->flags); wake_up_process(info->task); } void nthread_wakeup(struct iscsi_target *target) { struct network_thread_info *info = &target->nthread_info; spin_lock_bh(&info->nthread_lock); __nthread_wakeup(info); spin_unlock_bh(&info->nthread_lock); } static inline void iscsi_conn_init_read(struct iscsi_conn *conn, void *data, size_t len) { len = (len + 3) & -4; // XXX ??? conn->read_iov[0].iov_base = data; conn->read_iov[0].iov_len = len; conn->read_msg.msg_iov = conn->read_iov; conn->read_msg.msg_iovlen = 1; conn->read_size = (len + 3) & -4; } static void iscsi_conn_read_ahs(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) { cmnd->pdu.ahs = kmalloc(cmnd->pdu.ahssize, __GFP_NOFAIL|GFP_KERNEL); assert(cmnd->pdu.ahs); iscsi_conn_init_read(conn, cmnd->pdu.ahs, cmnd->pdu.ahssize); } static struct iscsi_cmnd * iscsi_get_send_cmnd(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd = NULL; spin_lock_bh(&conn->list_lock); if (!list_empty(&conn->write_list)) { cmnd = list_entry(conn->write_list.next, struct iscsi_cmnd, list); list_del_init(&cmnd->list); } spin_unlock_bh(&conn->list_lock); return cmnd; } static int is_data_available(struct iscsi_conn *conn) { int avail, res; mm_segment_t oldfs; struct socket *sock = conn->sock; oldfs = get_fs(); set_fs(get_ds()); res = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); set_fs(oldfs); return (res >= 0) ? avail : res; } static void forward_iov(struct msghdr *msg, int len) { while (msg->msg_iov->iov_len <= len) { len -= msg->msg_iov->iov_len; msg->msg_iov++; msg->msg_iovlen--; } msg->msg_iov->iov_base = (char *) msg->msg_iov->iov_base + len; msg->msg_iov->iov_len -= len; } static int do_recv(struct iscsi_conn *conn, int state) { mm_segment_t oldfs; struct msghdr msg; struct iovec iov[ISCSI_CONN_IOV_MAX]; int i, len, res; if (!test_bit(CONN_ACTIVE, &conn->state)) { res = -EIO; goto out; } if (is_data_available(conn) <= 0) { res = -EAGAIN; goto out; } msg.msg_iov = iov; msg.msg_iovlen = min_t(size_t, conn->read_msg.msg_iovlen, ISCSI_CONN_IOV_MAX); for (i = 0, len = 0; i < msg.msg_iovlen; i++) { iov[i] = conn->read_msg.msg_iov[i]; len += iov[i].iov_len; } oldfs = get_fs(); set_fs(get_ds()); res = sock_recvmsg(conn->sock, &msg, len, MSG_DONTWAIT | MSG_NOSIGNAL); set_fs(oldfs); if (res <= 0) { switch (res) { case -EAGAIN: case -ERESTARTSYS: break; default: eprintk("%d\n", res); conn_close(conn); break; } } else { conn->read_size -= res; if (conn->read_size) forward_iov(&conn->read_msg, res); else conn->read_state = state; } out: dprintk(D_IOD, "%d\n", res); return res; } enum rx_state { RX_INIT_BHS, /* Must be zero. */ RX_BHS, RX_INIT_AHS, RX_AHS, RX_INIT_HDIGEST, RX_HDIGEST, RX_CHECK_HDIGEST, RX_INIT_DATA, RX_DATA, RX_INIT_DDIGEST, RX_DDIGEST, RX_CHECK_DDIGEST, RX_END, }; static void rx_ddigest(struct iscsi_conn *conn, int state) { struct iscsi_cmnd *cmnd = conn->read_cmnd; int res = digest_rx_data(cmnd); if (!res) conn->read_state = state; else conn_close(conn); } static void rx_hdigest(struct iscsi_conn *conn, int state) { struct iscsi_cmnd *cmnd = conn->read_cmnd; int res = digest_rx_header(cmnd); if (!res) conn->read_state = state; else conn_close(conn); } static struct iscsi_cmnd *create_cmnd(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd; cmnd = cmnd_alloc(conn, 1); iscsi_conn_init_read(cmnd->conn, &cmnd->pdu.bhs, sizeof(cmnd->pdu.bhs)); conn->read_state = RX_BHS; return cmnd; } static int recv(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd = conn->read_cmnd; int hdigest, ddigest, res = 1; if (!test_bit(CONN_ACTIVE, &conn->state)) return -EIO; hdigest = conn->hdigest_type & DIGEST_NONE ? 0 : 1; ddigest = conn->ddigest_type & DIGEST_NONE ? 0 : 1; next_state: switch (conn->read_state) { case RX_INIT_BHS: assert(!cmnd); cmnd = conn->read_cmnd = create_cmnd(conn); case RX_BHS: res = do_recv(conn, RX_INIT_AHS); if (res <= 0 || conn->read_state != RX_INIT_AHS) break; case RX_INIT_AHS: iscsi_cmnd_get_length(&cmnd->pdu); if (cmnd->pdu.ahssize) { iscsi_conn_read_ahs(conn, cmnd); conn->read_state = RX_AHS; } else conn->read_state = hdigest ? RX_INIT_HDIGEST : RX_INIT_DATA; if (conn->read_state != RX_AHS) break; case RX_AHS: res = do_recv(conn, hdigest ? RX_INIT_HDIGEST : RX_INIT_DATA); if (res <= 0 || conn->read_state != RX_INIT_HDIGEST) break; case RX_INIT_HDIGEST: iscsi_conn_init_read(conn, &cmnd->hdigest, sizeof(u32)); conn->read_state = RX_HDIGEST; case RX_HDIGEST: res = do_recv(conn, RX_CHECK_HDIGEST); if (res <= 0 || conn->read_state != RX_CHECK_HDIGEST) break; case RX_CHECK_HDIGEST: rx_hdigest(conn, RX_INIT_DATA); if (conn->read_state != RX_INIT_DATA) return -EIO; case RX_INIT_DATA: cmnd_rx_start(cmnd); conn->read_state = cmnd->pdu.datasize ? RX_DATA : RX_END; if (conn->read_state != RX_DATA) break; case RX_DATA: res = do_recv(conn, ddigest ? RX_INIT_DDIGEST : RX_END); if (res <= 0 || conn->read_state != RX_INIT_DDIGEST) break; case RX_INIT_DDIGEST: iscsi_conn_init_read(conn, &cmnd->ddigest, sizeof(u32)); conn->read_state = RX_DDIGEST; case RX_DDIGEST: res = do_recv(conn, RX_CHECK_DDIGEST); if (res <= 0 || conn->read_state != RX_CHECK_DDIGEST) break; case RX_CHECK_DDIGEST: rx_ddigest(conn, RX_END); if (conn->read_state != RX_END) return -EIO; break; default: eprintk("%d %d %x\n", res, conn->read_state, cmnd_opcode(cmnd)); assert(0); } if (res <= 0) return res; if (conn->read_state != RX_END) goto next_state; if (conn->read_size) { eprintk("%d %x %d\n", res, cmnd_opcode(cmnd), conn->read_size); assert(0); } cmnd_rx_end(cmnd); if (conn->read_size) { eprintk("%x %d\n", cmnd_opcode(cmnd), conn->read_size); conn->read_state = RX_DATA; return 1; } conn->read_cmnd = NULL; conn->read_state = RX_INIT_BHS; return 0; } /* * @locking: grabs the target's nthread_lock to protect it from races with * iet_write_space() */ static void set_conn_wspace_wait(struct iscsi_conn *conn) { struct network_thread_info *info = &conn->session->target->nthread_info; struct sock *sk = conn->sock->sk; spin_lock_bh(&info->nthread_lock); if (sk_stream_wspace(sk) < sk_stream_min_wspace(sk)) set_bit(CONN_WSPACE_WAIT, &conn->state); spin_unlock_bh(&info->nthread_lock); } /* This is taken from the Ardis code. */ static int write_data(struct iscsi_conn *conn) { mm_segment_t oldfs; struct file *file; struct socket *sock; ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); struct tio *tio; struct iovec *iop; int saved_size, size, sendsize; int offset, idx; int flags, res; file = conn->file; saved_size = size = conn->write_size; iop = conn->write_iop; if (iop) while (1) { loff_t off = 0; unsigned long count; struct iovec *vec; int rest; vec = iop; for (count = 0; vec->iov_len; count++, vec++) ; oldfs = get_fs(); set_fs(KERNEL_DS); res = vfs_writev(file, (struct iovec __user *) iop, count, &off); set_fs(oldfs); dprintk(D_DATA, "%#Lx:%u: %d(%ld)\n", (unsigned long long) conn->session->sid, conn->cid, res, (long) iop->iov_len); if (unlikely(res <= 0)) { if (res == -EAGAIN || res == -EINTR) { conn->write_iop = iop; goto out_iov; } goto err; } rest = res; size -= res; while (iop->iov_len <= rest && rest) { rest -= iop->iov_len; iop++; } iop->iov_base += rest; iop->iov_len -= rest; if (!iop->iov_len) { conn->write_iop = NULL; if (size) break; goto out_iov; } } if (!(tio = conn->write_tcmnd)) { eprintk("%s\n", "warning data missing!"); return 0; } offset = conn->write_offset; idx = offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; sock = conn->sock; sendpage = sock->ops->sendpage ? : sock_no_sendpage; flags = MSG_DONTWAIT; while (1) { sendsize = PAGE_CACHE_SIZE - offset; if (size <= sendsize) { res = sendpage(sock, tio->pvec[idx], offset, size, flags); dprintk(D_DATA, "%s %#Lx:%u: %d(%lu,%u,%u)\n", sock->ops->sendpage ? "sendpage" : "writepage", (unsigned long long ) conn->session->sid, conn->cid, res, tio->pvec[idx]->index, offset, size); if (unlikely(res <= 0)) { if (res == -EAGAIN || res == -EINTR) { goto out; } goto err; } if (res == size) { conn->write_tcmnd = NULL; conn->write_size = 0; return saved_size; } offset += res; size -= res; continue; } res = sendpage(sock, tio->pvec[idx], offset,sendsize, flags | MSG_MORE); dprintk(D_DATA, "%s %#Lx:%u: %d(%lu,%u,%u)\n", sock->ops->sendpage ? "sendpage" : "writepage", (unsigned long long ) conn->session->sid, conn->cid, res, tio->pvec[idx]->index, offset, sendsize); if (unlikely(res <= 0)) { if (res == -EAGAIN || res == -EINTR) { goto out; } goto err; } if (res == sendsize) { idx++; offset = 0; } else offset += res; size -= res; } out: conn->write_offset = (idx << PAGE_CACHE_SHIFT) + offset; out_iov: conn->write_size = size; if (res == -EAGAIN) { set_conn_wspace_wait(conn); if (saved_size == size) return res; } return saved_size - size; err: eprintk("error %d at %#Lx:%u\n", res, (unsigned long long) conn->session->sid, conn->cid); return res; } static void exit_tx(struct iscsi_conn *conn, int res) { if (res > 0) return; switch (res) { case -EAGAIN: case -ERESTARTSYS: break; default: eprintk("%d %d %d\n", conn->write_size, conn->write_state, res); conn_close(conn); break; } } static int tx_ddigest(struct iscsi_cmnd *cmnd, int state) { int res, rest = cmnd->conn->write_size; struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; struct kvec iov; iov.iov_base = (char *) (&cmnd->ddigest) + (sizeof(u32) - rest); iov.iov_len = rest; res = kernel_sendmsg(cmnd->conn->sock, &msg, &iov, 1, rest); if (res > 0) { cmnd->conn->write_size -= res; if (!cmnd->conn->write_size) cmnd->conn->write_state = state; } else exit_tx(cmnd->conn, res); return res; } static void init_tx_hdigest(struct iscsi_cmnd *cmnd) { struct iscsi_conn *conn = cmnd->conn; struct iovec *iop; if (conn->hdigest_type & DIGEST_NONE) return; digest_tx_header(cmnd); for (iop = conn->write_iop; iop->iov_len; iop++) ; iop->iov_base = &(cmnd->hdigest); iop->iov_len = sizeof(u32); conn->write_size += sizeof(u32); iop++; iop->iov_len = 0; return; } enum tx_state { TX_INIT, /* Must be zero. */ TX_BHS_DATA, TX_INIT_DDIGEST, TX_DDIGEST, TX_END, }; static int do_send(struct iscsi_conn *conn, int state) { int res; res = write_data(conn); if (res > 0) { if (!conn->write_size) conn->write_state = state; } else exit_tx(conn, res); return res; } static int send(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd = conn->write_cmnd; int ddigest, res = 0; ddigest = conn->ddigest_type != DIGEST_NONE ? 1 : 0; next_state: switch (conn->write_state) { case TX_INIT: assert(!cmnd); cmnd = conn->write_cmnd = iscsi_get_send_cmnd(conn); if (!cmnd) return 0; cmnd_tx_start(cmnd); init_tx_hdigest(cmnd); conn->write_state = TX_BHS_DATA; case TX_BHS_DATA: res = do_send(conn, ddigest && cmnd->pdu.datasize ? TX_INIT_DDIGEST : TX_END); if (res <= 0 || conn->write_state != TX_INIT_DDIGEST) break; case TX_INIT_DDIGEST: digest_tx_data(cmnd); assert(!cmnd->conn->write_size); cmnd->conn->write_size += sizeof(u32); conn->write_state = TX_DDIGEST; case TX_DDIGEST: res = tx_ddigest(cmnd, TX_END); break; default: eprintk("%d %d %x\n", res, conn->write_state, cmnd_opcode(cmnd)); assert(0); } if (res <= 0) return res; if (conn->write_state != TX_END) goto next_state; if (conn->write_size) { eprintk("%d %x %u\n", res, cmnd_opcode(cmnd), conn->write_size); assert(!conn->write_size); } cmnd_tx_end(cmnd); cmnd_release(cmnd, 0); conn->write_cmnd = NULL; conn->write_state = TX_INIT; return 0; } static void conn_nop_timeout(unsigned long data) { struct iscsi_conn *conn = (struct iscsi_conn *)data; if (test_bit(CONN_ACTIVE, &conn->state)) set_bit(CONN_NEED_NOP_IN, &conn->state); dprintk(D_THREAD, "conn %llu:%hu, NOP timer %p\n", conn->session->sid, conn->cid, &conn->nop_timer); nthread_wakeup(conn->session->target); } static void conn_reset_nop_timer(struct iscsi_conn *conn) { struct iscsi_target *target = conn->session->target; if (target->trgt_param.nop_interval) mod_timer(&conn->nop_timer, jiffies + HZ * target->trgt_param.nop_interval); } static void conn_start_nop_timer(struct iscsi_conn *conn) { struct iscsi_target *target = conn->session->target; if (!target->trgt_param.nop_interval || timer_pending(&conn->nop_timer)) return; conn->nop_timer.data = (unsigned long)conn; conn->nop_timer.function = conn_nop_timeout; dprintk(D_THREAD, "conn %llu:%hu, NOP timer %p\n", conn->session->sid, conn->cid, &conn->nop_timer); mod_timer(&conn->nop_timer, jiffies + HZ * target->trgt_param.nop_interval); } static void process_io(struct iscsi_conn *conn) { struct iscsi_target *target = conn->session->target; int res, wakeup = 0; res = recv(conn); if (is_data_available(conn) > 0 || res > 0) { conn_reset_nop_timer(conn); wakeup = 1; } if (!test_bit(CONN_ACTIVE, &conn->state)) { wakeup = 1; goto out; } if (test_bit(CONN_WSPACE_WAIT, &conn->state)) goto out; res = send(conn); if (!list_empty(&conn->write_list) || conn->write_cmnd) { conn_reset_nop_timer(conn); wakeup = 1; } out: if (wakeup) nthread_wakeup(target); else if (test_and_clear_bit(CONN_NEED_NOP_IN, &conn->state)) { send_nop_in(conn); nthread_wakeup(target); } else conn_start_nop_timer(conn); return; } static void close_conn(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; struct iscsi_target *target = conn->session->target; struct iscsi_cmnd *cmnd; if (target->trgt_param.nop_interval) del_timer_sync(&conn->nop_timer); conn->sock->ops->shutdown(conn->sock, 2); write_lock_bh(&conn->sock->sk->sk_callback_lock); conn->sock->sk->sk_state_change = target->nthread_info.old_state_change; conn->sock->sk->sk_data_ready = target->nthread_info.old_data_ready; conn->sock->sk->sk_write_space = target->nthread_info.old_write_space; write_unlock_bh(&conn->sock->sk->sk_callback_lock); fput(conn->file); conn->file = NULL; conn->sock = NULL; while (atomic_read(&conn->nr_busy_cmnds)) yield(); while (!list_empty(&conn->pdu_list)) { cmnd = list_entry(conn->pdu_list.next, struct iscsi_cmnd, conn_list); list_del_init(&cmnd->list); cmnd_release(cmnd, 1); } if (atomic_read(&conn->nr_cmnds)) { eprintk("%u\n", atomic_read(&conn->nr_cmnds)); list_for_each_entry(cmnd, &conn->pdu_list, conn_list) eprintk("%x %x\n", cmnd_opcode(cmnd), cmnd_itt(cmnd)); assert(0); } event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0); conn_free(conn); if (list_empty(&session->conn_list)) { if (session->done) complete(session->done); else session_del(target, session->sid); } } static int istd(void *arg) { struct iscsi_target *target = arg; struct network_thread_info *info = &target->nthread_info; struct iscsi_conn *conn, *tmp; __set_current_state(TASK_RUNNING); do { spin_lock_bh(&info->nthread_lock); __set_current_state(TASK_INTERRUPTIBLE); if (!test_bit(D_DATA_READY, &info->flags)) { spin_unlock_bh(&info->nthread_lock); schedule(); spin_lock_bh(&info->nthread_lock); } __set_current_state(TASK_RUNNING); clear_bit(D_DATA_READY, &info->flags); spin_unlock_bh(&info->nthread_lock); target_lock(target, 0); list_for_each_entry_safe(conn, tmp, &info->active_conns, poll_list) { if (test_bit(CONN_ACTIVE, &conn->state)) process_io(conn); else close_conn(conn); } target_unlock(target); } while (!kthread_should_stop()); return 0; } int nthread_init(struct iscsi_target *target) { struct network_thread_info *info = &target->nthread_info; info->flags = 0; info->task = NULL; info->old_state_change = NULL; info->old_data_ready = NULL; info->old_write_space = NULL; INIT_LIST_HEAD(&info->active_conns); spin_lock_init(&info->nthread_lock); return 0; } int nthread_start(struct iscsi_target *target) { int err = 0; struct network_thread_info *info = &target->nthread_info; struct task_struct *task; if (info->task) { eprintk("Target (%u) already runs\n", target->tid); return -EALREADY; } task = kthread_run(istd, target, "istd%d", target->tid); if (IS_ERR(task)) err = PTR_ERR(task); else info->task = task; return err; } int nthread_stop(struct iscsi_target *target) { int err; struct network_thread_info *info = &target->nthread_info; if (!info->task) return -ESRCH; err = kthread_stop(info->task); if (err < 0 && err != -EINTR) return err; info->task = NULL; return 0; } iscsitarget-1.4.20.3+svn502/kernel/null-io.c000066400000000000000000000036621233224651300203240ustar00rootroot00000000000000/* * Target device null I/O. * (C) 2005 MING Zhang * This code is licenced under the GPL. * * The nullio mode will not return any meaningful or previous written * data. It is only for performance measurement purpose. */ #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" enum { opt_blk_cnt, opt_ignore, opt_err, }; static match_table_t tokens = { /* alias for compatibility with existing setups and documentation */ {opt_blk_cnt, "sectors=%u"}, /* but actually it is the scsi block count, now that we can * specify the block size. */ {opt_blk_cnt, "blocks=%u"}, {opt_ignore, "scsiid=%s"}, {opt_ignore, "scsisn=%s"}, {opt_ignore, "blocksize=%s"}, {opt_ignore, "type=%s"}, {opt_err, NULL}, }; static int parse_nullio_params(struct iet_volume *volume, char *params) { int err = 0; char *p, *q; while ((p = strsep(¶ms, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case opt_blk_cnt: q = match_strdup(&args[0]); if (!q) return -ENOMEM; volume->blk_cnt = simple_strtoull(q, NULL, 10); kfree(q); break; case opt_ignore: break; default: eprintk("Unknown %s\n", p); return -EINVAL; break; } } return err; } static void nullio_detach(struct iet_volume *lu) { } static int nullio_attach(struct iet_volume *lu, char *args) { int err = 0; if ((err = parse_nullio_params(lu, args)) < 0) { eprintk("%d\n", err); goto out; } if (!lu->blk_shift) lu->blk_shift = blksize_bits(IET_DEF_BLOCK_SIZE); /* defaults to 64 GiB */ if (!lu->blk_cnt) lu->blk_cnt = 1 << (36 - lu->blk_shift); out: if (err < 0) nullio_detach(lu); return err; } struct iotype nullio = { .name = "nullio", .attach = nullio_attach, .detach = nullio_detach, }; iscsitarget-1.4.20.3+svn502/kernel/param.c000066400000000000000000000144041233224651300200410ustar00rootroot00000000000000/* * (C) 2005 FUJITA Tomonori * * This code is licenced under the GPL. */ #include "iscsi.h" #include "iscsi_dbg.h" #include "digest.h" struct target_type *target_type_array[] = { &disk_ops, }; #define CHECK_PARAM(info, iparam, word, min, max) \ do { \ if (!info->partial || (info->partial & 1 << key_##word)) \ if (iparam[key_##word] < min || \ iparam[key_##word] > max) { \ eprintk("%s: %u is out of range (%u %u)\n", \ #word, iparam[key_##word], min, max); \ iparam[key_##word] = min; \ } \ } while (0) #define SET_PARAM(param, info, iparam, word) \ ({ \ int changed = 0; \ if (!info->partial || (info->partial & 1 << key_##word)) { \ if (param->word != iparam[key_##word]) \ changed = 1; \ param->word = iparam[key_##word]; \ } \ changed; \ }) #define GET_PARAM(param, info, iparam, word) \ do { \ iparam[key_##word] = param->word; \ } while (0) static void sess_param_check(struct iscsi_param_info *info) { u32 *iparam = info->session_param; CHECK_PARAM(info, iparam, max_connections, 1, 65535); CHECK_PARAM(info, iparam, max_recv_data_length, 512, (u32) ((ISCSI_CONN_IOV_MAX - 1) * PAGE_CACHE_SIZE)); CHECK_PARAM(info, iparam, max_xmit_data_length, 512, (u32) ((ISCSI_CONN_IOV_MAX - 1) * PAGE_CACHE_SIZE)); CHECK_PARAM(info, iparam, error_recovery_level, 0, 0); CHECK_PARAM(info, iparam, data_pdu_inorder, 1, 1); CHECK_PARAM(info, iparam, data_sequence_inorder, 1, 1); CHECK_PARAM(info, iparam, default_retain_time, 0, 0); digest_alg_available(&iparam[key_header_digest]); digest_alg_available(&iparam[key_data_digest]); CHECK_PARAM(info, iparam, ofmarker, 0, 0); CHECK_PARAM(info, iparam, ifmarker, 0, 0); } static void sess_param_set(struct iscsi_sess_param *param, struct iscsi_param_info *info) { u32 *iparam = info->session_param; SET_PARAM(param, info, iparam, initial_r2t); SET_PARAM(param, info, iparam, immediate_data); SET_PARAM(param, info, iparam, max_connections); SET_PARAM(param, info, iparam, max_recv_data_length); SET_PARAM(param, info, iparam, max_xmit_data_length); SET_PARAM(param, info, iparam, max_burst_length); SET_PARAM(param, info, iparam, first_burst_length); SET_PARAM(param, info, iparam, default_wait_time); SET_PARAM(param, info, iparam, default_retain_time); SET_PARAM(param, info, iparam, max_outstanding_r2t); SET_PARAM(param, info, iparam, data_pdu_inorder); SET_PARAM(param, info, iparam, data_sequence_inorder); SET_PARAM(param, info, iparam, error_recovery_level); SET_PARAM(param, info, iparam, header_digest); SET_PARAM(param, info, iparam, data_digest); SET_PARAM(param, info, iparam, ofmarker); SET_PARAM(param, info, iparam, ifmarker); SET_PARAM(param, info, iparam, ofmarkint); SET_PARAM(param, info, iparam, ifmarkint); } static void sess_param_get(struct iscsi_sess_param *param, struct iscsi_param_info *info) { u32 *iparam = info->session_param; GET_PARAM(param, info, iparam, initial_r2t); GET_PARAM(param, info, iparam, immediate_data); GET_PARAM(param, info, iparam, max_connections); GET_PARAM(param, info, iparam, max_recv_data_length); GET_PARAM(param, info, iparam, max_xmit_data_length); GET_PARAM(param, info, iparam, max_burst_length); GET_PARAM(param, info, iparam, first_burst_length); GET_PARAM(param, info, iparam, default_wait_time); GET_PARAM(param, info, iparam, default_retain_time); GET_PARAM(param, info, iparam, max_outstanding_r2t); GET_PARAM(param, info, iparam, data_pdu_inorder); GET_PARAM(param, info, iparam, data_sequence_inorder); GET_PARAM(param, info, iparam, error_recovery_level); GET_PARAM(param, info, iparam, header_digest); GET_PARAM(param, info, iparam, data_digest); GET_PARAM(param, info, iparam, ofmarker); GET_PARAM(param, info, iparam, ifmarker); GET_PARAM(param, info, iparam, ofmarkint); GET_PARAM(param, info, iparam, ifmarkint); } static void trgt_param_check(struct iscsi_param_info *info) { u32 *iparam = info->target_param; CHECK_PARAM(info, iparam, wthreads, MIN_NR_WTHREADS, MAX_NR_WTHREADS); CHECK_PARAM(info, iparam, target_type, 0, (unsigned int) ARRAY_SIZE(target_type_array) - 1); CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS, MAX_NR_QUEUED_CMNDS); CHECK_PARAM(info, iparam, nop_interval, MIN_NOP_INTERVAL, MAX_NOP_INTERVAL); CHECK_PARAM(info, iparam, nop_timeout, MIN_NOP_TIMEOUT, MAX_NOP_TIMEOUT); } static void trgt_param_set(struct iscsi_target *target, struct iscsi_param_info *info) { struct iscsi_trgt_param *param = &target->trgt_param; u32 *iparam = info->target_param; if (!worker_thread_pool && SET_PARAM(param, info, iparam, wthreads)) wthread_start(target->wthread_info, target->trgt_param.wthreads, target->tid); SET_PARAM(param, info, iparam, target_type); SET_PARAM(param, info, iparam, queued_cmnds); SET_PARAM(param, info, iparam, nop_interval); SET_PARAM(param, info, iparam, nop_timeout); } static void trgt_param_get(struct iscsi_trgt_param *param, struct iscsi_param_info *info) { u32 *iparam = info->target_param; GET_PARAM(param, info, iparam, wthreads); GET_PARAM(param, info, iparam, target_type); GET_PARAM(param, info, iparam, queued_cmnds); GET_PARAM(param, info, iparam, nop_interval); GET_PARAM(param, info, iparam, nop_timeout); } static int trgt_param(struct iscsi_target *target, struct iscsi_param_info *info, int set) { if (set) { trgt_param_check(info); trgt_param_set(target, info); } else trgt_param_get(&target->trgt_param, info); return 0; } static int sess_param(struct iscsi_target *target, struct iscsi_param_info *info, int set) { struct iscsi_session *session = NULL; struct iscsi_sess_param *param; int err = -ENOENT; if (set) sess_param_check(info); if (info->sid) { if (!(session = session_lookup(target, info->sid))) goto out; param = &session->param; } else { param = &target->sess_param; } if (set) { sess_param_set(param, info); show_param(param); } else sess_param_get(param, info); err = 0; out: return err; } int iscsi_param_set(struct iscsi_target *target, struct iscsi_param_info *info, int set) { int err; if (info->param_type == key_session) err = sess_param(target, info, set); else if (info->param_type == key_target) err = trgt_param(target, info, set); else err = -EINVAL; return err; } iscsitarget-1.4.20.3+svn502/kernel/persist.c000066400000000000000000000522161233224651300204350ustar00rootroot00000000000000/* * Copyright (C) 2011 Shivaram U, shivaram.u@quadstor.com * * Released under the terms of the GNU GPL v2.0. */ #include #include "iscsi.h" #include "iscsi_dbg.h" #include "persist.h" static bool pr_is_reserved_by_sid(const struct reservation *res, const u64 sid) { if (!pr_is_reserved(res)) return false; if (pr_type_is_all_registrants(res)) { return pr_initiator_has_registered(res, sid); } else { return res->sid == sid; } } bool pr_is_reserved_by_session(const struct reservation *res, const struct iscsi_session *sess) { return pr_is_reserved_by_sid(res, sess->sid); } bool pr_initiator_has_registered(const struct reservation *res, u64 sid) { struct registration *reg; list_for_each_entry(reg, &res->registration_list, r_list) { if (reg->sid == sid) return true; } return false; } static const struct pr_in_report_capabilities_data pr_capabilities = { .length = cpu_to_be16(8), .crh_sip_atp_ptpl_c = 0, .tmv_ptpl_a = PR_IN_REPORT_CAP_TMV, .type_mask = cpu_to_be16(PR_TYPE_WR_EX| PR_TYPE_EX_AC| PR_TYPE_WR_EX_RO| PR_TYPE_EX_AC_RO| PR_TYPE_WR_EX_AR| PR_TYPE_EX_AC_AR), }; static void pr_in_report_capabilities(struct iscsi_cmnd *cmnd, u16 allocation_length) { u8 *data = page_address(cmnd->tio->pvec[0]); const u32 min_len = min_t(u16, allocation_length, sizeof(pr_capabilities)); BUG_ON(!data); memcpy(data, &pr_capabilities, min_len); tio_set(cmnd->tio, min_len, 0); dprintk_pr(cmnd, "ret len %u\n", min_len); } static void pr_in_read_reservation(struct iscsi_cmnd *cmnd, u16 allocation_length) { struct iet_volume *volume = cmnd->lun; const struct reservation *reservation = &volume->reservation; struct pr_in_read_reservation_data *pin_data = (struct pr_in_read_reservation_data *)page_address(cmnd->tio->pvec[0]); u32 size; BUG_ON(!pin_data); memset(pin_data, 0x0, sizeof(*pin_data)); spin_lock(&volume->reserve_lock); pin_data->generation = cpu_to_be32(reservation->generation); if (pr_is_reserved(reservation)) { if (pr_type_is_all_registrants(reservation)) pin_data->reservation_key = 0; else pin_data->reservation_key = reservation->reservation_key; pin_data->scope_type = PR_SCOPE_LU | reservation->persistent_type; /* * SPC-3, 6.11.3.2 * "The ADDITIONAL LENGTH field contains a count of the number of * bytes to follow and shall be set to 16" */ pin_data->additional_length = cpu_to_be32(16); size = sizeof(*pin_data); dprintk_pr(cmnd, "key %#Lx, size %u\n", pin_data->reservation_key, size); } else { size = 8; /* generation + additional length */ dprintk_pr(cmnd, "size %u\n", size); } spin_unlock(&volume->reserve_lock); tio_set(cmnd->tio, min_t(u32, size, allocation_length), 0); } static void pr_in_read_full_status(struct iscsi_cmnd *cmnd, u16 allocation_length) { struct iet_volume *volume = cmnd->lun; const struct reservation *reservation = &volume->reservation; struct tio_iterator tio_it; struct pr_in_read_full_status_data *pfull = (struct pr_in_read_full_status_data *)page_address(cmnd->tio->pvec[0]); struct registration *reg; u16 left = (allocation_length > sizeof(*pfull)) ? allocation_length - sizeof(*pfull) : 0; u32 addl_data_len = 0; tio_init_iterator(cmnd->tio, &tio_it); tio_it.pg_off += sizeof(*pfull); spin_lock(&volume->reserve_lock); pfull->generation = cpu_to_be32(reservation->generation); list_for_each_entry(reg, &reservation->registration_list, r_list) { const size_t init_name_len = PAD_TO_4_BYTES(strlen(reg->init_name)); const struct iscsi_transport_id tid = { .fmt_code_proto_id = TRANSPORT_ID_FMT_CODE_ISCSI|TRANSPORT_ID_PROTO_ID_ISCSI, .additional_length = cpu_to_be16(init_name_len), }; const struct pr_in_full_status_descriptor desc = { .reservation_key = reg->reservation_key, .all_tg_pt_r_holder = pr_is_reserved_by_sid(reservation, reg->sid), .scope_type = PR_SCOPE_LU|reservation->persistent_type, /* only rel_tgt_port_id 1 is supported */ .rel_tgt_port_id = cpu_to_be16(1), .additional_desc_length = cpu_to_be32(sizeof(tid) + init_name_len), }; left -= tio_add_data(&tio_it, (const u8 *)&desc, min_t(u16, left, sizeof(desc))); left -= tio_add_data(&tio_it, (const u8 *)&tid, min_t(u16, left, sizeof(tid))); left -= tio_add_data(&tio_it, (const u8 *)reg->init_name, min_t(u16, left, init_name_len)); addl_data_len += sizeof(desc) + sizeof(tid) + init_name_len; dprintk_pr(cmnd, "init name %s, sess %#Lx, key %#Lx, rtype %d, scope_type %x, all_tg_pt_r_holder %x, desc.addlen %u, tid.addlen %u, addlen %u, left %u\n", reg->init_name, reg->sid, reg->reservation_key, reservation->reservation_type, desc.scope_type, desc.all_tg_pt_r_holder, be32_to_cpu(desc.additional_desc_length), be16_to_cpu(tid.additional_length), addl_data_len, left); } spin_unlock(&volume->reserve_lock); dprintk_pr(cmnd, "dlen %u, tlen %u\n", addl_data_len, allocation_length - left); pfull->additional_length = cpu_to_be32(addl_data_len); tio_set(cmnd->tio, allocation_length - left, 0); } static void pr_in_read_keys(struct iscsi_cmnd *cmnd, u16 allocation_length) { struct iet_volume *volume = cmnd->lun; const struct reservation *reservation = &volume->reservation; struct tio_iterator tio_it; struct pr_in_read_keys_data *kdata = (struct pr_in_read_keys_data *)page_address(cmnd->tio->pvec[0]); struct registration *reg; u16 left = (allocation_length >= sizeof(*kdata)) ? allocation_length - sizeof(*kdata) : 0; u32 addl_data_len = 0; tio_init_iterator(cmnd->tio, &tio_it); tio_it.pg_off += sizeof(*kdata); spin_lock(&volume->reserve_lock); kdata->generation = cpu_to_be32(reservation->generation); list_for_each_entry(reg, &reservation->registration_list, r_list) { left -= tio_add_data(&tio_it, (const u8 *)®->reservation_key, min_t(u16, left, sizeof(reg->reservation_key))); addl_data_len += sizeof(reg->reservation_key); dprintk_pr(cmnd, "found reg, init name %s, sess %#Lx, key %#Lx, kdata len %u, left %u\n", reg->init_name, reg->sid, reg->reservation_key, addl_data_len, left); } spin_unlock(&volume->reserve_lock); dprintk_pr(cmnd, "dlen %u, tlen %u\n", addl_data_len, allocation_length - left); kdata->additional_length = cpu_to_be32(addl_data_len); tio_set(cmnd->tio, allocation_length - left, 0); dprintk_pr(cmnd, "keys[0]: %#Lx\n", kdata->keys[0]); } void build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd) { const struct persistent_reserve_in *pr_in = (const struct persistent_reserve_in *)(cmnd_hdr(cmnd)->scb); const u16 allocation_length = be16_to_cpu(pr_in->allocation_length); const enum pr_in_service_actions action = pr_in->service_action & PR_SERVICE_ACTION_MASK; dprintk_pr(cmnd, "svc action %x, alloc len %u\n", action, allocation_length); switch (action) { case SERVICE_ACTION_READ_KEYS: case SERVICE_ACTION_READ_RESERVATION: case SERVICE_ACTION_REPORT_CAPABILITIES: case SERVICE_ACTION_READ_FULL_STATUS: if (allocation_length == 0) return; cmnd->tio = tio_alloc(get_pgcnt(allocation_length)); break; default: eprintk("%#Lx:%hu: invalid PR In Service Action %x\n", cmnd->conn->session->sid, cmnd->conn->cid, action); iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, INVALID_FIELD_IN_CDB_ASCQ); return; } switch (action) { case SERVICE_ACTION_READ_KEYS: pr_in_read_keys(cmnd, allocation_length); break; case SERVICE_ACTION_READ_RESERVATION: pr_in_read_reservation(cmnd, allocation_length); break; case SERVICE_ACTION_REPORT_CAPABILITIES: pr_in_report_capabilities(cmnd, allocation_length); break; case SERVICE_ACTION_READ_FULL_STATUS: pr_in_read_full_status(cmnd, allocation_length); break; } } static void pr_out_register(struct iscsi_cmnd *cmnd, bool ignore) { const struct pr_out_param_list *param = (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; struct registration *reg; struct registration *new_reg = kzalloc(sizeof(*new_reg), GFP_KERNEL); dprintk_pr(cmnd, "rkey %#Lx, skey %#Lx, spec_i_pt_all_tg_pt_aptl %x ignore %d\n", param->reservation_key, param->service_action_key, param->spec_i_pt_all_tg_pt_aptl, ignore); if (!new_reg) { eprintk("%#Lx:%hu: failed to alloc new registration\n", cmnd->conn->session->sid, cmnd->conn->cid); iscsi_cmnd_set_sense(cmnd, /* TODO: verify sense key / asc / ascq */ ILLEGAL_REQUEST, INSUFFICIENT_REGISTRATION_RESOURCES_ASC, INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ); return; } spin_lock(&volume->reserve_lock); list_for_each_entry(reg, &reservation->registration_list, r_list) { dprintk_pr(cmnd, "found reg, init name %s, sess %#Lx, key %#Lx\n", reg->init_name, reg->sid, reg->reservation_key); if (reg->sid != session->sid) continue; if (!ignore && param->reservation_key != reg->reservation_key) { /* * SPC4r33: Table 49: when REGISTER is received on a * registered I_T nexus with a reservation key not * equal to the existing reservation key, we should * return RESERVATION CONFLICT */ cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if ((param->spec_i_pt_all_tg_pt_aptl & PR_OUT_PARAM_SPEC_I_PT)) { iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, INVALID_FIELD_IN_CDB_ASCQ); goto out; } if (!param->service_action_key) { if (pr_is_reserved_by_session(reservation, session) && !pr_type_is_all_registrants(reservation)) { reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->persistent_type = 0; reservation->reservation_key = 0; ua_establish_for_other_sessions(session, volume->lun, RESERVATIONS_RELEASED_ASC, RESERVATIONS_RELEASED_ASCQ); } list_del(®->r_list); kfree(reg); if (list_empty(&reservation->registration_list) && pr_type_is_all_registrants(reservation)) { reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->persistent_type = 0; reservation->reservation_key = 0; } } else { reg->reservation_key = param->service_action_key; } reservation->generation++; goto out; } if (!param->reservation_key && !param->service_action_key) { reservation->generation++; goto out; } if (param->reservation_key) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } new_reg->sid = session->sid; new_reg->reservation_key = param->service_action_key; strncpy(new_reg->init_name, cmnd->conn->session->initiator, sizeof(new_reg->init_name)); INIT_LIST_HEAD(&new_reg->r_list); list_add_tail(&new_reg->r_list, &reservation->registration_list); reservation->generation++; spin_unlock(&volume->reserve_lock); dprintk_pr(cmnd, "init_name %s, key %#Lx, generation %u\n", new_reg->init_name, new_reg->reservation_key, reservation->generation); return; out: kfree(new_reg); spin_unlock(&volume->reserve_lock); } static bool persistent_type_valid(int type) { switch (type) { case PR_TYPE_WRITE_EXCLUSIVE: case PR_TYPE_EXCLUSIVE_ACCESS: case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: return true; default: return false; } } static void pr_out_reserve(struct iscsi_cmnd *cmnd, enum persistent_reservation_type type) { const struct pr_out_param_list *param = (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); bool registered; struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; spin_lock(&volume->reserve_lock); registered = pr_initiator_has_registered(reservation, session->sid); if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if (pr_is_reserved(reservation) && reservation->sid != session->sid) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if (pr_is_reserved(reservation) && reservation->reservation_key != param->reservation_key) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if (pr_is_reserved(reservation) && reservation->persistent_type != type) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if (pr_is_reserved(reservation)) goto out; if (!persistent_type_valid(type)) { iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, INVALID_FIELD_IN_CDB_ASCQ); goto out; } reservation->reservation_type = RESERVATION_TYPE_PERSISTENT; reservation->persistent_type = type; reservation->reservation_key = param->reservation_key; reservation->sid = session->sid; dprintk_pr(cmnd, "key %#Lx, sess %#Lx, generation %u, rtype %d, ptype %d\n", reservation->reservation_key, reservation->sid, reservation->generation, reservation->reservation_type, reservation->persistent_type); out: spin_unlock(&volume->reserve_lock); } static void pr_out_release(struct iscsi_cmnd *cmnd, enum persistent_reservation_type type) { const struct pr_out_param_list *param = (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); bool registered; struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; bool send_ua; spin_lock(&volume->reserve_lock); if (!pr_is_reserved(reservation)) goto out; registered = pr_initiator_has_registered(reservation, session->sid); if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if (!pr_type_is_all_registrants(reservation)) { if (reservation->sid != session->sid) goto out; if (reservation->reservation_key != param->reservation_key) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } } switch (reservation->persistent_type) { case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: send_ua = true; break; default: send_ua = false; } dprintk_pr(cmnd, "key %#Lx, sess %#Lx, generation %u, rtype %d, ptype %d, ua %d\n", reservation->reservation_key, reservation->sid, reservation->generation, reservation->reservation_type, reservation->persistent_type, send_ua); reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->persistent_type = PR_TYPE_NONE; reservation->reservation_key = 0; if (send_ua) ua_establish_for_other_sessions(session, volume->lun, RESERVATIONS_RELEASED_ASC, RESERVATIONS_RELEASED_ASCQ); out: spin_unlock(&volume->reserve_lock); } static void pr_out_clear(struct iscsi_cmnd *cmnd) { bool registered; struct iscsi_session *tmp_session, *session = cmnd->conn->session; struct iscsi_target *target = session->target; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; struct registration *reg, *tmp_reg; spin_lock(&volume->reserve_lock); registered = pr_initiator_has_registered(reservation, session->sid); if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } list_for_each_entry_safe(reg, tmp_reg, &reservation->registration_list, r_list) { if (reg->sid != session->sid) { tmp_session = session_lookup(target, reg->sid); if (tmp_session) ua_establish_for_session(session, volume->lun, RESERVATIONS_PREEMPTED_ASC, RESERVATIONS_PREEMPTED_ASCQ); } list_del(®->r_list); kfree(reg); } dprintk_pr(cmnd, "key %#Lx, sess %#Lx, generation %u, rtype %d, ptype %d\n", reservation->reservation_key, reservation->sid, reservation->generation, reservation->reservation_type, reservation->persistent_type); reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->persistent_type = PR_TYPE_NONE; reservation->reservation_key = 0; reservation->generation++; out: spin_unlock(&volume->reserve_lock); } static void pr_out_preempt(struct iscsi_cmnd *cmnd, enum persistent_reservation_type pr_type, bool abort) { const struct pr_out_param_list *param = (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); struct registration *reg, *tmp_reg; bool registered; struct iscsi_session *session = cmnd->conn->session; struct iscsi_session *reserv_session; struct iscsi_target *target = session->target; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; bool all = 0; spin_lock(&volume->reserve_lock); if (!param->service_action_key && !pr_type_is_all_registrants(reservation)) { iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAMETER_LIST_ASC, INVALID_FIELD_IN_PARAMETER_LIST_ASCQ); goto out; } registered = pr_initiator_has_registered(reservation, session->sid); if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } if (pr_is_reserved(reservation)) { if ((!pr_type_is_all_registrants(reservation) && reservation->reservation_key == param->service_action_key && reservation->sid != session->sid) || (pr_type_is_all_registrants(reservation) && !param->service_action_key)) { reserv_session = session_lookup(target, reservation->sid); if (reserv_session) { if (abort) session_abort_tasks(reserv_session, volume->lun); ua_establish_for_session(reserv_session, volume->lun, RESERVATIONS_PREEMPTED_ASC, RESERVATIONS_PREEMPTED_ASCQ); } reservation->reservation_type = RESERVATION_TYPE_PERSISTENT; reservation->sid = session->sid; reservation->reservation_key = param->reservation_key; reservation->persistent_type = pr_type; all = true; } } list_for_each_entry_safe(reg, tmp_reg, &reservation->registration_list, r_list) { if (reg->sid == session->sid) continue; if (!all && reg->reservation_key != param->service_action_key && !pr_type_is_all_registrants(reservation)) continue; reserv_session = session_lookup(target, reg->sid); if (reserv_session) ua_establish_for_session(reserv_session, volume->lun, REGISTRATIONS_PREEMPTED_ASC, REGISTRATIONS_PREEMPTED_ASCQ); list_del(®->r_list); kfree(reg); } reservation->generation++; out: spin_unlock(&volume->reserve_lock); } void build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd) { const struct persistent_reserve_out *pr_out = (const struct persistent_reserve_out *)(cmnd_hdr(cmnd)->scb); const enum pr_out_service_actions action = pr_out->service_action & PR_SERVICE_ACTION_MASK; const u32 param_list_length = be32_to_cpu(pr_out->parameter_list_length); dprintk_pr(cmnd, "svc action %x, scope_type %x, param len %u\n", action, pr_out->scope_type, param_list_length); switch (action) { case SERVICE_ACTION_REGISTER: case SERVICE_ACTION_REGISTER_IGNORE: case SERVICE_ACTION_RESERVE: case SERVICE_ACTION_RELEASE: case SERVICE_ACTION_CLEAR: case SERVICE_ACTION_PREEMPT: case SERVICE_ACTION_PREEMPT_ABORT: break; case SERVICE_ACTION_REGISTER_MOVE: /* not implemented (yet) */ default: eprintk("%#Lx:%hu: invalid PR Out Service Action %x\n", cmnd->conn->session->sid, cmnd->conn->cid, action); iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, INVALID_FIELD_IN_CDB_ASCQ); return; } if ((pr_out->scope_type & PR_SCOPE_MASK) != PR_SCOPE_LU) { eprintk("%#Lx:%hu: invalid PR scope %x\n", cmnd->conn->session->sid, cmnd->conn->cid, pr_out->scope_type & PR_SCOPE_MASK); iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, INVALID_FIELD_IN_CDB_ASCQ); return; } if (param_list_length < sizeof(struct pr_out_param_list)) { eprintk("%#Lx:%hu: invalid PR Out parameter list length %d\n", cmnd->conn->session->sid, cmnd->conn->cid, param_list_length); iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, PARAMETER_LIST_LENGTH_ERROR_ASC, PARAMETER_LIST_LENGTH_ERROR_ASCQ); return; } switch (action) { case SERVICE_ACTION_REGISTER: pr_out_register(cmnd, false); break; case SERVICE_ACTION_REGISTER_IGNORE: pr_out_register(cmnd, true); break; case SERVICE_ACTION_RESERVE: pr_out_reserve(cmnd, pr_out->scope_type & PR_TYPE_MASK); break; case SERVICE_ACTION_RELEASE: pr_out_release(cmnd, pr_out->scope_type & PR_TYPE_MASK); break; case SERVICE_ACTION_CLEAR: pr_out_clear(cmnd); break; case SERVICE_ACTION_PREEMPT: pr_out_preempt(cmnd, pr_out->scope_type & PR_TYPE_MASK, false); break; case SERVICE_ACTION_PREEMPT_ABORT: pr_out_preempt(cmnd, pr_out->scope_type & PR_TYPE_MASK, true); break; case SERVICE_ACTION_REGISTER_MOVE: /* not implemented (yet) */ default: /* not reachable due to the earlier switch stmt */ BUG(); } } iscsitarget-1.4.20.3+svn502/kernel/persist.h000066400000000000000000000140321233224651300204340ustar00rootroot00000000000000/* * Copyright (C) 2011 Shivaram U, shivaram.u@quadstor.com * * Released under the terms of the GNU GPL v2.0. */ #ifndef IET_PERSIST_H_ #define IET_PERSIST_H_ struct registration { u64 sid; __be64 reservation_key; char init_name[ISCSI_NAME_LEN]; struct list_head r_list; }; #define PARAMETER_LIST_LENGTH_ERROR_ASC 0x1A #define PARAMETER_LIST_LENGTH_ERROR_ASCQ 0x00 #define INVALID_COMMAND_OPERATION_CODE_ASC 0x20 #define INVALID_COMMAND_OPERATION_CODE_ASCQ 0x00 #define INVALID_FIELD_IN_CDB_ASC 0x24 #define INVALID_FIELD_IN_CDB_ASCQ 0x00 #define INVALID_FIELD_IN_PARAMETER_LIST_ASC 0x26 #define INVALID_FIELD_IN_PARAMETER_LIST_ASCQ 0x00 #define INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASC 0x26 #define INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASCQ 0x04 #define RESERVATIONS_PREEMPTED_ASC 0x2A #define RESERVATIONS_PREEMPTED_ASCQ 0x03 #define RESERVATIONS_RELEASED_ASC 0x2A #define RESERVATIONS_RELEASED_ASCQ 0x04 #define REGISTRATIONS_PREEMPTED_ASC 0x2A #define REGISTRATIONS_PREEMPTED_ASCQ 0x05 #define INSUFFICIENT_RESERVATION_RESOURCES_ASC 0x55 #define INSUFFICIENT_RESERVATION_RESOURCES_ASCQ 0x02 #define INSUFFICIENT_REGISTRATION_RESOURCES_ASC 0x55 #define INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ 0x04 enum pr_in_service_actions { SERVICE_ACTION_READ_KEYS = 0x0, SERVICE_ACTION_READ_RESERVATION = 0x1, SERVICE_ACTION_REPORT_CAPABILITIES = 0x2, SERVICE_ACTION_READ_FULL_STATUS = 0x3 }; struct persistent_reserve_in { u8 opcode; /* PERSISTENT_RESERVE_IN == 0x5e */ u8 service_action; u8 rsvd[5]; __be16 allocation_length; u8 control; } __packed; enum pr_type_mask { PR_TYPE_WR_EX_AR = 0x8000, /* Write Excl., All Registrants */ PR_TYPE_EX_AC_RO = 0x4000, /* Excl. Access, Registrants Only */ PR_TYPE_WR_EX_RO = 0x2000, /* Write Excl., Registrants Only */ PR_TYPE_EX_AC = 0x800, /* Excl. Access */ PR_TYPE_WR_EX = 0x200, /* Write Excl. */ PR_TYPE_EX_AC_AR = 0x1, /* Excl. Access, All Registrants */ }; enum { PR_IN_REPORT_CAP_PTPL_C = 1, /* Persist Through Power Loss Capable */ PR_IN_REPORT_CAP_ATP_C = 1 << 2, /* All Target Ports Capable */ PR_IN_REPORT_CAP_SIP_C = 1 << 3, /* Specify Initiator Ports Capable */ PR_IN_REPORT_CAP_CRH = 1 << 4 /* Compatible Reservation Handling */ }; enum { PR_IN_REPORT_CAP_PTPL_A = 1, /* Persist Through Power Loss Activated */ PR_IN_REPORT_CAP_TMV = 1 << 7, /* Type Mask Valid */ }; struct pr_in_report_capabilities_data { __be16 length; u8 crh_sip_atp_ptpl_c; u8 tmv_ptpl_a; /* SPC-4 has allow_commands here - don't care for now */ __be16 type_mask; u8 rsvd4[2]; } __packed; enum { PR_SERVICE_ACTION_MASK = 0x1f, PR_TYPE_MASK = 0xf, PR_SCOPE_MASK = 0xf << 4, }; struct pr_in_read_reservation_data { __be32 generation; __be32 additional_length; __be64 reservation_key; u8 obsolete1[4]; u8 rsvd; u8 scope_type; u8 obsolete2[2]; } __packed; enum { TRANSPORT_ID_FMT_CODE_MASK = 0xc0, TRANSPORT_ID_FMT_CODE_ISCSI = 0x0, TRANSPORT_ID_PROTO_ID_MASK = 0xf, TRANSPORT_ID_PROTO_ID_ISCSI = 0x5, }; struct iscsi_transport_id { u8 fmt_code_proto_id; u8 rsvd; __be16 additional_length; u8 iscsi_name[0]; } __packed; enum { PR_OUT_STATUS_DESC_R_HOLDER = 1, PR_OUT_STATUS_DESC_ALL_TG_PT = 1 << 1, }; /* this is iscsi specific */ struct pr_in_full_status_descriptor { __be64 reservation_key; u8 rsvd1[4]; u8 all_tg_pt_r_holder; u8 scope_type; u8 rsvd2[4]; __be16 rel_tgt_port_id; __be32 additional_desc_length; struct iscsi_transport_id iscsi_transport_id[0]; } __packed; struct pr_in_read_full_status_data { __be32 generation; __be32 additional_length; struct pr_in_full_status_descriptor descriptors[0]; } __packed; struct pr_in_read_keys_data { __be32 generation; __be32 additional_length; __be64 keys[0]; } __packed; enum pr_out_service_actions { SERVICE_ACTION_REGISTER = 0x0, SERVICE_ACTION_RESERVE = 0x1, SERVICE_ACTION_RELEASE = 0x2, SERVICE_ACTION_CLEAR = 0x3, SERVICE_ACTION_PREEMPT = 0x4, SERVICE_ACTION_PREEMPT_ABORT = 0x5, SERVICE_ACTION_REGISTER_IGNORE = 0x6, SERVICE_ACTION_REGISTER_MOVE = 0x7 }; enum persistent_reservation_scope { PR_SCOPE_LU = 0x0, }; enum persistent_reservation_type { PR_TYPE_NONE = 0x0, /* "abuse" obsolete value */ PR_TYPE_WRITE_EXCLUSIVE = 0x1, PR_TYPE_EXCLUSIVE_ACCESS = 0x3, PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY = 0x5, PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY = 0x6, PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS = 0x7, PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS = 0x8 }; struct persistent_reserve_out { u8 opcode; /* PERSISTENT_RESERVE_OUT == 0x5f */ u8 service_action; u8 scope_type; u8 rsvd[2]; __be32 parameter_list_length; u8 control; } __packed; enum { PR_OUT_PARAM_APTPL = 1, PR_OUT_PARAM_ALL_TG_PT = 1 << 2, PR_OUT_PARAM_SPEC_I_PT = 1 << 3, }; struct pr_out_param_list { __be64 reservation_key; __be64 service_action_key; u8 obsolete1[4]; u8 spec_i_pt_all_tg_pt_aptl; u8 rsvd; u8 obsolete2[2]; u8 additional_parameter_data[0]; } __packed; enum reservation_type { RESERVATION_TYPE_NONE, RESERVATION_TYPE_RESERVE, RESERVATION_TYPE_PERSISTENT }; struct reservation { /* RESERVATION_TYPE_NONE indicates "not reserved" */ enum reservation_type reservation_type; enum persistent_reservation_type persistent_type; u32 generation; u64 sid; __be64 reservation_key; struct list_head registration_list; }; static inline bool pr_is_reserved(const struct reservation* res) { return res->reservation_type != RESERVATION_TYPE_NONE; } static inline bool pr_type_is_all_registrants(const struct reservation *res) { return ((res->persistent_type == PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS) || (res->persistent_type == PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS)); } struct iscsi_session; bool pr_is_reserved_by_session(const struct reservation *res, const struct iscsi_session *sess); bool pr_initiator_has_registered(const struct reservation *res, u64 sid); struct iscsi_cmnd; void build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd); void build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd); #endif iscsitarget-1.4.20.3+svn502/kernel/session.c000066400000000000000000000107041233224651300204230ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include "iscsi.h" #include "iscsi_dbg.h" struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid) { struct iscsi_session *session; list_for_each_entry(session, &target->session_list, list) { if (session->sid == sid) return session; } return NULL; } static struct iscsi_session * iet_session_alloc(struct iscsi_target *target, struct session_info *info) { int i; struct iscsi_session *session; struct iet_volume *vol; dprintk(D_SETUP, "%p %u %#Lx\n", target, target->tid, (unsigned long long) info->sid); session = kzalloc(sizeof(*session), GFP_KERNEL); if (!session) return NULL; session->target = target; session->sid = info->sid; memcpy(&session->param, &target->sess_param, sizeof(session->param)); session->max_queued_cmnds = target->trgt_param.queued_cmnds; session->exp_cmd_sn = info->exp_cmd_sn; session->max_cmd_sn = info->max_cmd_sn; session->initiator = kstrdup(info->initiator_name, GFP_KERNEL); if (!session->initiator) { kfree(session); return NULL; } INIT_LIST_HEAD(&session->conn_list); INIT_LIST_HEAD(&session->pending_list); spin_lock_init(&session->cmnd_hash_lock); for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++) INIT_LIST_HEAD(&session->cmnd_hash[i]); spin_lock_init(&session->ua_hash_lock); for (i = 0; i < ARRAY_SIZE(session->ua_hash); i++) INIT_LIST_HEAD(&session->ua_hash[i]); list_for_each_entry(vol, &target->volumes, list) /* power-on, reset, or bus device reset occurred */ ua_establish_for_session(session, vol->lun, 0x29, 0x0); session->next_ttt = 1; spin_lock(&target->session_list_lock); list_add(&session->list, &target->session_list); spin_unlock(&target->session_list_lock); return session; } static int session_free(struct iscsi_session *session) { int i; struct ua_entry *ua, *tmp; struct list_head *l; struct iscsi_target *target = session->target; dprintk(D_SETUP, "%#Lx\n", (unsigned long long) session->sid); spin_lock(&target->session_list_lock); assert(list_empty(&session->conn_list)); for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++) { if (!list_empty(&session->cmnd_hash[i])) BUG(); } for (i = 0; i < ARRAY_SIZE(session->ua_hash); i++) { l = &session->ua_hash[i]; list_for_each_entry_safe(ua, tmp, l, entry) { list_del_init(&ua->entry); ua_free(ua); } } list_del(&session->list); kfree(session->initiator); kfree(session); spin_unlock(&target->session_list_lock); return 0; } int session_add(struct iscsi_target *target, struct session_info *info) { struct iscsi_session *session; session = session_lookup(target, info->sid); if (session) return -EEXIST; session = iet_session_alloc(target, info); if (!session) return -ENOMEM; return 0; } void session_abort_tasks(struct iscsi_session *session, u32 lun) { struct iscsi_conn *conn; struct iscsi_cmnd *cmnd, *tmp; list_for_each_entry(conn, &session->conn_list, list) { list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { if (translate_lun(cmnd_hdr(cmnd)->lun) != lun) continue; __cmnd_abort(cmnd); } } } int session_del(struct iscsi_target *target, u64 sid) { struct iscsi_session *session; struct iet_volume *volume; session = session_lookup(target, sid); if (!session) return -ENOENT; if (!list_empty(&session->conn_list)) { DECLARE_COMPLETION_ONSTACK(done); struct iscsi_conn *conn; session->done = &done; list_for_each_entry(conn, &session->conn_list, list) conn_close(conn); target_unlock(target); wait_for_completion(&done); target_lock(target, 0); } list_for_each_entry(volume, &target->volumes, list){ volume_release(volume, sid, 0); } return session_free(session); } static void iet_session_info_show(struct seq_file *seq, struct iscsi_target *target) { struct iscsi_session *session; list_for_each_entry(session, &target->session_list, list) { seq_printf(seq, "\tsid:%llu initiator:%s\n", (unsigned long long) session->sid, session->initiator); conn_info_show(seq, session); } } static int iet_session_seq_open(struct inode *inode, struct file *file) { int res; res = seq_open(file, &iet_seq_op); if (!res) ((struct seq_file *)file->private_data)->private = iet_session_info_show; return res; } struct file_operations session_seq_fops = { .owner = THIS_MODULE, .open = iet_session_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; iscsitarget-1.4.20.3+svn502/kernel/target.c000066400000000000000000000166001233224651300202270ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include #include "iscsi.h" #include "digest.h" #include "iscsi_dbg.h" #define MAX_NR_TARGETS (1UL << 30) static LIST_HEAD(target_list); static DEFINE_MUTEX(target_list_mutex); static u32 next_target_id; static u32 nr_targets; static struct iscsi_sess_param default_session_param = { .initial_r2t = 1, .immediate_data = 1, .max_connections = 1, .max_recv_data_length = 8192, .max_xmit_data_length = 8192, .max_burst_length = 262144, .first_burst_length = 65536, .default_wait_time = 2, .default_retain_time = 0, .max_outstanding_r2t = 1, .data_pdu_inorder = 1, .data_sequence_inorder = 1, .error_recovery_level = 0, .header_digest = DIGEST_NONE, .data_digest = DIGEST_NONE, .ofmarker = 0, .ifmarker = 0, .ofmarkint = 2048, .ifmarkint = 2048, }; static struct iscsi_trgt_param default_target_param = { .wthreads = DEFAULT_NR_WTHREADS, .target_type = 0, .queued_cmnds = DEFAULT_NR_QUEUED_CMNDS, }; inline int target_lock(struct iscsi_target *target, int interruptible) { int err = 0; if (interruptible) err = mutex_lock_interruptible(&target->target_mutex); else mutex_lock(&target->target_mutex); return err; } inline void target_unlock(struct iscsi_target *target) { mutex_unlock(&target->target_mutex); } static struct iscsi_target *__target_lookup_by_id(u32 id) { struct iscsi_target *target; list_for_each_entry(target, &target_list, t_list) { if (target->tid == id) return target; } return NULL; } static struct iscsi_target *__target_lookup_by_name(char *name) { struct iscsi_target *target; list_for_each_entry(target, &target_list, t_list) { if (!strcmp(target->name, name)) return target; } return NULL; } struct iscsi_target *target_lookup_by_id(u32 id) { struct iscsi_target *target; mutex_lock(&target_list_mutex); target = __target_lookup_by_id(id); mutex_unlock(&target_list_mutex); return target; } static int target_thread_start(struct iscsi_target *target) { int err; if ((err = nthread_start(target)) < 0) return err; if (!worker_thread_pool) { err = wthread_start(target->wthread_info, target->trgt_param.wthreads, target->tid); if (err) nthread_stop(target); } return err; } static void target_thread_stop(struct iscsi_target *target) { if (!worker_thread_pool) wthread_stop(target->wthread_info); nthread_stop(target); } static int iscsi_target_create(struct target_info *info, u32 tid) { int err = -EINVAL, len; char *name = info->name; struct iscsi_target *target; dprintk(D_SETUP, "%u %s\n", tid, name); if (!(len = strlen(name))) { eprintk("The length of the target name is zero %u\n", tid); return err; } if (!try_module_get(THIS_MODULE)) { eprintk("Fail to get module %u\n", tid); return err; } target = kzalloc(sizeof(*target), GFP_KERNEL); if (!target) { err = -ENOMEM; goto out; } if (!worker_thread_pool) { target->wthread_info = kmalloc(sizeof(struct worker_thread_info), GFP_KERNEL); if (!target->wthread_info) { err = -ENOMEM; goto out; } } target->tid = info->tid = tid; memcpy(&target->sess_param, &default_session_param, sizeof(default_session_param)); memcpy(&target->trgt_param, &default_target_param, sizeof(default_target_param)); strncpy(target->name, name, sizeof(target->name) - 1); mutex_init(&target->target_mutex); spin_lock_init(&target->session_list_lock); INIT_LIST_HEAD(&target->session_list); INIT_LIST_HEAD(&target->volumes); atomic_set(&target->nr_volumes, 0); nthread_init(target); if (!worker_thread_pool) wthread_init(target->wthread_info); else target->wthread_info = worker_thread_pool; if ((err = target_thread_start(target)) < 0) { target_thread_stop(target); goto out; } list_add(&target->t_list, &target_list); return 0; out: if (!worker_thread_pool) kfree(target->wthread_info); kfree(target); module_put(THIS_MODULE); return err; } int target_add(struct target_info *info) { u32 tid = info->tid; int err; err = mutex_lock_interruptible(&target_list_mutex); if (err < 0) return err; if (nr_targets > MAX_NR_TARGETS) { err = -EBUSY; goto out; } if (__target_lookup_by_name(info->name) || (tid && __target_lookup_by_id(tid))) { err = -EEXIST; goto out; } if (!tid) { do { if (!++next_target_id) ++next_target_id; } while (__target_lookup_by_id(next_target_id)); tid = next_target_id; } err = iscsi_target_create(info, tid); if (!err) nr_targets++; out: mutex_unlock(&target_list_mutex); return err; } static void target_destroy(struct iscsi_target *target) { dprintk(D_SETUP, "%u\n", target->tid); while (!list_empty(&target->volumes)) { struct iet_volume *volume; volume = list_entry(target->volumes.next, struct iet_volume, list); volume->l_state = IDEV_DEL; iscsi_volume_destroy(volume); } target_thread_stop(target); if (!worker_thread_pool) kfree(target->wthread_info); kfree(target); module_put(THIS_MODULE); } /* @locking: target_list_mutex must be locked */ static int __target_del(struct iscsi_target *target) { int err; target_lock(target, 0); if (!list_empty(&target->session_list)) { struct iscsi_session *session; do { session = list_entry(target->session_list.next, struct iscsi_session, list); err = session_del(target, session->sid); if (err < 0) { target_unlock(target); return err; } } while (!list_empty(&target->session_list)); } list_del(&target->t_list); nr_targets--; target_unlock(target); target_destroy(target); return 0; } int target_del(u32 id) { struct iscsi_target *target; int err; err = mutex_lock_interruptible(&target_list_mutex); if (err < 0) return err; target = __target_lookup_by_id(id); if (!target) { err = -ENOENT; goto out; } err = __target_del(target); out: mutex_unlock(&target_list_mutex); return err; } void target_del_all(void) { struct iscsi_target *target, *tmp; int err; mutex_lock(&target_list_mutex); if (!list_empty(&target_list)) iprintk("Removing all connections, sessions and targets\n"); list_for_each_entry_safe(target, tmp, &target_list, t_list) { u32 tid = target->tid; err =__target_del(target); if (err) eprintk("Error deleteing target %u: %d\n", tid, err); } next_target_id = 0; mutex_unlock(&target_list_mutex); } static void *iet_seq_start(struct seq_file *m, loff_t *pos) { int err; /* are you sure this is to be interruptible? */ err = mutex_lock_interruptible(&target_list_mutex); if (err < 0) return ERR_PTR(err); return seq_list_start(&target_list, *pos); } static void *iet_seq_next(struct seq_file *m, void *v, loff_t *pos) { return seq_list_next(v, &target_list, pos); } static void iet_seq_stop(struct seq_file *m, void *v) { if (PTR_ERR(v) != -EINTR) mutex_unlock(&target_list_mutex); } static int iet_seq_show(struct seq_file *m, void *p) { iet_show_info_t *func = (iet_show_info_t *)m->private; struct iscsi_target *target = list_entry(p, struct iscsi_target, t_list); int err; /* relly, interruptible? I'd think target_lock(target, 0) * would be more appropriate. --lge */ err = target_lock(target, 1); if (err < 0) return err; seq_printf(m, "tid:%u name:%s\n", target->tid, target->name); func(m, target); target_unlock(target); return 0; } struct seq_operations iet_seq_op = { .start = iet_seq_start, .next = iet_seq_next, .stop = iet_seq_stop, .show = iet_seq_show, }; iscsitarget-1.4.20.3+svn502/kernel/target_disk.c000066400000000000000000000363641233224651300212520ustar00rootroot00000000000000/* * (C) 2004 - 2005 FUJITA Tomonori * This code is licenced under the GPL. * * heavily based on code from kernel/iscsi.c: * Copyright (C) 2002-2003 Ardis Technolgies , * licensed under the terms of the GNU GPL v2.0, */ #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" static int insert_disconnect_pg(u8 *ptr) { unsigned char disconnect_pg[] = {0x02, 0x0e, 0x80, 0x80, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(ptr, disconnect_pg, sizeof(disconnect_pg)); return sizeof(disconnect_pg); } static int insert_caching_pg(u8 *ptr, int wcache, int rcache) { unsigned char caching_pg[] = {0x08, 0x12, 0x10, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(ptr, caching_pg, sizeof(caching_pg)); if (wcache) ptr[2] |= 0x04; /* set WCE bit if we're caching writes */ if (!rcache) ptr[2] |= 0x01; /* Read Cache Disable */ return sizeof(caching_pg); } static int insert_ctrl_m_pg(u8 *ptr) { unsigned char ctrl_m_pg[] = {0x0a, 0x0a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4b}; memcpy(ptr, ctrl_m_pg, sizeof(ctrl_m_pg)); return sizeof(ctrl_m_pg); } static int insert_iec_m_pg(u8 *ptr) { unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(ptr, iec_m_pg, sizeof(iec_m_pg)); return sizeof(iec_m_pg); } static int insert_format_m_pg(u8 *ptr, u32 sector_size) { unsigned char format_m_pg[] = {0x03, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00}; memcpy(ptr, format_m_pg, sizeof(format_m_pg)); ptr[12] = (sector_size >> 8) & 0xff; ptr[13] = sector_size & 0xff; return sizeof(format_m_pg); } static int insert_geo_m_pg(u8 *ptr, u64 sec) { unsigned char geo_m_pg[] = {0x04, 0x16, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x98, 0x00, 0x00}; u32 ncyl; u32 n; /* assume 0xff heads, 15krpm. */ memcpy(ptr, geo_m_pg, sizeof(geo_m_pg)); ncyl = sec >> 14; /* 256 * 64 */ memcpy(&n, ptr+1, sizeof(u32)); n = n | cpu_to_be32(ncyl); memcpy(ptr+1, &n, sizeof(u32)); return sizeof(geo_m_pg); } static void build_mode_sense_response(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); struct tio *tio = cmnd->tio; u8 *data, *scb = req->scb; int len = 4, err = 0; u8 pcode; /* changeable parameter mode pages are unsupported */ if ((scb[2] & 0xc0) >> 6 == 0x1) goto set_sense; pcode = req->scb[2] & 0x3f; assert(!tio); tio = cmnd->tio = tio_alloc(1); data = page_address(tio->pvec[0]); assert(data); clear_page(data); if (LUReadonly(cmnd->lun)) data[2] = 0x80; if ((scb[1] & 0x8)) data[3] = 0; else { data[3] = 8; len += 8; *(u32 *)(data + 4) = (cmnd->lun->blk_cnt >> 32) ? cpu_to_be32(0xffffffff) : cpu_to_be32(cmnd->lun->blk_cnt); *(u32 *)(data + 8) = cpu_to_be32(1 << cmnd->lun->blk_shift); } switch (pcode) { case 0x0: break; case 0x2: len += insert_disconnect_pg(data + len); break; case 0x3: len += insert_format_m_pg(data + len, 1 << cmnd->lun->blk_shift); break; case 0x4: len += insert_geo_m_pg(data + len, cmnd->lun->blk_cnt); break; case 0x8: len += insert_caching_pg(data + len, LUWCache(cmnd->lun), LURCache(cmnd->lun)); break; case 0xa: len += insert_ctrl_m_pg(data + len); break; case 0x1c: len += insert_iec_m_pg(data + len); break; case 0x3f: len += insert_disconnect_pg(data + len); len += insert_format_m_pg(data + len, 1 << cmnd->lun->blk_shift); len += insert_geo_m_pg(data + len, cmnd->lun->blk_cnt); len += insert_caching_pg(data + len, LUWCache(cmnd->lun), LURCache(cmnd->lun)); len += insert_ctrl_m_pg(data + len); len += insert_iec_m_pg(data + len); break; default: err = -1; } if (!err) { data[0] = len - 1; tio_set(tio, len, 0); return; } tio_put(tio); cmnd->tio = NULL; set_sense: /* Invalid Field In CDB */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); } static void build_inquiry_response(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); struct tio *tio = cmnd->tio; u8 *data; u8 *scb = req->scb; int err = -1; /* * - CmdDt and EVPD both set or EVPD and Page Code set: illegal * - CmdDt set: not supported */ if ((scb[1] & 0x3) > 0x1 || (!(scb[1] & 0x3) && scb[2])) goto set_sense; assert(!tio); tio = cmnd->tio = tio_alloc(1); data = page_address(tio->pvec[0]); assert(data); clear_page(data); if (!(scb[1] & 0x3)) { data[2] = 4; data[3] = 0x52; data[4] = 59; data[7] = 0x02; memset(data + 8, 0x20, 28); memcpy(data + 8, VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8)); memcpy(data + 16, PRODUCT_ID, min_t(size_t, strlen(PRODUCT_ID), 16)); memcpy(data + 32, PRODUCT_REV, min_t(size_t, strlen(PRODUCT_REV), 4)); data[58] = 0x03; data[59] = 0x20; data[60] = 0x09; data[61] = 0x60; data[62] = 0x03; data[63] = 0x00; tio_set(tio, 64, 0); err = 0; } else if (scb[1] & 0x1) { /* EVPD bit set */ if (scb[2] == 0x0) { data[1] = 0x0; data[3] = 3; data[4] = 0x0; data[5] = 0x80; data[6] = 0x83; tio_set(tio, 7, 0); err = 0; } else if (scb[2] == 0x80) { u32 len = 4; if (cmnd->lun) { if (strlen(cmnd->lun->scsi_sn) <= 16) len = 16; else len = SCSI_SN_LEN; } data[1] = 0x80; data[3] = len; memset(data + 4, 0x20, len); if (cmnd->lun) { size_t offset = len - strlen(cmnd->lun->scsi_sn); memcpy(data + 4 + offset, cmnd->lun->scsi_sn, strlen(cmnd->lun->scsi_sn)); } tio_set(tio, len + 4, 0); err = 0; } else if (scb[2] == 0x83) { u32 len = SCSI_ID_LEN + 8; data[1] = 0x83; data[3] = len + 4; data[4] = 0x1; data[5] = 0x1; data[7] = len; if (cmnd->lun) { /* We need this ? */ memset(data + 8, 0x00, 8); memcpy(data + 8, VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8)); memcpy(data + 16, cmnd->lun->scsi_id, SCSI_ID_LEN); } tio_set(tio, len + 8, 0); err = 0; } } if (!err) { tio_set(tio, min_t(u8, tio->size, scb[4]), 0); if (!cmnd->lun) data[0] = TYPE_NO_LUN; return; } tio_put(tio); cmnd->tio = NULL; set_sense: /* Invalid Field In CDB */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); } static void build_report_luns_response(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); struct tio *tio = cmnd->tio; u32 *data, size, len; struct iet_volume *lun; int rest, idx = 0; size = (u32)req->scb[6] << 24 | (u32)req->scb[7] << 16 | (u32)req->scb[8] << 8 | (u32)req->scb[9]; if (size < 16) { /* Invalid Field In CDB */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); return; } len = atomic_read(&cmnd->conn->session->target->nr_volumes) * 8; size = min(size & ~(8 - 1), len + 8); assert(!tio); tio = cmnd->tio = tio_alloc(get_pgcnt(size)); tio_set(tio, size, 0); data = page_address(tio->pvec[idx]); assert(data); *data++ = cpu_to_be32(len); *data++ = 0; size -= 8; rest = PAGE_CACHE_SIZE - 8; list_for_each_entry(lun, &cmnd->conn->session->target->volumes, list) { if (lun->l_state != IDEV_RUNNING) continue; *data++ = cpu_to_be32((0x3ff & lun->lun) << 16 | ((lun->lun > 0xff) ? (0x1 << 30) : 0)); *data++ = 0; if ((size -= 8) == 0) break; if ((rest -= 8) == 0) { idx++; data = page_address(tio->pvec[idx]); rest = PAGE_CACHE_SIZE; } } } static void build_read_capacity_response(struct iscsi_cmnd *cmnd) { struct tio *tio = cmnd->tio; u32 *data; assert(!tio); tio = cmnd->tio = tio_alloc(1); data = page_address(tio->pvec[0]); assert(data); clear_page(data); data[0] = (cmnd->lun->blk_cnt >> 32) ? cpu_to_be32(0xffffffff) : cpu_to_be32(cmnd->lun->blk_cnt - 1); data[1] = cpu_to_be32(1U << cmnd->lun->blk_shift); tio_set(tio, 8, 0); } static void build_request_sense_response(struct iscsi_cmnd *cmnd) { struct tio *tio = cmnd->tio; u8 *data; assert(!tio); tio = cmnd->tio = tio_alloc(1); data = page_address(tio->pvec[0]); assert(data); memset(data, 0, 18); data[0] = 0xf0; data[1] = 0; data[2] = NO_SENSE; data[7] = 10; tio_set(tio, 18, 0); } static void build_service_action_in_response(struct iscsi_cmnd *cmnd) { struct tio *tio = cmnd->tio; u32 *data; u64 *data64; u32 alloc_len; const u8* cdb = cmnd_hdr(cmnd)->scb; assert(!tio); /* only READ_CAPACITY_16 service action is currently supported */ if ((cdb[1] & 0x1F) != 0x10) { /* Invalid Field In CDB */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); return; } alloc_len = get_unaligned_be32(&cdb[10]); tio = cmnd->tio = tio_alloc(1); data = page_address(tio->pvec[0]); assert(data); clear_page(data); data64 = (u64*) data; data64[0] = cpu_to_be64(cmnd->lun->blk_cnt - 1); data[2] = cpu_to_be32(1UL << cmnd->lun->blk_shift); tio_set(tio, min_t(u32, alloc_len, 32), 0); } static void build_read_response(struct iscsi_cmnd *cmnd) { struct tio *tio = cmnd->tio; assert(tio); assert(cmnd->lun); if (tio_read(cmnd->lun, tio)) /* Medium Error/Unrecovered Read Error */ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x11, 0x0); } static void build_write_response(struct iscsi_cmnd *cmnd) { int err; struct tio *tio = cmnd->tio; assert(tio); assert(cmnd->lun); list_del_init(&cmnd->list); err = tio_write(cmnd->lun, tio); if (!err && !LUWCache(cmnd->lun)) err = tio_sync(cmnd->lun, tio); if (err) /* Medium Error/Write Fault */ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); } static void build_write_same_response(struct iscsi_cmnd *cmnd) { int err; struct tio *target_tio; struct iet_volume *lu = cmnd->lun; struct tio_iterator iter; u32 MAX_IO_SIZE = 1 << 20; /* 1MByte */ u64 length, medium_length; loff_t offset; u8 *data_addr; u32 data_size; length = cmnd->tio->size; medium_length = ((loff_t) lu->blk_cnt << lu->blk_shift); offset = cmnd->tio->offset; data_addr = page_address(cmnd->tio->pvec[0]); data_size = be32_to_cpu(cmnd_hdr(cmnd)->data_length); /* When length = 0, it means we need to write to the end. */ if (length == 0) { length = medium_length - offset + 1; dprintk(D_VAAI, "write to end, calculated length = %llu\n", length); } if (length + offset > medium_length) { /* Write out of boundary, Invalid Field in CDB */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); return; } list_del_init(&cmnd->list); /* Fill target_tio with data from request, because it's all same data, we can just reuse it later with differnt offset. */ target_tio = tio_alloc(get_pgcnt(min_t(u32, length, MAX_IO_SIZE))); tio_init_iterator(target_tio, &iter); while(iter.pg_idx < target_tio->pg_cnt) { tio_add_data(&iter, data_addr, data_size); } while (length > 0) { u32 to_write = (u32)min_t(u64, length, MAX_IO_SIZE); tio_set(target_tio, to_write, offset); /* submit to IO layer */ err = tio_write(lu, target_tio); if (!err && !LUWCache(lu)) err = tio_sync(lu, target_tio); if (err) { /* Medium Error/Write Fault */ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); break; } length -= to_write; offset += to_write; dprintk(D_VAAI, "Committed %u bytes, %llu left, offset %llu\n", to_write, length, offset); } tio_put(target_tio); } static void build_sync_cache_response(struct iscsi_cmnd *cmnd) { assert(cmnd->lun); if (tio_sync(cmnd->lun, NULL)) /* Medium Error/Write Fault */ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); } static void build_generic_response(struct iscsi_cmnd *cmnd) { return; } static void build_reserve_response(struct iscsi_cmnd *cmnd) { switch (volume_reserve(cmnd->lun, cmnd->conn->session->sid)) { case -ENOENT: /* Logical Unit Not Supported (?) */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x25, 0x0); break; case -EBUSY: cmnd->status = SAM_STAT_RESERVATION_CONFLICT; break; default: break; } } static void build_release_response(struct iscsi_cmnd *cmnd) { int ret = volume_release(cmnd->lun, cmnd->conn->session->sid, 0); switch (ret) { case -ENOENT: /* Logical Unit Not Supported (?) */ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x25, 0x0); break; case -EBUSY: cmnd->status = SAM_STAT_RESERVATION_CONFLICT; break; default: break; } } static void build_reservation_conflict_response(struct iscsi_cmnd *cmnd) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; } static int disk_check_ua(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); struct ua_entry *ua; if (cmnd->lun && ua_pending(cmnd->conn->session, cmnd->lun->lun)) { switch(req->scb[0]){ case INQUIRY: case REQUEST_SENSE: break; case REPORT_LUNS: ua = ua_get_match(cmnd->conn->session, cmnd->lun->lun, /* reported luns data has changed */ 0x3f, 0x0e); ua_free(ua); break; default: ua = ua_get_first(cmnd->conn->session, cmnd->lun->lun); /* * potential race: another wthread could've reported it * in the meantime */ if (ua) { iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, ua->asc, ua->ascq); ua_free(ua); send_scsi_rsp(cmnd, build_generic_response); return 1; } } } return 0; } static int disk_check_reservation(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); int ret = is_volume_reserved(cmnd->lun, cmnd->conn->session->sid, req->scb); if (ret == -EBUSY) { send_scsi_rsp(cmnd, build_reservation_conflict_response); return 1; } return 0; } static int disk_execute_cmnd(struct iscsi_cmnd *cmnd) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); req->opcode &= ISCSI_OPCODE_MASK; if (disk_check_ua(cmnd)) return 0; if (disk_check_reservation(cmnd)) return 0; switch (req->scb[0]) { case INQUIRY: send_data_rsp(cmnd, build_inquiry_response); break; case REPORT_LUNS: send_data_rsp(cmnd, build_report_luns_response); break; case READ_CAPACITY: send_data_rsp(cmnd, build_read_capacity_response); break; case MODE_SENSE: send_data_rsp(cmnd, build_mode_sense_response); break; case REQUEST_SENSE: send_data_rsp(cmnd, build_request_sense_response); break; case SERVICE_ACTION_IN: send_data_rsp(cmnd, build_service_action_in_response); break; case READ_6: case READ_10: case READ_16: send_data_rsp(cmnd, build_read_response); break; case WRITE_6: case WRITE_10: case WRITE_16: case WRITE_VERIFY: send_scsi_rsp(cmnd, build_write_response); break; case WRITE_SAME_16: send_scsi_rsp(cmnd, build_write_same_response); break; case SYNCHRONIZE_CACHE: send_scsi_rsp(cmnd, build_sync_cache_response); break; case RESERVE: send_scsi_rsp(cmnd, build_reserve_response); break; case RELEASE: send_scsi_rsp(cmnd, build_release_response); break; case START_STOP: case TEST_UNIT_READY: case VERIFY: case VERIFY_16: send_scsi_rsp(cmnd, build_generic_response); break; case PERSISTENT_RESERVE_IN: send_data_rsp(cmnd, build_persistent_reserve_in_response); break; case PERSISTENT_RESERVE_OUT: send_scsi_rsp(cmnd, build_persistent_reserve_out_response); break; default: eprintk("%s\n", "we should not come here!"); break; } return 0; } struct target_type disk_ops = { .id = 0, .execute_cmnd = disk_execute_cmnd, }; iscsitarget-1.4.20.3+svn502/kernel/tio.c000066400000000000000000000055471233224651300175440ustar00rootroot00000000000000/* * Target I/O. * (C) 2005 FUJITA Tomonori * This code is licenced under the GPL. */ #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" static int tio_add_pages(struct tio *tio, int count) { int i; struct page *page; dprintk(D_GENERIC, "%p %d (%d)\n", tio, count, tio->pg_cnt); tio->pg_cnt = count; count *= sizeof(struct page *); do { tio->pvec = kzalloc(count, GFP_KERNEL); if (!tio->pvec) yield(); } while (!tio->pvec); for (i = 0; i < tio->pg_cnt; i++) { do { if (!(page = alloc_page(GFP_KERNEL))) yield(); } while (!page); tio->pvec[i] = page; } return 0; } static struct kmem_cache *tio_cache; struct tio *tio_alloc(int count) { struct tio *tio; tio = kmem_cache_alloc(tio_cache, GFP_KERNEL | __GFP_NOFAIL); tio->pg_cnt = 0; tio->offset = 0; tio->size = 0; tio->pvec = NULL; atomic_set(&tio->count, 1); if (count) tio_add_pages(tio, count); return tio; } void tio_init_iterator(struct tio *tio, struct tio_iterator *iter) { iter->tio = tio; iter->size = 0; iter->pg_idx = 0; iter->pg_off = 0; } size_t tio_add_data(struct tio_iterator *iter, const u8 *data, size_t len) { struct tio *tio = iter->tio; const size_t to_copy = min(tio->pg_cnt * PAGE_SIZE - iter->size, len); size_t residual = to_copy; BUG_ON(tio->size < iter->size); do { u8 *ptr = page_address(iter->tio->pvec[iter->pg_idx]) + iter->pg_off; size_t chunk = min(PAGE_SIZE - iter->pg_off, residual); memcpy(ptr, data, chunk); residual -= chunk; if (residual || iter->pg_off + chunk == PAGE_SIZE) { ++iter->pg_idx; iter->pg_off = 0; } else iter->pg_off += chunk; } while (residual); return to_copy; } static void tio_free(struct tio *tio) { int i; for (i = 0; i < tio->pg_cnt; i++) { assert(tio->pvec[i]); __free_page(tio->pvec[i]); } kfree(tio->pvec); kmem_cache_free(tio_cache, tio); } void tio_put(struct tio *tio) { assert(atomic_read(&tio->count)); if (atomic_dec_and_test(&tio->count)) tio_free(tio); } void tio_get(struct tio *tio) { atomic_inc(&tio->count); } void tio_set(struct tio *tio, u32 size, loff_t offset) { tio->offset = offset; tio->size = size; } int tio_read(struct iet_volume *lu, struct tio *tio) { struct iotype *iot = lu->iotype; assert(iot); if (!tio->size) return 0; return iot->make_request ? iot->make_request(lu, tio, READ) : 0; } int tio_write(struct iet_volume *lu, struct tio *tio) { struct iotype *iot = lu->iotype; assert(iot); if (!tio->size) return 0; return iot->make_request ? iot->make_request(lu, tio, WRITE) : 0; } int tio_sync(struct iet_volume *lu, struct tio *tio) { struct iotype *iot = lu->iotype; assert(iot); return iot->sync ? iot->sync(lu, tio) : 0; } int tio_init(void) { tio_cache = KMEM_CACHE(tio, 0); return tio_cache ? 0 : -ENOMEM; } void tio_exit(void) { if (tio_cache) kmem_cache_destroy(tio_cache); } iscsitarget-1.4.20.3+svn502/kernel/ua.c000066400000000000000000000072501233224651300173470ustar00rootroot00000000000000/* * IET Unit Attention support * * Copyright (C) 2009 Xie Gang * Copyright (C) 2009 Arne Redlich * * Released under the terms of the GNU GPL v2.0. */ #include #include "iscsi.h" #include "iscsi_dbg.h" #define ua_hashfn(lun) ((lun % UA_HASH_LEN)) static struct kmem_cache *ua_cache; int ua_init(void) { ua_cache = KMEM_CACHE(ua_entry, 0); if (!ua_cache) { eprintk("%s", "Failed to create ua cache\n"); return -ENOMEM; } return 0; } void ua_exit(void) { if (ua_cache) kmem_cache_destroy(ua_cache); } /* sess->ua_hash_lock needs to be held */ static struct ua_entry * ua_find_hash(struct iscsi_session *sess, u32 lun, u8 asc, u8 ascq, int match) { struct ua_entry *ua; struct list_head *h = &sess->ua_hash[ua_hashfn(lun)]; list_for_each_entry(ua, h, entry) { if (ua->lun == lun) { if (!match) return ua; if (ua->asc == asc && ua->ascq == ascq) return ua; } } return NULL; } int ua_pending(struct iscsi_session *sess, u32 lun) { struct ua_entry *ua; spin_lock(&sess->ua_hash_lock); ua = ua_find_hash(sess, lun, 0, 0, 0); spin_unlock(&sess->ua_hash_lock); dprintk_ua(ua, sess, lun); return ua ? 1 : 0; } /* sess->ua_hash_lock needs to be held */ static struct ua_entry * __ua_get_hash(struct iscsi_session *sess, u32 lun, u8 asc, u8 ascq, int match) { struct ua_entry *ua = ua_find_hash(sess, lun, asc, ascq, match); if (ua) list_del_init(&ua->entry); return ua; } struct ua_entry * ua_get_first(struct iscsi_session *sess, u32 lun) { struct ua_entry *ua; spin_lock(&sess->ua_hash_lock); ua = __ua_get_hash(sess, lun, 0, 0, 0); spin_unlock(&sess->ua_hash_lock); dprintk_ua(ua, sess, lun); return ua; } struct ua_entry * ua_get_match(struct iscsi_session *sess, u32 lun, u8 asc, u8 ascq) { struct ua_entry *ua; spin_lock(&sess->ua_hash_lock); ua = __ua_get_hash(sess, lun, asc, ascq, 1); spin_unlock(&sess->ua_hash_lock); dprintk_ua(ua, sess, lun); return ua; } void ua_establish_for_session(struct iscsi_session *sess, u32 lun, u8 asc, u8 ascq) { struct list_head *l = &sess->ua_hash[ua_hashfn(lun)]; struct ua_entry *ua = kmem_cache_alloc(ua_cache, GFP_ATOMIC); struct ua_entry *e; if (!ua) { eprintk("%s", "Failed to alloc ua"); return; } ua->asc = asc; ua->ascq = ascq; ua->lun = lun; ua->session = sess; INIT_LIST_HEAD(&ua->entry); spin_lock(&sess->ua_hash_lock); /* One UA per occurrence of an event */ list_for_each_entry(e, l, entry) { if (e->session == sess && e->lun == lun && e->asc == asc && e->ascq == ascq && e->session->exp_cmd_sn == sess->exp_cmd_sn) { spin_unlock(&sess->ua_hash_lock); ua_free(ua); return; } } list_add_tail(&ua->entry, l); spin_unlock(&sess->ua_hash_lock); dprintk_ua(ua, sess, lun); } void ua_establish_for_other_sessions(struct iscsi_session *sess, u32 lun, u8 asc, u8 ascq) { struct list_head *l = &sess->target->session_list; struct iscsi_session *s; spin_lock(&sess->target->session_list_lock); list_for_each_entry(s, l, list) if (s->sid != sess->sid) ua_establish_for_session(s, lun, asc, ascq); spin_unlock(&sess->target->session_list_lock); } void ua_establish_for_all_sessions(struct iscsi_target *target, u32 lun, u8 asc, u8 ascq) { struct list_head *l = &target->session_list; struct iscsi_session *s; spin_lock(&target->session_list_lock); list_for_each_entry(s, l, list) ua_establish_for_session(s, lun, asc, ascq); spin_unlock(&target->session_list_lock); } void ua_free(struct ua_entry *ua) { if (!ua) return; dprintk_ua(ua, ua->session, ua->lun); BUG_ON(!list_empty(&ua->entry)); kmem_cache_free(ua_cache, ua); } iscsitarget-1.4.20.3+svn502/kernel/volume.c000066400000000000000000000262631233224651300202560ustar00rootroot00000000000000/* * Volume manager * (C) 2004 - 2005 FUJITA Tomonori * This code is licenced under the GPL. */ #include #include #include #include #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" struct iet_volume *volume_lookup(struct iscsi_target *target, u32 lun) { struct iet_volume *volume; list_for_each_entry(volume, &target->volumes, list) { if (volume->lun == lun) return volume; } return NULL; } enum { opt_type, opt_iomode, opt_scsiid, opt_scsisn, opt_blk_size, opt_err, }; static match_table_t tokens = { {opt_type, "type=%s"}, {opt_iomode, "iomode=%s"}, {opt_scsiid, "scsiid=%s"}, {opt_scsisn, "scsisn=%s"}, {opt_blk_size, "blocksize=%u"}, {opt_err, NULL}, }; static int set_scsiid(struct iet_volume *volume, const char *id) { size_t len; if ((len = strlen(id)) > SCSI_ID_LEN) { eprintk("SCSI ID too long, %zd provided, %u max\n", len, SCSI_ID_LEN); return -EINVAL; } memcpy(volume->scsi_id, id, len); return 0; } static int set_scsisn(struct iet_volume *volume, const char *sn) { size_t len; int i; if ((len = strlen(sn)) > SCSI_SN_LEN) { eprintk("SCSI SN too long, %zd provided, %u max\n", len, SCSI_SN_LEN); return -EINVAL; } for (i = 0; i < len; i++) { if (!isascii(*(sn + i)) || !isprint(*(sn + i))) { eprintk("invalid characters in SCSI SN, %s\n", "only printable ascii characters allowed!"); return -EINVAL; } } memcpy(volume->scsi_sn, sn, len); return 0; } /* Generate a MD5 hash of the target IQN and LUN number */ static void gen_scsiid(struct iet_volume *volume) { struct hash_desc hash; hash.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); hash.flags = 0; if (!IS_ERR(hash.tfm)) { struct scatterlist sg[2]; unsigned int nbytes = 0; sg_init_table(sg, 2); sg_set_buf(&sg[0], volume->target->name, strlen(volume->target->name)); nbytes += strlen(volume->target->name); sg_set_buf(&sg[1], &volume->lun, sizeof(volume->lun)); nbytes += sizeof(volume->lun); crypto_hash_init(&hash); crypto_hash_update(&hash, sg, nbytes); crypto_hash_final(&hash, volume->scsi_id); crypto_free_hash(hash.tfm); } else { /* If no MD5 available set ID to TID and LUN */ memcpy(volume->scsi_id, &volume->target->tid, sizeof(volume->target->tid)); memcpy(volume->scsi_id + sizeof(volume->target->tid), &volume->lun, sizeof(volume->lun)); } } static int parse_volume_params(struct iet_volume *volume, char *params) { int err = 0; unsigned blk_sz; substring_t args[MAX_OPT_ARGS]; char *p, *argp = NULL, *bp, *buf = (char *) get_zeroed_page(GFP_USER); if (!buf) return -ENOMEM; bp = buf; strncpy(buf, params, PAGE_CACHE_SIZE); while ((p = strsep(&buf, ",")) != NULL) { int token; if (!*p) continue; iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case opt_type: argp = match_strdup(&args[0]); if (!argp) { err = -ENOMEM; break; } if (!(volume->iotype = get_iotype(argp))) err = -ENOENT; kfree(argp); break; case opt_iomode: argp = match_strdup(&args[0]); if (!argp) { err = -ENOMEM; break; } if (!strcmp(argp, "ro")) SetLUReadonly(volume); else if (!strcmp(argp, "wb")) SetLUWCache(volume); else if (strcmp(argp, "wt")) err = -EINVAL; kfree(argp); break; case opt_scsiid: argp = match_strdup(&args[0]); if (!argp) { err = -ENOMEM; break; } err = set_scsiid(volume, argp); kfree(argp); break; case opt_scsisn: argp = match_strdup(&args[0]); if (!argp) { err = -ENOMEM; break; } err = set_scsisn(volume, argp); kfree(argp); break; case opt_blk_size: argp = match_strdup(&args[0]); if (!argp) { err = -ENOMEM; break; } blk_sz = simple_strtoull(argp, NULL, 10); if (is_power_of_2(blk_sz) && 512 <= blk_sz && blk_sz <= IET_MAX_BLOCK_SIZE) volume->blk_shift = blksize_bits(blk_sz); else { eprintk("invalid BlockSize=%u\n", blk_sz); err = -EINVAL; } kfree(argp); break; default: break; } } if (!err && !volume->iotype && !(volume->iotype = get_iotype("fileio"))) { eprintk("%s\n", "Cannot find fileio"); err = -EINVAL; } free_page((unsigned long) bp); return err; } static void volume_reservation_exit(struct iet_volume *volume) { struct list_head *l, *n; struct registration *tmp; struct reservation *reservation = &volume->reservation; list_for_each_safe(l, n, &reservation->registration_list) { tmp = list_entry(l, struct registration, r_list); list_del(l); kfree(tmp); } } int volume_add(struct iscsi_target *target, struct volume_info *info) { int ret; struct iet_volume *volume; char *args; volume = volume_lookup(target, info->lun); if (volume) return -EEXIST; if (info->lun > 0x3fff) return -EINVAL; volume = kzalloc(sizeof(*volume), GFP_KERNEL); if (!volume) return -ENOMEM; volume->target = target; volume->lun = info->lun; INIT_LIST_HEAD(&volume->reservation.registration_list); args = kzalloc(info->args_len + 1, GFP_KERNEL); if (!args) { ret = -ENOMEM; goto free_volume; } ret = copy_from_user(args, (void *)(unsigned long)info->args_ptr, info->args_len); if (ret) { ret = -EFAULT; goto free_args; } ret = parse_volume_params(volume, args); if (ret < 0) goto free_args; ret = volume->iotype->attach(volume, args); if (ret < 0) goto free_args; if (!volume->scsi_id[0]) gen_scsiid(volume); if (!volume->scsi_sn[0]) { int i; for (i = 0; i < SCSI_ID_LEN; i++) snprintf(volume->scsi_sn + (i * 2), 3, "%02x", volume->scsi_id[i]); } INIT_LIST_HEAD(&volume->queue.wait_list); spin_lock_init(&volume->queue.queue_lock); spin_lock_init(&volume->reserve_lock); volume->l_state = IDEV_RUNNING; atomic_set(&volume->l_count, 0); list_add_tail(&volume->list, &target->volumes); atomic_inc(&target->nr_volumes); kfree(args); return 0; free_args: kfree(args); free_volume: put_iotype(volume->iotype); kfree(volume); return ret; } void iscsi_volume_destroy(struct iet_volume *volume) { assert(volume->l_state == IDEV_DEL); assert(!atomic_read(&volume->l_count)); volume->iotype->detach(volume); put_iotype(volume->iotype); list_del(&volume->list); volume_reservation_exit(volume); kfree(volume); } int iscsi_volume_del(struct iscsi_target *target, struct volume_info *info) { struct iet_volume *volume; eprintk("%x %x\n", target->tid, info->lun); if (!(volume = volume_lookup(target, info->lun))) return -ENOENT; volume->l_state = IDEV_DEL; atomic_dec(&target->nr_volumes); if (!atomic_read(&volume->l_count)) iscsi_volume_destroy(volume); return 0; } struct iet_volume *volume_get(struct iscsi_target *target, u32 lun) { struct iet_volume *volume; if ((volume = volume_lookup(target, lun))) { if (volume->l_state == IDEV_RUNNING) atomic_inc(&volume->l_count); else volume = NULL; } return volume; } void volume_put(struct iet_volume *volume) { if (atomic_dec_and_test(&volume->l_count) && volume->l_state == IDEV_DEL) iscsi_volume_destroy(volume); } int volume_reserve(struct iet_volume *volume, u64 sid) { int err = 0; struct reservation *reservation; if (!volume) return -ENOENT; reservation = &volume->reservation; spin_lock(&volume->reserve_lock); if (pr_is_reserved(reservation) && reservation->sid != sid) err = -EBUSY; else { reservation->reservation_type = RESERVATION_TYPE_RESERVE; reservation->sid = sid; } spin_unlock(&volume->reserve_lock); return err; } int is_volume_reserved(struct iet_volume *volume, u64 sid, u8 *scb) { int err = 0; struct reservation *reservation; bool registered = false; bool write_excl = false; bool excl_access = false; bool write_excl_ro = false; bool excl_access_ro = false; if (!volume) return -ENOENT; reservation = &volume->reservation; spin_lock(&volume->reserve_lock); if (!pr_is_reserved(reservation) || reservation->sid == sid) { spin_unlock(&volume->reserve_lock); return 0; } if (reservation->reservation_type == RESERVATION_TYPE_RESERVE) { switch (scb[0]) { case INQUIRY: case RELEASE: case REPORT_LUNS: case REQUEST_SENSE: case READ_CAPACITY: /* allowed commands when reserved */ break; case SERVICE_ACTION_IN: if ((scb[1] & 0x1F) == 0x10) break; /* fall through */ default: err = -EBUSY; break; } spin_unlock(&volume->reserve_lock); return err; } registered = pr_initiator_has_registered(reservation, sid); switch (reservation->persistent_type) { case PR_TYPE_WRITE_EXCLUSIVE: write_excl = true; break; case PR_TYPE_EXCLUSIVE_ACCESS: excl_access = true; break; case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: write_excl_ro = true; break; case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: excl_access_ro = true; break; default: break; } switch (scb[0]) { case INQUIRY: case TEST_UNIT_READY: case PERSISTENT_RESERVE_IN: case PERSISTENT_RESERVE_OUT: case REPORT_LUNS: case REQUEST_SENSE: case READ_CAPACITY: case START_STOP: break; case MODE_SENSE: case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: case WRITE_SAME_16: case WRITE_VERIFY: case SYNCHRONIZE_CACHE: if (write_excl || excl_access) err = -EBUSY; if ((write_excl_ro || excl_access_ro) && !registered) err = -EBUSY; break; case READ_6: case READ_10: case READ_12: case READ_16: case VERIFY: case VERIFY_16: if (excl_access) err = -EBUSY; if (excl_access_ro && !registered) err = -EBUSY; break; case SERVICE_ACTION_IN: if ((scb[1] & 0x1F) == 0x10) break; /* fall through */ case RELEASE: case RESERVE: default: err = -EBUSY; break; } spin_unlock(&volume->reserve_lock); return err; } int volume_release(struct iet_volume *volume, u64 sid, int force) { int err = 0; struct reservation *reservation; if (!volume) return -ENOENT; reservation = &volume->reservation; spin_lock(&volume->reserve_lock); if (reservation->reservation_type == RESERVATION_TYPE_RESERVE && (force || reservation->sid == sid)) { reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->sid = 0; } else { err = -EBUSY; } spin_unlock(&volume->reserve_lock); return err; } static void iet_volume_info_show(struct seq_file *seq, struct iscsi_target *target) { struct iet_volume *volume; list_for_each_entry(volume, &target->volumes, list) { seq_printf(seq, "\tlun:%u state:%x iotype:%s", volume->lun, volume->l_state, volume->iotype->name); if (LUReadonly(volume)) seq_printf(seq, " iomode:ro"); else if (LUWCache(volume)) seq_printf(seq, " iomode:wb"); else seq_printf(seq, " iomode:wt"); seq_printf(seq, " blocks:%llu blocksize:%u", volume->blk_cnt, 1 << volume->blk_shift); if (volume->iotype->show) volume->iotype->show(volume, seq); else seq_printf(seq, "\n"); } } static int iet_volume_seq_open(struct inode *inode, struct file *file) { int res; res = seq_open(file, &iet_seq_op); if (!res) ((struct seq_file *)file->private_data)->private = iet_volume_info_show; return res; } struct file_operations volume_seq_fops = { .owner = THIS_MODULE, .open = iet_volume_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; iscsitarget-1.4.20.3+svn502/kernel/wthread.c000066400000000000000000000120061233224651300203730ustar00rootroot00000000000000/* * Worker thread. * (C) 2004 - 2005 FUJITA Tomonori * This code is licenced under the GPL. */ #include #include "iscsi.h" #include "iscsi_dbg.h" struct worker_thread_info *worker_thread_pool; void wthread_queue(struct iscsi_cmnd *cmnd) { struct worker_thread_info *info = cmnd->conn->session->target->wthread_info; if (!list_empty(&cmnd->list)) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); eprintk("%x %p %x %x %x %x %lx %x\n", cmnd_itt(cmnd), req, req->opcode, req->scb[0], cmnd->pdu.datasize, be32_to_cpu(req->data_length), cmnd->flags, req->flags); if (cmnd->lun) eprintk("%u\n", cmnd->lun->lun); assert(list_empty(&cmnd->list)); } spin_lock(&info->wthread_lock); list_add_tail(&cmnd->list, &info->work_queue); spin_unlock(&info->wthread_lock); atomic_inc(&cmnd->conn->nr_busy_cmnds); wake_up(&info->wthread_sleep); } static struct iscsi_cmnd * get_ready_cmnd(struct worker_thread_info *info) { struct iscsi_cmnd *cmnd = NULL; spin_lock(&info->wthread_lock); if (!list_empty(&info->work_queue)) { cmnd = list_entry(info->work_queue.next, struct iscsi_cmnd, list); list_del_init(&cmnd->list); assert(cmnd->conn); } spin_unlock(&info->wthread_lock); return cmnd; } static int cmnd_execute(struct iscsi_cmnd *cmnd) { int type = cmnd->conn->session->target->trgt_param.target_type; assert(target_type_array[type]->execute_cmnd); return target_type_array[type]->execute_cmnd(cmnd); } static int worker_thread(void *arg) { struct worker_thread *wt = (struct worker_thread *) arg; struct worker_thread_info *info = wt->w_info; struct iscsi_cmnd *cmnd; struct iscsi_conn *conn; DECLARE_WAITQUEUE(wait, current); if (current->io_context) put_io_context(current->io_context); if (!info->wthread_ioc) info->wthread_ioc = get_task_io_context(current, GFP_KERNEL, -1); ioc_task_link(info->wthread_ioc); current->io_context = info->wthread_ioc; add_wait_queue(&info->wthread_sleep, &wait); __set_current_state(TASK_RUNNING); do { while (!list_empty(&info->work_queue) && (cmnd = get_ready_cmnd(info))) { conn = cmnd->conn; if (cmnd_tmfabort(cmnd)) cmnd_release(cmnd, 1); else cmnd_execute(cmnd); assert(conn); atomic_dec(&conn->nr_busy_cmnds); } set_current_state(TASK_INTERRUPTIBLE); if (list_empty(&info->work_queue)) schedule(); __set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); remove_wait_queue(&info->wthread_sleep, &wait); return 0; } static int start_one_worker_thread(struct worker_thread_info *info, u32 tid) { struct worker_thread *wt; struct task_struct *task; if (!(wt = kmalloc(sizeof(struct worker_thread), GFP_KERNEL))) return -ENOMEM; wt->w_info = info; task = kthread_create(worker_thread, wt, "istiod%d", tid); if (IS_ERR(task)) { kfree(wt); return PTR_ERR(task); } wt->w_task = task; list_add(&wt->w_list, &info->wthread_list); info->nr_running_wthreads++; wake_up_process(task); return 0; } static int stop_one_worker_thread(struct worker_thread *wt) { struct worker_thread_info *info = wt->w_info; int err; assert(wt->w_task); err = kthread_stop(wt->w_task); if (err < 0 && err != -EINTR) return err; list_del(&wt->w_list); kfree(wt); info->nr_running_wthreads--; return 0; } int wthread_init(struct worker_thread_info *info) { spin_lock_init(&info->wthread_lock); info->nr_running_wthreads = 0; info->wthread_ioc = NULL; INIT_LIST_HEAD(&info->work_queue); INIT_LIST_HEAD(&info->wthread_list); init_waitqueue_head(&info->wthread_sleep); return 0; } int wthread_start(struct worker_thread_info *info, int wthreads, u32 tid) { int err = 0; while (info->nr_running_wthreads < wthreads) { if ((err = start_one_worker_thread(info, tid)) < 0) { eprintk("Fail to create a worker thread %d\n", err); goto out; } } while (info->nr_running_wthreads > wthreads) { struct worker_thread *wt; wt = list_entry(info->wthread_list.next, struct worker_thread, w_list); if ((err = stop_one_worker_thread(wt)) < 0) { eprintk("Fail to stop a worker thread %d\n", err); break; } } out: return err; } int wthread_stop(struct worker_thread_info *info) { struct worker_thread *wt, *tmp; int err = 0; list_for_each_entry_safe(wt, tmp, &info->wthread_list, w_list) { if ((err = stop_one_worker_thread(wt)) < 0) { eprintk("Fail to stop a worker thread %d\n", err); return err; } } return err; } int wthread_module_init() { int err; if (!worker_thread_pool_size) return 0; worker_thread_pool = kmalloc(sizeof(struct worker_thread_info), GFP_KERNEL); if (!worker_thread_pool) return -ENOMEM; wthread_init(worker_thread_pool); err = wthread_start(worker_thread_pool, worker_thread_pool_size, 0); if (err) { kfree(worker_thread_pool); worker_thread_pool = NULL; return err; } iprintk("iscsi_trgt using worker thread pool; size = %ld\n", worker_thread_pool_size); return 0; } void wthread_module_exit() { if (!worker_thread_pool_size) return; wthread_stop(worker_thread_pool); kfree(worker_thread_pool); } iscsitarget-1.4.20.3+svn502/patches/000077500000000000000000000000001233224651300167415ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/patches/compat-2.6.14-2.6.18.patch000066400000000000000000000170461233224651300225350ustar00rootroot00000000000000Index: kernel/iscsi.h =================================================================== --- 1/kernel/iscsi.h (revision 105) +++ 2/kernel/iscsi.h (working copy) @@ -260,8 +260,8 @@ struct iscsi_conn { u32 write_offset; int write_state; - struct hash_desc rx_hash; - struct hash_desc tx_hash; + struct crypto_tfm *rx_digest_tfm; + struct crypto_tfm *tx_digest_tfm; struct scatterlist hash_sg[ISCSI_CONN_IOV_MAX]; }; Index: kernel/digest.c =================================================================== --- 1/kernel/digest.c (revision 105) +++ 2/kernel/digest.c (working copy) @@ -12,8 +12,7 @@ void digest_alg_available(unsigned int *val) { - if (*val & DIGEST_CRC32C && - !crypto_has_alg("crc32c", 0, CRYPTO_ALG_ASYNC)) { + if (*val & DIGEST_CRC32C && !crypto_alg_available("crc32c", 0)) { printk("CRC32C digest algorithm not available in kernel\n"); *val |= ~DIGEST_CRC32C; } @@ -37,22 +36,15 @@ int digest_init(struct iscsi_conn *conn) if (!(conn->ddigest_type & DIGEST_ALL)) conn->ddigest_type = DIGEST_NONE; - if (conn->hdigest_type & DIGEST_CRC32C || - conn->ddigest_type & DIGEST_CRC32C) { - conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0, - CRYPTO_ALG_ASYNC); - conn->rx_hash.flags = 0; - if (IS_ERR(conn->rx_hash.tfm)) { - conn->rx_hash.tfm = NULL; + if (conn->hdigest_type & DIGEST_CRC32C || conn->ddigest_type & DIGEST_CRC32C) { + conn->rx_digest_tfm = crypto_alloc_tfm("crc32c", 0); + if (!conn->rx_digest_tfm) { err = -ENOMEM; goto out; } - conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, - CRYPTO_ALG_ASYNC); - conn->tx_hash.flags = 0; - if (IS_ERR(conn->tx_hash.tfm)) { - conn->tx_hash.tfm = NULL; + conn->tx_digest_tfm = crypto_alloc_tfm("crc32c", 0); + if (!conn->tx_digest_tfm) { err = -ENOMEM; goto out; } @@ -73,10 +65,10 @@ out: */ void digest_cleanup(struct iscsi_conn *conn) { - if (conn->tx_hash.tfm) - crypto_free_hash(conn->tx_hash.tfm); - if (conn->rx_hash.tfm) - crypto_free_hash(conn->rx_hash.tfm); + if (conn->tx_digest_tfm) + crypto_free_tfm(conn->tx_digest_tfm); + if (conn->rx_digest_tfm) + crypto_free_tfm(conn->rx_digest_tfm); } /** @@ -167,28 +159,28 @@ static inline void __dbg_simulate_data_d (sg).length = (l); \ } while (0) -static void digest_header(struct hash_desc *hash, struct iscsi_pdu *pdu, - u8 *crc) +static void digest_header(struct crypto_tfm *tfm, struct iscsi_pdu *pdu, u8 *crc) { struct scatterlist sg[2]; - unsigned int nbytes = sizeof(struct iscsi_hdr); + int i = 0; - SETSG(sg[0], &pdu->bhs, nbytes); + SETSG(sg[i], &pdu->bhs, sizeof(struct iscsi_hdr)); + i++; if (pdu->ahssize) { - SETSG(sg[1], pdu->ahs, pdu->ahssize); - nbytes += pdu->ahssize; + SETSG(sg[i], pdu->ahs, pdu->ahssize); + i++; } - crypto_hash_init(hash); - crypto_hash_update(hash, sg, nbytes); - crypto_hash_final(hash, crc); + crypto_digest_init(tfm); + crypto_digest_update(tfm, sg, i); + crypto_digest_final(tfm, crc); } int digest_rx_header(struct iscsi_cmnd *cmnd) { u32 crc; - digest_header(&cmnd->conn->rx_hash, &cmnd->pdu, (u8 *) &crc); + digest_header(cmnd->conn->rx_digest_tfm, &cmnd->pdu, (u8 *) &crc); if (crc != cmnd->hdigest) return -EIO; @@ -197,19 +189,18 @@ int digest_rx_header(struct iscsi_cmnd * void digest_tx_header(struct iscsi_cmnd *cmnd) { - digest_header(&cmnd->conn->tx_hash, &cmnd->pdu, (u8 *) &cmnd->hdigest); + digest_header(cmnd->conn->tx_digest_tfm, &cmnd->pdu, (u8 *) &cmnd->hdigest); } -static void digest_data(struct hash_desc *hash, struct iscsi_cmnd *cmnd, +static void digest_data(struct crypto_tfm *tfm, struct iscsi_cmnd *cmnd, struct tio *tio, u32 offset, u8 *crc) { struct scatterlist *sg = cmnd->conn->hash_sg; u32 size, length; int i, idx, count; - unsigned int nbytes; size = cmnd->pdu.datasize; - nbytes = size = (size + 3) & ~3; + size = (size + 3) & ~3; offset += tio->offset; idx = offset >> PAGE_CACHE_SHIFT; @@ -219,7 +210,7 @@ static void digest_data(struct hash_desc assert(count <= ISCSI_CONN_IOV_MAX); - crypto_hash_init(hash); + crypto_digest_init(tfm); for (i = 0; size; i++) { if (offset + size > PAGE_CACHE_SIZE) @@ -234,8 +225,8 @@ static void digest_data(struct hash_desc offset = 0; } - crypto_hash_update(hash, sg, nbytes); - crypto_hash_final(hash, crc); + crypto_digest_update(tfm, sg, count); + crypto_digest_final(tfm, crc); } int digest_rx_data(struct iscsi_cmnd *cmnd) @@ -261,10 +252,9 @@ int digest_rx_data(struct iscsi_cmnd *cm offset = 0; } - digest_data(&cmnd->conn->rx_hash, cmnd, tio, offset, (u8 *) &crc); + digest_data(cmnd->conn->rx_digest_tfm, cmnd, tio, offset, (u8 *) &crc); - if (!cmnd->conn->read_overflow && - (cmnd_opcode(cmnd) != ISCSI_OP_PDU_REJECT)) { + if (!cmnd->conn->read_overflow && (cmnd_opcode(cmnd) != ISCSI_OP_PDU_REJECT)) { if (crc != cmnd->ddigest) return -EIO; } @@ -278,6 +268,6 @@ void digest_tx_data(struct iscsi_cmnd *c struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; assert(tio); - digest_data(&cmnd->conn->tx_hash, cmnd, tio, + digest_data(cmnd->conn->tx_digest_tfm, cmnd, tio, be32_to_cpu(req->buffer_offset), (u8 *) &cmnd->ddigest); } Index: kernel/iscsi.c =================================================================== --- 1/kernel/iscsi.c (revision 105) +++ 2/kernel/iscsi.c (working copy) @@ -17,7 +17,7 @@ unsigned long debug_enable_flags; unsigned long worker_thread_pool_size; -static struct kmem_cache *iscsi_cmnd_cache; +static kmem_cache_t *iscsi_cmnd_cache; static u8 dummy_data[PAGE_SIZE]; static int ctr_major; Index: kernel/tio.c =================================================================== --- 1/kernel/tio.c (revision 105) +++ 2/kernel/tio.c (working copy) @@ -35,7 +35,7 @@ static int tio_add_pages(struct tio *tio return 0; } -static struct kmem_cache *tio_cache; +static kmem_cache_t *tio_cache; struct tio *tio_alloc(int count) { --- param/kernel/volume.c 2010-04-08 18:05:38.000000000 -0400 +++ param/kernel/volume.c.18 2010-04-08 18:05:38.000000000 -0400 @@ -84,28 +84,26 @@ static int set_scsisn(struct iet_volume /* Generate a MD5 hash of the target IQN and LUN number */ static void gen_scsiid(struct iet_volume *volume) { - struct hash_desc hash; + struct crypto_tfm *tfm; - hash.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); - hash.flags = 0; + tfm = crypto_alloc_tfm("md5", 0); - if (!IS_ERR(hash.tfm)) { + if (tfm) { struct scatterlist sg[2]; - unsigned int nbytes = 0; sg[0].page = virt_to_page(volume->target->name); sg[0].offset = offset_in_page(volume->target->name); - nbytes += sg[0].length = strlen(volume->target->name); + sg[0].length = strlen(volume->target->name); sg[1].page = virt_to_page(&volume->lun); sg[1].offset = offset_in_page(&volume->lun); - nbytes += sg[1].length = sizeof(volume->lun); + sg[1].length = sizeof(volume->lun); - crypto_hash_init(&hash); - crypto_hash_update(&hash, sg, nbytes); - crypto_hash_final(&hash, volume->scsi_id); + crypto_digest_init(tfm); + crypto_digest_update(tfm, sg, 2); + crypto_digest_final(tfm, volume->scsi_id); - crypto_free_hash(hash.tfm); + crypto_free_tfm(tfm); } else { /* If no MD5 available set ID to TID and LUN */ memcpy(volume->scsi_id, &volume->target->tid, --- ioc/kernel/wthread.c 2011-04-12 19:50:49.000000000 -0400 +++ ioc/kernel/wthread.c 2011-04-12 19:51:29.000000000 -0400 @@ -68,7 +68,7 @@ static int worker_thread(void *arg) DECLARE_WAITQUEUE(wait, current); if (!current->io_context) - get_io_context(GFP_KERNEL, -1); + get_io_context(GFP_KERNEL); add_wait_queue(&info->wthread_sleep, &wait); iscsitarget-1.4.20.3+svn502/patches/compat-2.6.19-2.6.21.patch000066400000000000000000000032751233224651300225330ustar00rootroot00000000000000Index: kernel/event.c =================================================================== --- 1/kernel/event.c (working copy) +++ 2/kernel/event.c (revision 122) @@ -95,8 +95,7 @@ int event_send(u32 tid, u64 sid, u32 cid int event_init(void) { - nl = netlink_kernel_create(NETLINK_IET, 1, event_recv, NULL, - THIS_MODULE); + nl = netlink_kernel_create(NETLINK_IET, 1, event_recv, THIS_MODULE); if (!nl) return -ENOMEM; else Index: kernel/iscsi.c =================================================================== --- 1/kernel/iscsi.c (working copy) +++ 2/kernel/iscsi.c (revision 137) @@ -1942,7 +1942,8 @@ static int iscsi_init(void) if ((err = event_init()) < 0) goto err; - iscsi_cmnd_cache = KMEM_CACHE(iscsi_cmnd, 0); + iscsi_cmnd_cache = kmem_cache_create("iscsi_cmnd", sizeof(struct iscsi_cmnd), + 0, 0, NULL, NULL); if (!iscsi_cmnd_cache) goto err; Index: kernel/tio.c =================================================================== --- 1/kernel/tio.c (working copy) +++ 2/kernel/tio.c (revision 137) @@ -114,7 +114,8 @@ int tio_sync(struct iet_volume *lu, stru int tio_init(void) { - tio_cache = KMEM_CACHE(tio, 0); + tio_cache = kmem_cache_create("tio", sizeof(struct tio), + 0, 0, NULL, NULL); return tio_cache ? 0 : -ENOMEM; } Index: kernel/ua.c =================================================================== --- 1/kernel/ua.c +++ 2/kernel/ua.c @@ -18,7 +18,8 @@ static struct kmem_cache *ua_cache; int ua_init(void) { - ua_cache = KMEM_CACHE(ua_entry, 0); + ua_cache = kmem_cache_create("iet_ua_cache", sizeof(struct ua_entry), + 0, 0, NULL, NULL); if (!ua_cache) { eprintk("%s", "Failed to create ua cache\n"); return -ENOMEM; iscsitarget-1.4.20.3+svn502/patches/compat-2.6.22.patch000066400000000000000000000050451233224651300217760ustar00rootroot00000000000000Index: kernel/volume.c =================================================================== --- a/kernel/volume.c (revision 205) +++ b/kernel/volume.c (working copy) @@ -11,6 +11,7 @@ #include "iscsi.h" #include "iscsi_dbg.h" #include "iotype.h" +#include "seq_list.h" struct iet_volume *volume_lookup(struct iscsi_target *target, u32 lun) { Index: kernel/seq_list.c =================================================================== --- a/kernel/seq_list.c (revision 0) +++ b/kernel/seq_list.c (revision 0) @@ -0,0 +1,34 @@ +/* + * seq_list_* API lifted from the kernel's seq_file.c for backward compatibility + * with kernels < 2.6.23 + */ + +#include + +struct list_head *seq_list_start(struct list_head *head, loff_t pos) +{ + struct list_head *lh; + + list_for_each(lh, head) + if (pos-- == 0) + return lh; + + return NULL; +} + +struct list_head *seq_list_start_head(struct list_head *head, loff_t pos) +{ + if (!pos) + return head; + + return seq_list_start(head, pos - 1); +} + +struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos) +{ + struct list_head *lh; + + lh = ((struct list_head *)v)->next; + ++*ppos; + return lh == head ? NULL : lh; +} Index: kernel/target.c =================================================================== --- a/kernel/target.c (revision 205) +++ b/kernel/target.c (working copy) @@ -9,6 +9,7 @@ #include "iscsi.h" #include "digest.h" #include "iscsi_dbg.h" +#include "seq_list.h" #define MAX_NR_TARGETS (1UL << 30) Index: kernel/seq_list.h =================================================================== --- a/kernel/seq_list.h (revision 0) +++ b/kernel/seq_list.h (revision 0) @@ -0,0 +1,16 @@ +/* + * seq_list_* API lifted from the kernel's seq_file.c for backward compatibility + * with kernels < 2.6.23 + */ + +#ifndef __SEQ_LIST_H +#define __SEQ_LIST_H + +#include +#include + +struct list_head *seq_list_start(struct list_head *head, loff_t pos); +struct list_head *seq_list_start_head(struct list_head *head, loff_t pos); +struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos); + +#endif Index: kernel/Makefile =================================================================== --- a/kernel/Makefile (revision 205) +++ b/kernel/Makefile (working copy) @@ -13,5 +13,5 @@ obj-m += iscsi_trgt.o iscsi_trgt-objs := tio.o iscsi.o nthread.o wthread.o config.o digest.o \ conn.o session.o target.o volume.o iotype.o \ file-io.o null-io.o target_disk.o event.o param.o \ - block-io.o ua.o + block-io.o ua.o seq_list.o iscsitarget-1.4.20.3+svn502/patches/compat-2.6.23.patch000066400000000000000000000115351233224651300220000ustar00rootroot00000000000000Index: kernel/block-io.c =================================================================== --- 1/kernel/block-io.c (working copy) +++ 2/kernel/block-io.c (revision 145) @@ -29,10 +29,15 @@ struct tio_work { struct completion tio_complete; }; -static void blockio_bio_endio(struct bio *bio, int error) +static int +blockio_bio_endio(struct bio *bio, unsigned int bytes_done, int error) { struct tio_work *tio_work = bio->bi_private; + /* Ignore partials */ + if (bio->bi_size) + return 1; + error = test_bit(BIO_UPTODATE, &bio->bi_flags) ? error : -EIO; if (error) @@ -43,6 +48,8 @@ static void blockio_bio_endio(struct bio complete(&tio_work->tio_complete); bio_put(bio); + + return 0; } /* Index: kernel/config.c =================================================================== --- 1/kernel/config.c (working copy) +++ 2/kernel/config.c (revision 145) @@ -42,7 +42,7 @@ int iet_procfs_init(void) int i; struct proc_dir_entry *ent; - if (!(proc_iet_dir = proc_mkdir("iet", init_net.proc_net))) + if (!(proc_iet_dir = proc_mkdir("net/iet", 0))) goto err; proc_iet_dir->owner = THIS_MODULE; Index: kernel/digest.c =================================================================== --- 1/kernel/digest.c (working copy) +++ 2/kernel/digest.c (revision 145) @@ -160,17 +160,22 @@ static inline void __dbg_simulate_data_d } } +/* Copied from linux-iscsi initiator and slightly adjusted */ +#define SETSG(sg, p, l) do { \ + (sg).page = virt_to_page((p)); \ + (sg).offset = ((unsigned long)(p) & ~PAGE_CACHE_MASK); \ + (sg).length = (l); \ +} while (0) + static void digest_header(struct hash_desc *hash, struct iscsi_pdu *pdu, u8 *crc) { struct scatterlist sg[2]; unsigned int nbytes = sizeof(struct iscsi_hdr); - sg_init_table(sg, pdu->ahssize ? 2 : 1); - - sg_set_buf(&sg[0], &pdu->bhs, nbytes); + SETSG(sg[0], &pdu->bhs, nbytes); if (pdu->ahssize) { - sg_set_buf(&sg[1], pdu->ahs, pdu->ahssize); + SETSG(sg[1], pdu->ahs, pdu->ahssize); nbytes += pdu->ahssize; } @@ -214,7 +219,6 @@ static void digest_data(struct hash_desc assert(count <= ISCSI_CONN_IOV_MAX); - sg_init_table(sg, ARRAY_SIZE(cmnd->conn->hash_sg)); crypto_hash_init(hash); for (i = 0; size; i++) { @@ -223,13 +227,13 @@ static void digest_data(struct hash_desc else length = size; - sg_set_page(&sg[i], tio->pvec[idx + i], length, offset); + sg[i].page = tio->pvec[idx + i]; + sg[i].offset = offset; + sg[i].length = length; size -= length; offset = 0; } - sg_mark_end(&sg[i - 1]); - crypto_hash_update(hash, sg, nbytes); crypto_hash_final(hash, crc); } Index: kernel/event.c =================================================================== --- 1/kernel/event.c (working copy) +++ 2/kernel/event.c (revision 145) @@ -28,7 +28,7 @@ static int event_recv_msg(struct sk_buff return 0; } -static void event_recv_skb(struct sk_buff *skb) +static int event_recv_skb(struct sk_buff *skb) { int err; struct nlmsghdr *nlh; @@ -37,7 +37,7 @@ static void event_recv_skb(struct sk_buf while (skb->len >= NLMSG_SPACE(0)) { nlh = (struct nlmsghdr *)skb->data; if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) - break; + return 0; rlen = NLMSG_ALIGN(nlh->nlmsg_len); if (rlen > skb->len) rlen = skb->len; @@ -47,6 +47,19 @@ static void event_recv_skb(struct sk_buf netlink_ack(skb, nlh, 0); skb_pull(skb, rlen); } + return 0; +} + +static void event_recv(struct sock *sk, int length) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + if (event_recv_skb(skb) && skb->len) + skb_queue_head(&sk->sk_receive_queue, skb); + else + kfree_skb(skb); + } } static int notify(void *data, int len, int gfp_mask) @@ -82,8 +95,8 @@ int event_send(u32 tid, u64 sid, u32 cid int event_init(void) { - nl = netlink_kernel_create(&init_net, NETLINK_IET, 1, event_recv_skb, - NULL, THIS_MODULE); + nl = netlink_kernel_create(NETLINK_IET, 1, event_recv, NULL, + THIS_MODULE); if (!nl) return -ENOMEM; else --- param/kernel/volume.c 2010-03-03 13:41:36.000000000 -0500 +++ param/kernel/volume.c 2010-03-03 13:42:04.000000000 -0500 @@ -92,14 +92,13 @@ static void gen_scsiid(struct iet_volume struct scatterlist sg[2]; unsigned int nbytes = 0; - sg_init_table(sg, 2); - - sg_set_buf(&sg[0], volume->target->name, - strlen(volume->target->name)); - nbytes += strlen(volume->target->name); - - sg_set_buf(&sg[1], &volume->lun, sizeof(volume->lun)); - nbytes += sizeof(volume->lun); + sg[0].page = virt_to_page(volume->target->name); + sg[0].offset = offset_in_page(volume->target->name); + nbytes += sg[0].length = strlen(volume->target->name); + + sg[1].page = virt_to_page(&volume->lun); + sg[1].offset = offset_in_page(&volume->lun); + nbytes += sg[1].length = sizeof(volume->lun); crypto_hash_init(&hash); crypto_hash_update(&hash, sg, nbytes); iscsitarget-1.4.20.3+svn502/patches/compat-2.6.24.patch000066400000000000000000000024731233224651300220020ustar00rootroot00000000000000diff --git a/kernel/event.c b/kernel/event.c index 240404d..e45ed67 100644 --- a/kernel/event.c +++ b/kernel/event.c @@ -92,5 +92,6 @@ int event_init(void) void event_exit(void) { - netlink_kernel_release(nl); + if (nl) + sock_release(nl->sk_socket); } --- ioc/kernel/iscsi.h 2011-04-12 17:31:46.000000000 -0400 +++ ioc/kernel/iscsi.h 2011-04-12 17:46:06.000000000 -0400 @@ -94,8 +94,6 @@ struct worker_thread_info { struct list_head work_queue; wait_queue_head_t wthread_sleep; - - struct io_context *wthread_ioc; }; struct iscsi_cmnd; --- ioc/kernel/wthread.c 2011-04-12 19:49:26.000000000 -0400 +++ ioc/kernel/wthread.c 2011-04-12 19:50:49.000000000 -0400 @@ -67,11 +67,8 @@ static int worker_thread(void *arg) struct iscsi_conn *conn; DECLARE_WAITQUEUE(wait, current); - if (current->io_context) - put_io_context(current->io_context); - - if (!(current->io_context = ioc_task_link(info->wthread_ioc))) - info->wthread_ioc = get_io_context(GFP_KERNEL, -1); + if (!current->io_context) + get_io_context(GFP_KERNEL, -1); add_wait_queue(&info->wthread_sleep, &wait); @@ -147,7 +144,6 @@ int wthread_init(struct worker_thread_in spin_lock_init(&info->wthread_lock); info->nr_running_wthreads = 0; - info->wthread_ioc = NULL; INIT_LIST_HEAD(&info->work_queue); INIT_LIST_HEAD(&info->wthread_list); iscsitarget-1.4.20.3+svn502/patches/compat-2.6.25-2.6.27.patch000066400000000000000000000021031233224651300225230ustar00rootroot00000000000000diff --git a/kernel/block-io.c b/kernel/block-io.c index 708f101..e4a25f7 100644 --- a/kernel/block-io.c +++ b/kernel/block-io.c @@ -155,14 +155,14 @@ blockio_open_path(struct iet_volume *volume, const char *path) { struct blockio_data *bio_data = volume->private; struct block_device *bdev; - int flags = FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); + int flags = LUReadonly(volume) ? MS_RDONLY : 0; int err = 0; bio_data->path = kstrdup(path, GFP_KERNEL); if (!bio_data->path) return -ENOMEM; - bdev = open_bdev_exclusive(path, flags, THIS_MODULE); + bdev = open_bdev_excl(path, flags, THIS_MODULE); if (IS_ERR(bdev)) { err = PTR_ERR(bdev); eprintk("Can't open device %s, error %d\n", path, err); @@ -250,10 +250,9 @@ static void blockio_detach(struct iet_volume *volume) { struct blockio_data *bio_data = volume->private; - int flags = FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); if (bio_data->bdev) - close_bdev_exclusive(bio_data->bdev, flags); + close_bdev_excl(bio_data->bdev); kfree(bio_data->path); kfree(volume->private); iscsitarget-1.4.20.3+svn502/patches/compat-2.6.28.patch000066400000000000000000000007701233224651300220040ustar00rootroot00000000000000diff --git a/kernel/conn.c b/kernel/conn.c index 09b4c0c..a5c0228 100644 --- a/kernel/conn.c +++ b/kernel/conn.c @@ -47,8 +47,9 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session) "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr)); break; case AF_INET6: - snprintf(buf, sizeof(buf), "[%pI6]", - &inet6_sk(sk)->daddr); + snprintf(buf, sizeof(buf), + "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", + NIP6(inet6_sk(sk)->daddr)); break; default: break; iscsitarget-1.4.20.3+svn502/patches/compat-2.6.29.patch000066400000000000000000000006431233224651300220040ustar00rootroot00000000000000diff --git a/kernel/config.c b/kernel/config.c index 51331fb..9e479c0 100644 --- a/kernel/config.c +++ b/kernel/config.c @@ -45,6 +45,8 @@ int iet_procfs_init(void) if (!(proc_iet_dir = proc_mkdir("iet", init_net.proc_net))) goto err; + proc_iet_dir->owner = THIS_MODULE; + for (i = 0; i < ARRAY_SIZE(iet_proc_entries); i++) { ent = create_proc_entry(iet_proc_entries[i].name, 0, proc_iet_dir); if (ent) iscsitarget-1.4.20.3+svn502/patches/compat-2.6.30.patch000066400000000000000000000010351233224651300217700ustar00rootroot00000000000000--- sector2/kernel/block-io.c 2010-03-01 11:44:57.000000000 -0500 +++ sector2/kernel/block-io.c.30 2010-03-01 11:50:00.000000000 -0500 @@ -280,7 +280,7 @@ blockio_attach(struct iet_volume *volume err = parse_blockio_params(volume, args); if (!err) { /* see Documentation/ABI/testing/sysfs-block */ - unsigned bsz = bdev_logical_block_size(bio_data->bdev); + unsigned bsz = bdev_hardsect_size(bio_data->bdev); if (!volume->blk_shift) volume->blk_shift = blksize_bits(bsz); else if (volume->blk_shift < blksize_bits(bsz)) { iscsitarget-1.4.20.3+svn502/patches/compat-2.6.31.patch000066400000000000000000000006221233224651300217720ustar00rootroot00000000000000--- sync/kernel/file-io.c 2009-12-10 17:43:36.000000000 -0500 +++ trunk/kernel/file-io.c 2008-12-04 17:35:21.000000000 -0500 @@ -89,7 +89,7 @@ count = lu->blk_cnt << lu->blk_shift; } - res = filemap_write_and_wait_range(mapping, ppos, ppos + count - 1); + res = sync_page_range(inode, mapping, ppos, count); if (res) { eprintk("I/O error: syncing pages failed: %d\n", res); return -EIO; iscsitarget-1.4.20.3+svn502/patches/compat-2.6.32.patch000066400000000000000000000007111233224651300217720ustar00rootroot00000000000000diff --git b/kernel/conn.c a/kernel/conn.c index f179db5..4b53acd 100644 --- b/kernel/conn.c +++ a/kernel/conn.c @@ -44,7 +44,7 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session) switch (sk->sk_family) { case AF_INET: snprintf(buf, sizeof(buf), - "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->inet_daddr)); + "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr)); break; case AF_INET6: snprintf(buf, sizeof(buf), "[%pI6]", iscsitarget-1.4.20.3+svn502/patches/compat-2.6.33-2.6.35.patch000066400000000000000000000006771233224651300225370ustar00rootroot00000000000000diff --git b/kernel/conn.c a/kernel/conn.c index c7b8ea1..ec6dada 100644 --- b/kernel/conn.c +++ a/kernel/conn.c @@ -44,7 +44,7 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session) switch (sk->sk_family) { case AF_INET: snprintf(buf, sizeof(buf), - "%pI4", &inet_sk(sk)->inet_daddr); + "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->inet_daddr)); break; case AF_INET6: snprintf(buf, sizeof(buf), "[%pI6]", iscsitarget-1.4.20.3+svn502/patches/compat-2.6.36-2.6.37.patch000066400000000000000000000022701233224651300225330ustar00rootroot00000000000000diff --git b/kernel/block-io.c a/kernel/block-io.c index 4c6d8bd..c812abf 100644 --- b/kernel/block-io.c +++ a/kernel/block-io.c @@ -155,14 +155,14 @@ blockio_open_path(struct iet_volume *volume, const char *path) { struct blockio_data *bio_data = volume->private; struct block_device *bdev; - int flags = FMODE_EXCL | FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); + int flags = FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); int err = 0; bio_data->path = kstrdup(path, GFP_KERNEL); if (!bio_data->path) return -ENOMEM; - bdev = blkdev_get_by_path(path, flags, THIS_MODULE); + bdev = open_bdev_exclusive(path, flags, THIS_MODULE); if (IS_ERR(bdev)) { err = PTR_ERR(bdev); eprintk("Can't open device %s, error %d\n", path, err); @@ -250,10 +250,10 @@ static void blockio_detach(struct iet_volume *volume) { struct blockio_data *bio_data = volume->private; - int flags = FMODE_EXCL | FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); + int flags = FMODE_READ | (LUReadonly(volume) ? 0 : FMODE_WRITE); if (bio_data->bdev) - blkdev_put(bio_data->bdev, flags); + close_bdev_exclusive(bio_data->bdev, flags); kfree(bio_data->path); kfree(volume->private); iscsitarget-1.4.20.3+svn502/patches/compat-2.6.38.patch000066400000000000000000000023651233224651300220070ustar00rootroot00000000000000diff --git b/kernel/block-io.c a/kernel/block-io.c index e24f1ca..4c6d8bd 100644 --- b/kernel/block-io.c +++ a/kernel/block-io.c @@ -56,7 +56,6 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw) struct request_queue *bdev_q = bdev_get_queue(bio_data->bdev); struct tio_work *tio_work; struct bio *tio_bio = NULL, *bio = NULL, *biotail = NULL; - struct blk_plug plug; u32 offset = tio->offset; u32 size = tio->size; @@ -119,8 +118,6 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw) } } - blk_start_plug(&plug); - /* Walk the list, submitting bios 1 by 1 */ while (tio_bio) { bio = tio_bio; @@ -130,7 +127,8 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw) submit_bio(rw, bio); } - blk_finish_plug(&plug); + if (bdev_q && bdev_q->unplug_fn) + bdev_q->unplug_fn(bdev_q); wait_for_completion(&tio_work->tio_complete); diff --git b/kernel/iotype.c a/kernel/iotype.c index 9fd69fa..b3d6117 100644 --- b/kernel/iotype.c +++ a/kernel/iotype.c @@ -9,7 +9,7 @@ #include "iscsi_dbg.h" static LIST_HEAD(iotypes); -static DEFINE_RWLOCK(iotypes_lock); +static rwlock_t iotypes_lock = RW_LOCK_UNLOCKED; static struct iotype *find_iotype(const char *name) { iscsitarget-1.4.20.3+svn502/patches/compat-2.6.39-3.2.patch000066400000000000000000000007151233224651300223050ustar00rootroot00000000000000diff --git b/kernel/wthread.c a/kernel/wthread.c index 940de9d..f7c8b1d 100644 --- b/kernel/wthread.c +++ a/kernel/wthread.c @@ -71,7 +71,7 @@ static int worker_thread(void *arg) put_io_context(current->io_context); if (!(current->io_context = ioc_task_link(info->wthread_ioc))) - info->wthread_ioc = get_task_io_context(current, GFP_KERNEL, -1); + info->wthread_ioc = get_io_context(GFP_KERNEL, -1); add_wait_queue(&info->wthread_sleep, &wait); iscsitarget-1.4.20.3+svn502/patches/compat-3.12.patch000066400000000000000000000006201233224651300216240ustar00rootroot00000000000000Index: iscsitarget-code/kernel/conn.c =================================================================== --- iscsitarget-code.orig/kernel/conn.c +++ iscsitarget-code/kernel/conn.c @@ -48,7 +48,7 @@ void conn_info_show(struct seq_file *seq break; case AF_INET6: snprintf(buf, sizeof(buf), "[%pI6]", - &(sk)->sk_v6_daddr); + &inet6_sk(sk)->daddr); break; default: break; iscsitarget-1.4.20.3+svn502/patches/compat-3.13.patch000066400000000000000000000015511233224651300216310ustar00rootroot00000000000000diff --git b/Makefile a/Makefile index fc7de9a..78d0596 100644 --- b/Makefile +++ a/Makefile @@ -66,10 +66,6 @@ kver_lk = $(shell [ `echo $(KVER) | egrep $(1)` ] && echo 1 || echo 0) # base first the earlier patch sets will not need to be modified. # -ifeq ($(call kver_lt,3,14,0),1) - PATCHES := $(PATCHES) compat-3.13.patch -endif - ifeq ($(call kver_lt,3,13,0),1) PATCHES := $(PATCHES) compat-3.12.patch endif diff --git b/kernel/block-io.c a/kernel/block-io.c index 00365b5..7c9ea21 100644 --- b/kernel/block-io.c +++ a/kernel/block-io.c @@ -87,7 +87,7 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw) } /* bi_sector is ALWAYS in units of 512 bytes */ - bio->bi_iter.bi_sector = ppos >> 9; + bio->bi_sector = ppos >> 9; bio->bi_bdev = bio_data->bdev; bio->bi_end_io = blockio_bio_endio; bio->bi_private = tio_work; iscsitarget-1.4.20.3+svn502/patches/compat-3.2-3.4.patch000066400000000000000000000012521233224651300220470ustar00rootroot00000000000000diff --git b/kernel/wthread.c a/kernel/wthread.c index 55d7321..940de9d 100644 --- b/kernel/wthread.c +++ a/kernel/wthread.c @@ -65,16 +65,13 @@ static int worker_thread(void *arg) struct worker_thread_info *info = wt->w_info; struct iscsi_cmnd *cmnd; struct iscsi_conn *conn; - DECLARE_WAITQUEUE(wait, current); if (current->io_context) put_io_context(current->io_context); - if (!info->wthread_ioc) + if (!(current->io_context = ioc_task_link(info->wthread_ioc))) info->wthread_ioc = get_task_io_context(current, GFP_KERNEL, -1); - ioc_task_link(info->wthread_ioc); - current->io_context = info->wthread_ioc; add_wait_queue(&info->wthread_sleep, &wait); iscsitarget-1.4.20.3+svn502/patches/compat-3.5.patch000066400000000000000000000011131233224651300215440ustar00rootroot00000000000000diff --git b/kernel/event.c a/kernel/event.c index 1c5f3a4..0266101 100644 --- b/kernel/event.c +++ a/kernel/event.c @@ -68,17 +68,8 @@ int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic) int event_init(void) { - struct netlink_kernel_cfg cfg = { - .groups = 1, - .input = event_recv_skb, - .cb_mutex = NULL, - .bind = NULL, - }; - - nl = netlink_kernel_create(&init_net, - NETLINK_IET, - THIS_MODULE, - &cfg); + nl = netlink_kernel_create(&init_net, NETLINK_IET, 1, event_recv_skb, + NULL, THIS_MODULE); if (!nl) return -ENOMEM; else iscsitarget-1.4.20.3+svn502/patches/compat-3.6.patch000066400000000000000000000011321233224651300215460ustar00rootroot00000000000000diff --git b/kernel/event.c a/kernel/event.c index ef4adcb..024c1be 100644 --- b/kernel/event.c +++ a/kernel/event.c @@ -26,7 +26,7 @@ static void event_recv_skb(struct sk_buff *skb) rlen = NLMSG_ALIGN(nlh->nlmsg_len); if (rlen > skb->len) rlen = skb->len; - ietd_pid = NETLINK_CB(skb).portid; + ietd_pid = NETLINK_CB(skb).pid; WARN_ON(ietd_pid == 0); if (nlh->nlmsg_flags & NLM_F_ACK) netlink_ack(skb, nlh, 0); @@ -78,6 +78,7 @@ int event_init(void) nl = netlink_kernel_create(&init_net, NETLINK_IET, + THIS_MODULE, &cfg); if (!nl) return -ENOMEM; iscsitarget-1.4.20.3+svn502/patches/compat-3.9.patch000066400000000000000000000014531233224651300215570ustar00rootroot00000000000000diff --git b/kernel/config.c a/kernel/config.c index cd1d2f6..672a1e3 100644 --- b/kernel/config.c +++ a/kernel/config.c @@ -34,7 +34,7 @@ void iet_procfs_exit(void) for (i = 0; i < ARRAY_SIZE(iet_proc_entries); i++) remove_proc_entry(iet_proc_entries[i].name, proc_iet_dir); - proc_remove(proc_iet_dir); + remove_proc_entry(proc_iet_dir->name, proc_iet_dir->parent); } int iet_procfs_init(void) @@ -46,9 +46,10 @@ int iet_procfs_init(void) goto err; for (i = 0; i < ARRAY_SIZE(iet_proc_entries); i++) { - ent = proc_create(iet_proc_entries[i].name, 0, proc_iet_dir, - iet_proc_entries[i].fops); - if (!ent) + ent = create_proc_entry(iet_proc_entries[i].name, 0, proc_iet_dir); + if (ent) + ent->proc_fops = iet_proc_entries[i].fops; + else goto err; } iscsitarget-1.4.20.3+svn502/patches/compat-rhel4.patch000066400000000000000000000021551233224651300222640ustar00rootroot00000000000000--- 1/kernel/config.c +++ 2/kernel/config.c @@ -216,7 +216,7 @@ static int add_target(unsigned long ptr) return err; } -static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct iscsi_target *target = NULL; long err; @@ -311,6 +315,5 @@ done: struct file_operations ctr_fops = { .owner = THIS_MODULE, - .unlocked_ioctl = ioctl, - .compat_ioctl = ioctl, + .ioctl = ioctl, }; --- 1/kernel/event.c +++ 2/kernel/event.c @@ -71,7 +72,7 @@ static int notify(void *data, int len, i if (!(skb = alloc_skb(NLMSG_SPACE(len), gfp_mask))) return -ENOMEM; - nlh = __nlmsg_put(skb, ietd_pid, seq++, NLMSG_DONE, len - sizeof(*nlh), 0); + nlh = __nlmsg_put(skb, ietd_pid, seq++, NLMSG_DONE, len - sizeof(*nlh)); memcpy(NLMSG_DATA(nlh), data, len); @@ -95,7 +100,7 @@ int event_send(u32 tid, u64 sid, u32 cid int event_init(void) { - nl = netlink_kernel_create(NETLINK_IET, 1, event_recv, THIS_MODULE); + nl = netlink_kernel_create(NETLINK_IET, event_recv); if (!nl) return -ENOMEM; else iscsitarget-1.4.20.3+svn502/patches/compat-sles10sp2.patch000066400000000000000000000123341233224651300230020ustar00rootroot00000000000000Index: kernel/iscsi.h =================================================================== --- 1/kernel/iscsi.h.p 2008-11-11 12:00:45.000000000 -0500 +++ 2/kernel/iscsi.h 2008-11-11 12:00:59.000000000 -0500 @@ -241,8 +241,8 @@ struct iscsi_conn { u32 write_offset; int write_state; - struct crypto_tfm *rx_digest_tfm; - struct crypto_tfm *tx_digest_tfm; + struct hash_desc rx_hash; + struct hash_desc tx_hash; struct scatterlist hash_sg[ISCSI_CONN_IOV_MAX]; }; Index: kernel/digest.c =================================================================== --- 1/kernel/digest.c.p 2008-11-11 12:00:53.000000000 -0500 +++ 2/kernel/digest.c 2008-11-11 12:01:38.000000000 -0500 @@ -12,7 +12,8 @@ void digest_alg_available(unsigned int *val) { - if (*val & DIGEST_CRC32C && !crypto_alg_available("crc32c", 0)) { + if (*val & DIGEST_CRC32C && + !crypto_has_alg("crc32c", 0, CRYPTO_ALG_ASYNC)) { printk("CRC32C digest algorithm not available in kernel\n"); *val |= ~DIGEST_CRC32C; } @@ -36,15 +37,22 @@ int digest_init(struct iscsi_conn *conn) if (!(conn->ddigest_type & DIGEST_ALL)) conn->ddigest_type = DIGEST_NONE; - if (conn->hdigest_type & DIGEST_CRC32C || conn->ddigest_type & DIGEST_CRC32C) { - conn->rx_digest_tfm = crypto_alloc_tfm("crc32c", 0); - if (!conn->rx_digest_tfm) { + if (conn->hdigest_type & DIGEST_CRC32C || + conn->ddigest_type & DIGEST_CRC32C) { + conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0, + CRYPTO_ALG_ASYNC); + conn->rx_hash.flags = 0; + if (IS_ERR(conn->rx_hash.tfm)) { + conn->rx_hash.tfm = NULL; err = -ENOMEM; goto out; } - conn->tx_digest_tfm = crypto_alloc_tfm("crc32c", 0); - if (!conn->tx_digest_tfm) { + conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, + CRYPTO_ALG_ASYNC); + conn->tx_hash.flags = 0; + if (IS_ERR(conn->tx_hash.tfm)) { + conn->tx_hash.tfm = NULL; err = -ENOMEM; goto out; } @@ -65,10 +73,10 @@ out: */ void digest_cleanup(struct iscsi_conn *conn) { - if (conn->tx_digest_tfm) - crypto_free_tfm(conn->tx_digest_tfm); - if (conn->rx_digest_tfm) - crypto_free_tfm(conn->rx_digest_tfm); + if (conn->tx_hash.tfm) + crypto_free_hash(conn->tx_hash.tfm); + if (conn->rx_hash.tfm) + crypto_free_hash(conn->rx_hash.tfm); } /** @@ -159,28 +167,28 @@ static inline void __dbg_simulate_data_d (sg).length = (l); \ } while (0) -static void digest_header(struct crypto_tfm *tfm, struct iscsi_pdu *pdu, u8 *crc) +static void digest_header(struct hash_desc *hash, struct iscsi_pdu *pdu, + u8 *crc) { struct scatterlist sg[2]; - int i = 0; + unsigned int nbytes = sizeof(struct iscsi_hdr); - SETSG(sg[i], &pdu->bhs, sizeof(struct iscsi_hdr)); - i++; + SETSG(sg[0], &pdu->bhs, nbytes); if (pdu->ahssize) { - SETSG(sg[i], pdu->ahs, pdu->ahssize); - i++; + SETSG(sg[1], pdu->ahs, pdu->ahssize); + nbytes += pdu->ahssize; } - crypto_digest_init(tfm); - crypto_digest_update(tfm, sg, i); - crypto_digest_final(tfm, crc); + crypto_hash_init(hash); + crypto_hash_update(hash, sg, nbytes); + crypto_hash_final(hash, crc); } int digest_rx_header(struct iscsi_cmnd *cmnd) { u32 crc; - digest_header(cmnd->conn->rx_digest_tfm, &cmnd->pdu, (u8 *) &crc); + digest_header(&cmnd->conn->rx_hash, &cmnd->pdu, (u8 *) &crc); if (crc != cmnd->hdigest) return -EIO; @@ -189,18 +197,19 @@ int digest_rx_header(struct iscsi_cmnd * void digest_tx_header(struct iscsi_cmnd *cmnd) { - digest_header(cmnd->conn->tx_digest_tfm, &cmnd->pdu, (u8 *) &cmnd->hdigest); + digest_header(&cmnd->conn->tx_hash, &cmnd->pdu, (u8 *) &cmnd->hdigest); } -static void digest_data(struct crypto_tfm *tfm, struct iscsi_cmnd *cmnd, +static void digest_data(struct hash_desc *hash, struct iscsi_cmnd *cmnd, struct tio *tio, u32 offset, u8 *crc) { struct scatterlist *sg = cmnd->conn->hash_sg; u32 size, length; int i, idx, count; + unsigned int nbytes; size = cmnd->pdu.datasize; - size = (size + 3) & ~3; + nbytes = size = (size + 3) & ~3; offset += tio->offset; idx = offset >> PAGE_CACHE_SHIFT; @@ -210,7 +219,7 @@ static void digest_data(struct crypto_tf assert(count <= ISCSI_CONN_IOV_MAX); - crypto_digest_init(tfm); + crypto_hash_init(hash); for (i = 0; size; i++) { if (offset + size > PAGE_CACHE_SIZE) @@ -225,8 +234,8 @@ static void digest_data(struct crypto_tf offset = 0; } - crypto_digest_update(tfm, sg, count); - crypto_digest_final(tfm, crc); + crypto_hash_update(hash, sg, nbytes); + crypto_hash_final(hash, crc); } int digest_rx_data(struct iscsi_cmnd *cmnd) @@ -252,9 +261,10 @@ int digest_rx_data(struct iscsi_cmnd *cm offset = 0; } - digest_data(cmnd->conn->rx_digest_tfm, cmnd, tio, offset, (u8 *) &crc); + digest_data(&cmnd->conn->rx_hash, cmnd, tio, offset, (u8 *) &crc); - if (!cmnd->conn->read_overflow && (cmnd_opcode(cmnd) != ISCSI_OP_PDU_REJECT)) { + if (!cmnd->conn->read_overflow && + (cmnd_opcode(cmnd) != ISCSI_OP_PDU_REJECT)) { if (crc != cmnd->ddigest) return -EIO; } @@ -268,6 +278,6 @@ void digest_tx_data(struct iscsi_cmnd *c struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; assert(tio); - digest_data(cmnd->conn->tx_digest_tfm, cmnd, tio, + digest_data(&cmnd->conn->tx_hash, cmnd, tio, be32_to_cpu(req->buffer_offset), (u8 *) &cmnd->ddigest); } iscsitarget-1.4.20.3+svn502/usr/000077500000000000000000000000001233224651300161235ustar00rootroot00000000000000iscsitarget-1.4.20.3+svn502/usr/Makefile000066400000000000000000000006321233224651300175640ustar00rootroot00000000000000CFLAGS += -O2 -fno-inline -Wall -Wstrict-prototypes -I../include CFLAGS += -D_GNU_SOURCE # required for glibc >= 2.8 PROGRAMS = ietd ietadm LIBS = all: $(PROGRAMS) ietd: ietd.o iscsid.o conn.o session.o target.o message.o ctldev.o log.o chap.o event.o param.o plain.o isns.o md5.o sha1.o $(CC) $(LDFLAGS) $^ -o $@ $(LIBS) ietadm: ietadm.o param.o $(CC) $(LDFLAGS) $^ -o $@ clean: rm -f *.o $(PROGRAMS) iscsitarget-1.4.20.3+svn502/usr/chap.c000066400000000000000000000374341233224651300172150ustar00rootroot00000000000000/* * chap.c - support for (mutual) CHAP authentication. * (C) 2004 Xiranet Communications GmbH * available under the terms of the GNU GPL v2.0 * * heavily based on code from iscsid.c: * Copyright (C) 2002-2003 Ardis Technolgies , * licensed under the terms of the GNU GPL v2.0, * * and code taken from UNH iSCSI software: * Copyright (C) 2001-2003 InterOperability Lab (IOL) * University of New Hampshire (UNH) * Durham, NH 03824 * licensed under the terms of the GNU GPL v2.0 */ #include #include #include #include "sha1.h" #include "md5.h" #include "iscsid.h" #define HEX_FORMAT 0x01 #define BASE64_FORMAT 0x02 #define CHAP_DIGEST_ALG_MD5 5 #define CHAP_DIGEST_ALG_SHA1 7 #define CHAP_MD5_DIGEST_LEN 16 #define CHAP_SHA1_DIGEST_LEN 20 #define CHAP_INITIATOR_ERROR -1 #define CHAP_AUTH_ERROR -2 #define CHAP_TARGET_ERROR -3 #define CHAP_AUTH_STATE_START AUTH_STATE_START #define CHAP_AUTH_STATE_CHALLENGE 1 #define CHAP_AUTH_STATE_RESPONSE 2 #define CHAP_INITIATOR_AUTH 0 #define CHAP_TARGET_AUTH 1 #define CHAP_CHALLENGE_MAX 50 static inline int decode_hex_digit(char c) { switch (c) { case '0' ... '9': return c - '0'; case 'a' ... 'f': return c - 'a' + 10; case 'A' ... 'F': return c - 'A' + 10; } return 0; } static void decode_hex_string(char *hex_string, u8 *intnum, int intlen) { char *ptr; int j; j = strlen(hex_string); ptr = hex_string + j; j = --intlen; do { intnum[j] = decode_hex_digit(*--ptr); intnum[j] |= decode_hex_digit(*--ptr) << 4; j--; } while (ptr > hex_string); while (j >= 0) intnum[j--] = 0; } /* Base64 decoding, taken from UNH-iSCSI "Base64codeToNumber()" */ static u8 decode_base64_digit(char base64) { switch (base64) { case '=': return 64; case '/': return 63; case '+': return 62; default: if ((base64 >= 'A') && (base64 <= 'Z')) return base64 - 'A'; else if ((base64 >= 'a') && (base64 <= 'z')) return 26 + (base64 - 'a'); else if ((base64 >= '0') && (base64 <= '9')) return 52 + (base64 - '0'); else return -1; } } /* Base64 decoding, taken from UNH-iSCSI "Base64StringToInteger()" */ static void decode_base64_string(char *string, u8 *intnum, int int_len) { int len; int count; int intptr; u8 num[4]; int octets; if ((string == NULL) || (intnum == NULL)) return; len = strlen(string); if (len == 0) return; if ((len % 4) != 0) return; count = 0; intptr = 0; while (count < len - 4) { num[0] = decode_base64_digit(string[count]); num[1] = decode_base64_digit(string[count + 1]); num[2] = decode_base64_digit(string[count + 2]); num[3] = decode_base64_digit(string[count + 3]); if ((num[0] == 65) || (num[1] == 65) || (num[2] == 65) || (num[3] == 65)) return; count += 4; octets = (num[0] << 18) | (num[1] << 12) | (num[2] << 6) | num[3]; intnum[intptr] = (octets & 0xFF0000) >> 16; intnum[intptr + 1] = (octets & 0x00FF00) >> 8; intnum[intptr + 2] = octets & 0x0000FF; intptr += 3; } num[0] = decode_base64_digit(string[count]); num[1] = decode_base64_digit(string[count + 1]); num[2] = decode_base64_digit(string[count + 2]); num[3] = decode_base64_digit(string[count + 3]); if ((num[0] == 64) || (num[1] == 64)) return; if (num[2] == 64) { if (num[3] != 64) return; intnum[intptr] = (num[0] << 2) | (num[1] >> 4); } else if (num[3] == 64) { intnum[intptr] = (num[0] << 2) | (num[1] >> 4); intnum[intptr + 1] = (num[1] << 4) | (num[2] >> 2); } else { octets = (num[0] << 18) | (num[1] << 12) | (num[2] << 6) | num[3]; intnum[intptr] = (octets & 0xFF0000) >> 16; intnum[intptr + 1] = (octets & 0x00FF00) >> 8; intnum[intptr + 2] = octets & 0x0000FF; } } static inline void encode_hex_string(u8 *intnum, long length, char *string) { int i; char *strptr; strptr = string; for (i = 0; i < length; i++, strptr += 2) sprintf(strptr, "%.2hhx", intnum[i]); } /* Base64 encoding, taken from UNH iSCSI "IntegerToBase64String()" */ static void encode_base64_string(u8 *intnum, long length, char *string) { int count, octets, strptr, delta; static const char base64code[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=' }; if ((!intnum) || (!string) || (!length)) return; count = 0; octets = 0; strptr = 0; while ((delta = (length - count)) > 2) { octets = (intnum[count] << 16) | (intnum[count + 1] << 8) | intnum[count + 2]; string[strptr] = base64code[(octets & 0xfc0000) >> 18]; string[strptr + 1] = base64code[(octets & 0x03f000) >> 12]; string[strptr + 2] = base64code[(octets & 0x000fc0) >> 6]; string[strptr + 3] = base64code[octets & 0x00003f]; count += 3; strptr += 4; } if (delta == 1) { string[strptr] = base64code[(intnum[count] & 0xfc) >> 2]; string[strptr + 1] = base64code[(intnum[count] & 0x03) << 4]; string[strptr + 2] = base64code[64]; string[strptr + 3] = base64code[64]; strptr += 4; } else if (delta == 2) { string[strptr] = base64code[(intnum[count] & 0xfc) >> 2]; string[strptr + 1] = base64code[((intnum[count] & 0x03) << 4) | ((intnum[count + 1] & 0xf0) >> 4)]; string[strptr + 2] = base64code[(intnum[count + 1] & 0x0f) << 2]; string[strptr + 3] = base64code[64]; strptr += 4; } string[strptr] = '\0'; } static inline int chap_check_encoding_format(char *encoded) { int encoding_fmt; if (!encoded) return -1; if ((strlen(encoded) < 3) || (encoded[0] != '0')) return -1; if (encoded[1] == 'x' || encoded[1] == 'X') encoding_fmt = HEX_FORMAT; else if (encoded[1] == 'b' || encoded[1] == 'B') encoding_fmt = BASE64_FORMAT; else return -1; return encoding_fmt; } static int chap_alloc_decode_buffer(char *encoded, u8 **decode_buf, int encoding_fmt) { int i; int decode_len = 0; i = strlen(encoded); i -= 2; if (encoding_fmt == HEX_FORMAT) decode_len = (i - 1) / 2 + 1; else if (encoding_fmt == BASE64_FORMAT) { if (i % 4) return CHAP_INITIATOR_ERROR; decode_len = i / 4 * 3; if (encoded[i + 1] == '=') decode_len--; if (encoded[i] == '=') decode_len--; } if (!decode_len) return CHAP_INITIATOR_ERROR; *decode_buf = malloc(decode_len); if (!*decode_buf) return CHAP_TARGET_ERROR; return decode_len; } static int chap_decode_string(char *encoded, u8 *decode_buf, int buf_len, int encoding_fmt) { if (encoding_fmt == HEX_FORMAT) { if ((strlen(encoded) - 2) > (2 * buf_len)) { log_error("%s(%d) BUG? " " buf[%d] !sufficient to decode string[%d]", __FUNCTION__, __LINE__, buf_len, (int) strlen(encoded)); return CHAP_TARGET_ERROR; } decode_hex_string(encoded + 2, decode_buf, buf_len); } else if (encoding_fmt == BASE64_FORMAT) { if ((strlen(encoded) - 2) > ((buf_len - 1) / 3 + 1) * 4) { log_error("%s(%d) BUG? " " buf[%d] !sufficient to decode string[%d]", __FUNCTION__, __LINE__, buf_len, (int) strlen(encoded)); return CHAP_TARGET_ERROR; } decode_base64_string(encoded + 2, decode_buf, buf_len); } else return CHAP_INITIATOR_ERROR; return 0; } static inline void chap_encode_string(u8 *intnum, int buf_len, char *encode_buf, int encoding_fmt) { encode_buf[0] = '0'; if (encoding_fmt == HEX_FORMAT) { encode_buf[1] = 'x'; encode_hex_string(intnum, buf_len, encode_buf + 2); } else if (encoding_fmt == BASE64_FORMAT) { encode_buf[1] = 'b'; encode_base64_string(intnum, buf_len, encode_buf + 2); } } static inline void chap_calc_digest_md5(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest) { struct md5_ctx ctx; md5_init(&ctx); md5_update(&ctx, &chap_id, 1); md5_update(&ctx, secret, secret_len); md5_update(&ctx, challenge, challenge_len); md5_final(&ctx, digest); } static inline void chap_calc_digest_sha1(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest) { struct sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, &chap_id, 1); sha1_update(&ctx, secret, secret_len); sha1_update(&ctx, challenge, challenge_len); sha1_final(&ctx, digest); } static int chap_initiator_auth_create_challenge(struct connection *conn) { char *value, *p; char text[CHAP_CHALLENGE_MAX * 2 + 8]; static int chap_id; int i; value = text_key_find(conn, "CHAP_A"); if (!value) return CHAP_INITIATOR_ERROR; while ((p = strsep(&value, ","))) { if (!strcmp(p, "5")) { conn->auth.chap.digest_alg = CHAP_DIGEST_ALG_MD5; conn->auth_state = CHAP_AUTH_STATE_CHALLENGE; break; } else if (!strcmp(p, "7")) { conn->auth.chap.digest_alg = CHAP_DIGEST_ALG_SHA1; conn->auth_state = CHAP_AUTH_STATE_CHALLENGE; break; } } if (!p) return CHAP_INITIATOR_ERROR; text_key_add(conn, "CHAP_A", p); conn->auth.chap.id = ++chap_id; sprintf(text, "%u", (unsigned char)conn->auth.chap.id); text_key_add(conn, "CHAP_I", text); /* * FIXME: does a random challenge length provide any benefits security- * wise, or should we rather always use the max. allowed length of * 1024 for the (unencoded) challenge? */ conn->auth.chap.challenge_size = (rand() % (CHAP_CHALLENGE_MAX / 2)) + CHAP_CHALLENGE_MAX / 2; conn->auth.chap.challenge = malloc(conn->auth.chap.challenge_size); if (!conn->auth.chap.challenge) return CHAP_TARGET_ERROR; p = text; strcpy(p, "0x"); p += 2; for (i = 0; i < conn->auth.chap.challenge_size; i++) { conn->auth.chap.challenge[i] = rand(); sprintf(p, "%.2hhx", conn->auth.chap.challenge[i]); p += 2; } text_key_add(conn, "CHAP_C", text); return 0; } static int chap_initiator_auth_check_response(struct connection *conn) { char *value; u8 *his_digest = NULL, *our_digest = NULL; int digest_len = 0, retval = 0, encoding_format; char pass[ISCSI_NAME_LEN]; memset(pass, 0, sizeof(pass)); if (cops->account_query(conn->tid, AUTH_DIR_INCOMING, pass, pass) < 0) { log_warning("CHAP initiator auth.: " "No CHAP credentials configured"); retval = CHAP_TARGET_ERROR; goto out; } if (!(value = text_key_find(conn, "CHAP_N"))) { retval = CHAP_INITIATOR_ERROR; goto out; } memset(pass, 0, sizeof(pass)); if (cops->account_query(conn->tid, AUTH_DIR_INCOMING, value, pass) < 0) { log_warning("CHAP initiator auth.: " "No valid user/pass combination for initiator %s " "found", conn->initiator); retval = CHAP_AUTH_ERROR; goto out; } if (!(value = text_key_find(conn, "CHAP_R"))) { retval = CHAP_INITIATOR_ERROR; goto out; } if ((encoding_format = chap_check_encoding_format(value)) < 0) { retval = CHAP_INITIATOR_ERROR; goto out; } switch (conn->auth.chap.digest_alg) { case CHAP_DIGEST_ALG_MD5: digest_len = CHAP_MD5_DIGEST_LEN; break; case CHAP_DIGEST_ALG_SHA1: digest_len = CHAP_SHA1_DIGEST_LEN; break; default: retval = CHAP_TARGET_ERROR; goto out; } if (!(his_digest = malloc(digest_len))) { retval = CHAP_TARGET_ERROR; goto out; } if (!(our_digest = malloc(digest_len))) { retval = CHAP_TARGET_ERROR; goto out; } if (chap_decode_string(value, his_digest, digest_len, encoding_format) < 0) { retval = CHAP_INITIATOR_ERROR; goto out; } switch (conn->auth.chap.digest_alg) { case CHAP_DIGEST_ALG_MD5: chap_calc_digest_md5(conn->auth.chap.id, pass, strlen(pass), conn->auth.chap.challenge, conn->auth.chap.challenge_size, our_digest); break; case CHAP_DIGEST_ALG_SHA1: chap_calc_digest_sha1(conn->auth.chap.id, pass, strlen(pass), conn->auth.chap.challenge, conn->auth.chap.challenge_size, our_digest); break; default: retval = CHAP_TARGET_ERROR; goto out; } if (memcmp(our_digest, his_digest, digest_len)) { log_warning("CHAP initiator auth.: " "authentication of %s failed (wrong secret!?)", conn->initiator); retval = CHAP_AUTH_ERROR; goto out; } conn->state = CHAP_AUTH_STATE_RESPONSE; out: if (his_digest) free(his_digest); if (our_digest) free(our_digest); return retval; } static int chap_target_auth_create_response(struct connection *conn) { char chap_id, *value, *response = NULL; u8 *challenge = NULL, *digest = NULL; int encoding_format, response_len; int challenge_len = 0, digest_len = 0, retval = 0; char pass[ISCSI_NAME_LEN], name[ISCSI_NAME_LEN]; if (!(value = text_key_find(conn, "CHAP_I"))) { /* initiator doesn't want target auth!? */ conn->state = STATE_SECURITY_DONE; retval = 0; goto out; } chap_id = strtol(value, &value, 10); memset(pass, 0, sizeof(pass)); memset(name, 0, sizeof(name)); if (cops->account_query(conn->tid, AUTH_DIR_OUTGOING, name, pass) < 0) { log_warning("CHAP target auth.: " "no outgoing credentials configured%s", conn->tid ? "." : " for discovery."); retval = CHAP_AUTH_ERROR; goto out; } if (!(value = text_key_find(conn, "CHAP_C"))) { log_warning("CHAP target auth.: " "got no challenge from initiator %s", conn->initiator); retval = CHAP_INITIATOR_ERROR; goto out; } if ((encoding_format = chap_check_encoding_format(value)) < 0) { retval = CHAP_INITIATOR_ERROR; goto out; } retval = chap_alloc_decode_buffer(value, &challenge, encoding_format); if (retval <= 0) goto out; else if (retval > 1024) { log_warning("CHAP target auth.: " "initiator %s sent challenge of invalid length %d", conn->initiator, challenge_len); retval = CHAP_INITIATOR_ERROR; goto out; } challenge_len = retval; retval = 0; switch (conn->auth.chap.digest_alg) { case CHAP_DIGEST_ALG_MD5: digest_len = CHAP_MD5_DIGEST_LEN; break; case CHAP_DIGEST_ALG_SHA1: digest_len = CHAP_SHA1_DIGEST_LEN; break; default: retval = CHAP_TARGET_ERROR; goto out; } if (encoding_format == HEX_FORMAT) response_len = 2 * digest_len; else response_len = ((digest_len - 1) / 3 + 1) * 4; //"0x" / "0b" and "\0": response_len += 3; if (!(digest = malloc(digest_len))) { retval = CHAP_TARGET_ERROR; goto out; } if (!(response = malloc(response_len))) { retval = CHAP_TARGET_ERROR; goto out; } if (chap_decode_string(value, challenge, challenge_len, encoding_format) < 0) { retval = CHAP_INITIATOR_ERROR; goto out; } /* RFC 3720, 8.2.1: CHAP challenges MUST NOT be reused */ if (challenge_len == conn->auth.chap.challenge_size) { if (!memcmp(challenge, conn->auth.chap.challenge, challenge_len)) { //FIXME: RFC 3720 demands to close TCP conn. log_warning("CHAP target auth.: " "initiator %s reflected our challenge", conn->initiator); retval = CHAP_INITIATOR_ERROR; goto out; } } switch (conn->auth.chap.digest_alg) { case CHAP_DIGEST_ALG_MD5: chap_calc_digest_md5(chap_id, pass, strlen(pass), challenge, challenge_len, digest); break; case CHAP_DIGEST_ALG_SHA1: chap_calc_digest_sha1(chap_id, pass, strlen(pass), challenge, challenge_len, digest); break; default: retval = CHAP_TARGET_ERROR; goto out; } memset(response, 0x0, response_len); chap_encode_string(digest, digest_len, response, encoding_format); text_key_add(conn, "CHAP_N", name); text_key_add(conn, "CHAP_R", response); conn->state = STATE_SECURITY_DONE; out: if (challenge) free(challenge); if (digest) free(digest); if (response) free(response); return retval; } int cmnd_exec_auth_chap(struct connection *conn) { int res; switch(conn->auth_state) { case CHAP_AUTH_STATE_START: res = chap_initiator_auth_create_challenge(conn); break; case CHAP_AUTH_STATE_CHALLENGE: res = chap_initiator_auth_check_response(conn); if (res < 0) break; /* fall through */ case CHAP_AUTH_STATE_RESPONSE: res = chap_target_auth_create_response(conn); break; default: log_error("%s(%d): BUG. unknown conn->auth_state %d", __FUNCTION__, __LINE__, conn->auth_state); res = CHAP_TARGET_ERROR; } return res; } iscsitarget-1.4.20.3+svn502/usr/config.h000066400000000000000000000034451233224651300175470ustar00rootroot00000000000000/* * config.h - ietd plain file-based configuration * * Copyright (C) 2004-2005 FUJITA Tomonori * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef CONFIG_H #define CONFIG_H struct config_operations { void (*init) (char *params, int *timeout); int (*target_add) (u32 *, char *); int (*target_stop) (u32); int (*target_del) (u32); int (*lunit_add) (u32, u32, char *); int (*lunit_stop) (u32, u32); int (*lunit_del) (u32, u32); int (*param_set) (u32, u64, int, u32, struct iscsi_param *); int (*account_add) (u32, int, char *, char *); int (*account_del) (u32, int, char *); int (*account_query) (u32, int, char *, char *); int (*account_list) (u32, int, u32 *, u32 *, char *, size_t); int (*initiator_allow) (u32, int, char *); int (*target_allow) (u32, struct sockaddr *); int (*target_redirect) (u32, char *, u8); }; extern struct config_operations *cops; #endif iscsitarget-1.4.20.3+svn502/usr/conn.c000066400000000000000000000042001233224651300172200ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #include #include #include "iscsid.h" #define ISCSI_CONN_NEW 1 #define ISCSI_CONN_EXIT 5 struct connection *conn_alloc(void) { struct connection *conn; if (!(conn = malloc(sizeof(*conn)))) return NULL; memset(conn, 0, sizeof(*conn)); conn->state = STATE_FREE; param_set_defaults(conn->session_param, session_keys); INIT_LIST_HEAD(&conn->rsp_buf_list); return conn; } void conn_free(struct connection *conn) { free(conn->initiator); free(conn); } void conn_take_fd(struct connection *conn, int fd) { int err; log_debug(1, "conn_take_fd: %d %u %u %u %" PRIx64, fd, conn->cid, conn->stat_sn, conn->exp_stat_sn, conn->sid.id64); err = ki->conn_create(conn->tid, conn->session->sid.id64, conn->cid, conn->stat_sn, conn->exp_stat_sn, fd, conn->session_param[key_header_digest].val, conn->session_param[key_data_digest].val); if (!err) conn->session->conn_cnt++; else log_error("unable to create connection %u for session %#" PRIx64 " in target %u: %d", conn->cid, conn->session->sid.id64, conn->tid, errno); return; } void conn_read_pdu(struct connection *conn) { conn->iostate = IOSTATE_READ_BHS; conn->buffer = (void *)&conn->req.bhs; conn->rwsize = BHS_SIZE; } void conn_write_pdu(struct connection *conn) { conn->iostate = IOSTATE_WRITE_BHS; memset(&conn->rsp, 0, sizeof(conn->rsp)); conn->buffer = (void *)&conn->rsp.bhs; conn->rwsize = BHS_SIZE; } void conn_free_rsp_buf_list(struct connection *conn) { struct buf_segment *seg, *tmp; list_for_each_entry_safe(seg, tmp, &conn->rsp_buf_list, entry) { list_del(&seg->entry); free(seg); } conn->rsp.datasize = 0; conn->rsp.data = NULL; } void conn_free_pdu(struct connection *conn) { conn->iostate = IOSTATE_FREE; if (conn->req.ahs) { free(conn->req.ahs); conn->req.ahs = NULL; } if (conn->rsp.ahs) { free(conn->rsp.ahs); conn->rsp.ahs = NULL; } conn_free_rsp_buf_list(conn); } iscsitarget-1.4.20.3+svn502/usr/ctldev.c000066400000000000000000000150731233224651300175560ustar00rootroot00000000000000/* * (C) 2004 - 2005 FUJITA Tomonori * * This code is licenced under the GPL. */ #include #include #include #include #include #include #include #include #include #include "iscsid.h" #define CTL_DEVICE "/dev/ietctl" extern int ctrl_fd; static int ctrdev_open(void) { FILE *f; char devname[256]; char buf[256]; int devn; int ctlfd; if (!(f = fopen("/proc/devices", "r"))) { log_error("cannot open control path to the driver: %m"); return -1; } devn = 0; while (!feof(f)) { if (!fgets(buf, sizeof (buf), f)) { break; } if (sscanf(buf, "%d %s", &devn, devname) != 2) { continue; } if (!strcmp(devname, "ietctl")) { break; } devn = 0; } fclose(f); if (!devn) { log_error("cannot find iscsictl in /proc/devices - " "make sure the kernel module is loaded"); return -1; } unlink(CTL_DEVICE); if (mknod(CTL_DEVICE, (S_IFCHR | 0600), (devn << 8))) { log_error("cannot create %s: %m", CTL_DEVICE); return -1; } ctlfd = open(CTL_DEVICE, O_RDWR); if (ctlfd < 0) { log_error("cannot open %s: %m", CTL_DEVICE); return -1; } return ctlfd; } static int iet_module_info(struct module_info *info) { int err; err = ioctl(ctrl_fd, GET_MODULE_INFO, info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl GET_MODULE_INFO: %m"); return (err < 0) ? -errno : 0; } static int iscsi_target_create(u32 *tid, char *name) { struct target_info info; int err; memset(&info, 0, sizeof(info)); snprintf(info.name, sizeof(info.name), "%s", name); info.tid = *tid; err = ioctl(ctrl_fd, ADD_TARGET, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl ADD_TARGET: %m"); else if (!err) *tid = info.tid; return (err < 0) ? -errno : 0; } static int iscsi_target_destroy(u32 tid) { struct target_info info; int err; memset(&info, 0, sizeof(info)); info.tid = tid; err = ioctl(ctrl_fd, DEL_TARGET, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl DEL_TARGET: %m"); return (err < 0) ? -errno : 0; } static int iscsi_lunit_create(u32 tid, u32 lun, char *args) { struct volume_info info; char *p; int err; memset(&info, 0, sizeof(info)); info.tid = tid; info.lun = lun; while (isspace(*args)) args++; for (p = args + (strlen(args) - 1); isspace(*p); p--) *p = '\0'; info.args_ptr = (unsigned long)args; info.args_len = strlen(args); err = ioctl(ctrl_fd, ADD_VOLUME, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl ADD_VOLUME: %m"); return (err < 0) ? -errno : 0; } static int iscsi_lunit_destroy(u32 tid, u32 lun) { struct volume_info info; int err; memset(&info, 0, sizeof(info)); info.tid = tid; info.lun = lun; err = ioctl(ctrl_fd, DEL_VOLUME, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl DEL_VOLUME: %m"); return (err < 0) ? -errno : 0; } static int iscsi_conn_destroy(u32 tid, u64 sid, u32 cid) { struct conn_info info; int err; info.tid = tid; info.sid = sid; info.cid = cid; err = ioctl(ctrl_fd, DEL_CONN, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl DEL_CONN: %m"); return (err < 0) ? -errno : 0; } static int iscsi_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param) { struct iscsi_param_info info; int err, i; memset(&info, 0, sizeof(info)); info.tid = tid; info.sid = sid; info.param_type = type; err = ioctl(ctrl_fd, ISCSI_PARAM_GET, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl ISCSI_PARAM_GET: %m"); else if (!err) { if (type == key_session) for (i = 0; i < session_key_last; i++) param[i].val = info.session_param[i]; else for (i = 0; i < target_key_last; i++) param[i].val = info.target_param[i]; } return (err < 0) ? -errno : 0; } static int iscsi_param_set(u32 tid, u64 sid, int type, u32 partial, struct iscsi_param *param) { struct iscsi_param_info info; int i, err; memset(&info, 0, sizeof(info)); info.tid = tid; info.sid = sid; info.param_type = type; info.partial = partial; if (info.param_type == key_session) for (i = 0; i < session_key_last; i++) info.session_param[i] = param[i].val; else for (i = 0; i < target_key_last; i++) info.target_param[i] = param[i].val; err = ioctl(ctrl_fd, ISCSI_PARAM_SET, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl ISCSI_PARAM_SET: %m"); return (err < 0) ? -errno : 0; } static int iscsi_session_create(u32 tid, u64 sid, u32 exp_cmd_sn, u32 max_cmd_sn, char *name) { struct session_info info; int err; memset(&info, 0, sizeof(info)); info.tid = tid; info.sid = sid; info.exp_cmd_sn = exp_cmd_sn; info.max_cmd_sn = max_cmd_sn; strncpy(info.initiator_name, name, sizeof(info.initiator_name) - 1); err = ioctl(ctrl_fd, ADD_SESSION, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl ADD_SESSION: %m"); return (err < 0) ? -errno : 0; } static int iscsi_session_destroy(u32 tid, u64 sid) { struct session_info info; int err; memset(&info, 0, sizeof(info)); info.tid = tid; info.sid = sid; err = ioctl(ctrl_fd, DEL_SESSION, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl DEL_SESSION: %m"); return (err < 0) ? -errno : 0; } static int iscsi_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn, int fd, u32 hdigest, u32 ddigest) { struct conn_info info; int err; memset(&info, 0, sizeof(info)); info.tid = tid; info.sid = sid; info.cid = cid; info.stat_sn = stat_sn; info.exp_stat_sn = exp_stat_sn; info.fd = fd; info.header_digest = hdigest; info.data_digest = ddigest; err = ioctl(ctrl_fd, ADD_CONN, &info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl ADD_CONN: %m"); return (err < 0) ? -errno : 0; } static int iscsi_session_info(struct session_info *info) { int err; err = ioctl(ctrl_fd, GET_SESSION_INFO, info); if (err < 0 && errno == EFAULT) log_error("error calling ioctl GET_SESSION_INFO: %m"); return (err < 0) ? -errno : 0; } struct iscsi_kernel_interface ioctl_ki = { .ctldev_open = ctrdev_open, .module_info = iet_module_info, .lunit_create = iscsi_lunit_create, .lunit_destroy = iscsi_lunit_destroy, .param_get = iscsi_param_get, .param_set = iscsi_param_set, .target_create = iscsi_target_create, .target_destroy = iscsi_target_destroy, .session_create = iscsi_session_create, .session_destroy = iscsi_session_destroy, .session_info = iscsi_session_info, .conn_create = iscsi_conn_create, .conn_destroy = iscsi_conn_destroy, }; struct iscsi_kernel_interface *ki = &ioctl_ki; iscsitarget-1.4.20.3+svn502/usr/event.c000066400000000000000000000052741233224651300174200ustar00rootroot00000000000000/* * Event notification code. * (C) 2005 FUJITA Tomonori * This code is licenced under the GPL. * * Some functions are based on open-iscsi code * written by Dmitry Yusupov, Alex Aizman. */ #include #include #include #include #include #include #include #include #include "iscsid.h" static struct sockaddr_nl src_addr, dest_addr; static int nl_write(int fd, void *data, int len) { struct iovec iov[2]; struct msghdr msg; struct nlmsghdr nlh = {0}; iov[0].iov_base = &nlh; iov[0].iov_len = sizeof(nlh); iov[1].iov_base = data; iov[1].iov_len = NLMSG_SPACE(len) - sizeof(nlh); nlh.nlmsg_len = NLMSG_SPACE(len); nlh.nlmsg_pid = getpid(); nlh.nlmsg_flags = 0; nlh.nlmsg_type = 0; memset(&msg, 0, sizeof(msg)); msg.msg_name= (void*)&dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = iov; msg.msg_iovlen = 2; return sendmsg(fd, &msg, 0); } static int nl_read(int fd, void *data, int len) { struct iovec iov[2]; struct msghdr msg; struct nlmsghdr nlh; iov[0].iov_base = &nlh; iov[0].iov_len = sizeof(nlh); iov[1].iov_base = data; iov[1].iov_len = len; memset(&msg, 0, sizeof(msg)); msg.msg_name= (void*)&src_addr; msg.msg_namelen = sizeof(src_addr); msg.msg_iov = iov; msg.msg_iovlen = 2; return recvmsg(fd, &msg, MSG_DONTWAIT); } void handle_iscsi_events(int fd) { struct session *session; struct iet_event event; int res; retry: if ((res = nl_read(fd, &event, sizeof(event))) < 0) { if (errno == EAGAIN) return; if (errno == EINTR) goto retry; log_error("read netlink fd (%d)", errno); exit(1); } log_debug(1, "conn %u session %llu target %u, state %u", event.cid, event.sid, event.tid, event.state); switch (event.state) { case E_CONN_CLOSE: if (!(session = session_find_id(event.tid, event.sid))) /* session previously closed for reinstatement? */ break; if (--session->conn_cnt <= 0) session_remove(session); break; default: log_error("%s(%d) %u\n", __FUNCTION__, __LINE__, event.state); exit(-1); break; } } int nl_open(void) { int nl_fd, res; nl_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IET); if (nl_fd == -1) { log_error("%s %d\n", __FUNCTION__, errno); return -1; } memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); src_addr.nl_groups = 0; /* not in mcast groups */ memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; /* kernel */ dest_addr.nl_groups = 0; /* unicast */ res = nl_write(nl_fd, NULL, 0); if (res < 0) { log_error("%s %d\n", __FUNCTION__, res); return res; } return nl_fd; } iscsitarget-1.4.20.3+svn502/usr/ietadm.c000066400000000000000000000447601233224651300175450ustar00rootroot00000000000000/* * ietadm.c - ietd management program * * Copyright (C) 2004-2005 FUJITA Tomonori * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsid.h" #include "ietadm.h" #define SET_TARGET (1 << 0) #define SET_SESSION (1 << 1) #define SET_CONNECTION (1 << 2) #define SET_LUNIT (1 << 3) #define SET_USER (1 << 4) #define SET_REDIRECT (1 << 5) typedef int (user_handle_fn_t)(struct ietadm_req *req, char *user, char *pass); enum ietadm_op { OP_NEW, OP_DELETE, OP_UPDATE, OP_SHOW, }; static char program_name[] = "ietadm"; static struct option const long_options[] = { {"op", required_argument, NULL, 'o'}, {"tid", required_argument, NULL, 't'}, {"sid", required_argument, NULL, 's'}, {"cid", required_argument, NULL, 'c'}, {"lun", required_argument, NULL, 'l'}, {"redirect", required_argument, NULL, 'r'}, {"params", required_argument, NULL, 'p'}, {"user", no_argument, NULL, 'u'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; static void usage(int status) { if (status != 0) fprintf(stderr, "Try `%s --help' for more information.\n", program_name); else { printf("Usage: %s [OPTION]\n", program_name); printf("\ iSCSI Enterprise Target Administration Utility.\n\ \n\ --op new --tid=[id] --params Name=[name]\n\ add a new target with [id]. [id] must not be zero.\n\ --op delete --tid=[id]\n\ delete specific target with [id]. The target must\n\ have no active sessions.\n\ --op show --tid=[id]\n\ show target parameters of target with [id].\n\ --op show --tid=[id] --sid=[sid]\n\ show iSCSI parameters in effect for session [sid]. If\n\ [sid] is \"0\" (zero), the configured parameters\n\ will be displayed.\n\ --op show --tid=[id] --user\n\ show list of Discovery (--tid omitted / id=0 (zero))\n\ or target CHAP accounts.\n\ --op show --tid=[id] --user --params=[user]=[name]\n\ show CHAP account information. [user] can be\n\ \"IncomingUser\" or \"OutgoingUser\". If --tid is\n\ omitted / id=0 (zero), [user] is treated as Discovery\n\ user.\n\ --op new --tid=[id] --lun=[lun] --params Path=[path]\n\ add a new logical unit with [lun] to specific\n\ target with [id]. The logical unit is offered\n\ to the initiators. [path] must be block device files\n\ (including LVM and RAID devices) or regular files.\n\ --op delete --tid=[id] --lun=[lun]\n\ delete specific logical unit with [lun] that\n\ the target with [id] has.\n\ --op delete --tid=[id] --sid=[sid] --cid=[cid]\n\ delete specific connection with [cid] in a session\n\ with [sid] that the target with [id] has.\n\ If the session has no connections after\n\ the operation, the session will be deleted\n\ automatically.\n\ --op delete stop all activity.\n\ --op update --tid=[id] --params=key1=value1,key2=value2,...\n\ change iSCSI IET target parameters of specific\n\ target with [id]. You can use parameters in ietd.conf\n\ as a key.\n\ --op new --tid=[id] --user --params=[user]=[name],Password=[pass]\n\ add a new account with [pass] for specific target.\n\ [user] could be [IncomingUser] or [OutgoingUser].\n\ If you don't specify a target (omit --tid option),\n\ you add a new account for discovery sessions.\n\ --op delete --tid=[id] --user --params=[user]=[name]\n\ delete specific account having [name] of specific\n\ target. [user] could be [IncomingUser] or\n\ [OutgoingUser].\n\ If you don't specify a target (omit --tid option),\n\ you delete the account for discovery sessions.\n\ --op update --tid=[id] --redirect=[dest]\n\ redirect login to target with tid [id] to portal\n\ [dest]. Discovery sessions will not be impacted,\n\ but normal sessions will be redirected before\n\ security negotiation. The destination should be\n\ specified using the format:\n\ [:port]\n\ IPv6 addresses need to be enclosed in [] brackets\n\ To clear, provide an empty string\n\ --version display version and exit\n\ --help display this help and exit\n\ \n\ Report bugs to .\n"); } exit(status == 0 ? 0 : -1); } static int str_to_op(char *str) { int op; if (!strcmp("new", str)) op = OP_NEW; else if (!strcmp("delete", str)) op = OP_DELETE; else if (!strcmp("update", str)) op = OP_UPDATE; else if (!strcmp("show", str)) op = OP_SHOW; else op = -1; return op; } static int ietd_request_send(int fd, struct ietadm_req *req) { int err, ret; if ((ret = write(fd, req, sizeof(*req))) != sizeof(*req)) { err = (ret < 0) ? -errno : -EIO; fprintf(stderr, "%s %d %d %d\n", __FUNCTION__, __LINE__, ret, err); } else err = 0; return err; } static int ietd_response_recv(int fd, struct ietadm_req *req, void *rsp_data, size_t rsp_data_sz) { int err, ret; struct iovec iov[2]; struct ietadm_rsp rsp; iov[0].iov_base = req; iov[0].iov_len = sizeof(*req); iov[1].iov_base = &rsp; iov[1].iov_len = sizeof(rsp); ret = readv(fd, iov, 2); if (ret != sizeof(rsp) + sizeof(*req)) { err = (ret < 0) ? -errno : -EIO; fprintf(stderr, "%s %d %d %d\n", __FUNCTION__, __LINE__, ret, err); } else err = rsp.err; if (!err && rsp_data_sz && rsp_data) { ret = read(fd, rsp_data, rsp_data_sz); if (ret != rsp_data_sz) { err = (ret < 0) ? -errno : -EIO; fprintf(stderr, "%s %d %d %d\n", __FUNCTION__, __LINE__, ret, err); } } return err; } static int ietd_connect(void) { int fd; struct sockaddr_un addr; fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) return -errno; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; memcpy((char *) &addr.sun_path + 1, IETADM_NAMESPACE, strlen(IETADM_NAMESPACE)); if (connect(fd, (struct sockaddr *) &addr, sizeof(addr))) fd = -errno; return fd; } static int ietd_request(struct ietadm_req *req, void *rsp_data, size_t rsp_data_sz) { int fd = -1, err = -EIO; if ((fd = ietd_connect()) < 0) { err = fd; goto out; } if ((err = ietd_request_send(fd, req)) < 0) goto out; err = ietd_response_recv(fd, req, rsp_data, rsp_data_sz); out: if (fd > 0) close(fd); if (err < 0) fprintf(stderr, "%s.\n", strerror(-err)); return err; } static void show_iscsi_param(int type, struct iscsi_param *param) { int i, nr; char buf[1024], *p; struct iscsi_key *keys; if (type == key_session) { nr = session_key_last; keys = session_keys; } else { nr = target_key_last; keys = target_keys; } for (i = 0; i < nr; i++) { memset(buf, 0, sizeof(buf)); strcpy(buf, keys[i].name); p = buf + strlen(buf); *p++ = '='; param_val_to_str(keys, i, param[i].val, p); printf("%s\n", buf); } } static int parse_trgt_params(struct msg_trgt *msg, char *params) { char *p, *q; while ((p = strsep(¶ms, ",")) != NULL) { int idx; u32 val; if (!*p) continue; if (!(q = strchr(p, '='))) continue; *q++ = '\0'; if (!((idx = param_index_by_name(p, target_keys)) < 0)) { if (param_str_to_val(target_keys, idx, q, &val)) { fprintf(stderr, "Invalid %s value \"%s\".\n", target_keys[idx].name, q); return -EINVAL; } if (!param_check_val(target_keys, idx, &val)) msg->target_partial |= (1 << idx); msg->target_param[idx].val = val; msg->type |= 1 << key_target; continue; } if (!((idx = param_index_by_name(p, session_keys)) < 0)) { if (param_str_to_val(session_keys, idx, q, &val)) { fprintf(stderr, "Invalid %s value \"%s\".\n", session_keys[idx].name, q); return -EINVAL; } if (!param_check_val(session_keys, idx, &val)) msg->session_partial |= (1 << idx); msg->session_param[idx].val = val; msg->type |= 1 << key_session; continue; } fprintf(stderr, "Unknown parameter \"%s\".\n", p); return -EINVAL; } return 0; } static int trgt_handle(int op, u32 set, u32 tid, char *params) { int err = -EINVAL; struct ietadm_req req; if (!(set & SET_TARGET)) goto out; memset(&req, 0, sizeof(req)); req.tid = tid; switch (op) { case OP_NEW: { char *p = params; if (!params || !(p = strchr(params, '='))) goto out; *p++ = '\0'; if (strcmp(params, "Name")) goto out; req.rcmnd = C_TRGT_NEW; strncpy(req.u.trgt.name, p, sizeof(req.u.trgt.name) - 1); break; } case OP_DELETE: req.rcmnd = C_TRGT_DEL; break; case OP_UPDATE: if (set & SET_REDIRECT) { req.rcmnd = C_TRGT_REDIRECT; strncpy(req.u.redir.dest, params, sizeof(req.u.redir.dest) - 1); } else { req.rcmnd = C_TRGT_UPDATE; if ((err = parse_trgt_params(&req.u.trgt, params)) < 0) goto out; } break; case OP_SHOW: req.rcmnd = C_TRGT_SHOW; break; } err = ietd_request(&req, NULL, 0); if (!err && req.rcmnd == C_TRGT_SHOW) show_iscsi_param(key_target, req.u.trgt.target_param); out: return err; } static int lunit_handle(int op, u32 set, u32 tid, u32 lun, char *params) { int err = -EINVAL; struct ietadm_req req; if (op == OP_UPDATE) { fprintf(stderr, "Unsupported.\n"); goto out; } if (!(set & SET_TARGET)) goto out; memset(&req, 0, sizeof(req)); req.tid = tid; req.lun = lun; switch (op) { case OP_NEW: req.rcmnd = C_LUNIT_NEW; strncpy(req.u.lunit.args, params, sizeof(req.u.lunit.args) - 1); break; case OP_DELETE: req.rcmnd = C_LUNIT_DEL; break; case OP_SHOW: req.rcmnd = C_LUNIT_SHOW; /* TODO */ break; } err = ietd_request(&req, NULL, 0); out: return err; } static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params) { int err = -EINVAL; struct ietadm_req req; if (op == OP_NEW || op == OP_UPDATE) { fprintf(stderr, "Unsupported.\n"); goto out; } if (!((set & SET_TARGET) && (set & SET_SESSION))) goto out; req.tid = tid; req.sid = sid; req.u.trgt.type = key_session; switch (op) { case OP_DELETE: /* close all connections */ break; case OP_SHOW: req.rcmnd = C_SESS_SHOW; err = ietd_request(&req, NULL, 0); if (!err) show_iscsi_param(key_session, req.u.trgt.session_param); break; } out: return err; } static int parse_user_params(char *params, u32 *auth_dir, char **user, char **pass) { char *p, *q; while ((p = strsep(¶ms, ",")) != NULL) { if (!*p) continue; if (!(q = strchr(p, '='))) continue; *q++ = '\0'; if (isspace(*q)) q++; if (!strcasecmp(p, "IncomingUser")) { if (*user) fprintf(stderr, "Already specified IncomingUser %s\n", q); *user = q; *auth_dir = AUTH_DIR_INCOMING; } else if (!strcasecmp(p, "OutgoingUser")) { if (*user) fprintf(stderr, "Already specified OutgoingUser %s\n", q); *user = q; *auth_dir = AUTH_DIR_OUTGOING; } else if (!strcasecmp(p, "Password")) { if (*pass) fprintf(stderr, "Already specified Password %s\n", q); *pass = q; } else { fprintf(stderr, "Unknown parameter %p\n", q); return -EINVAL; } } return 0; } static void show_account(int auth_dir, char *user, char *pass) { char buf[(ISCSI_NAME_LEN + 1) * 2] = {0}; snprintf(buf, ISCSI_NAME_LEN, "%s", user); if (pass) snprintf(buf + strlen(buf), ISCSI_NAME_LEN, " %s", pass); printf("%sUser %s\n", (auth_dir == AUTH_DIR_INCOMING) ? "Incoming" : "Outgoing", buf); } static int user_handle_show_user(struct ietadm_req *req, char *user) { int err; req->rcmnd = C_ACCT_SHOW; strncpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name) - 1); err = ietd_request(req, NULL, 0); if (!err) show_account(req->u.acnt.auth_dir, req->u.acnt.u.user.name, req->u.acnt.u.user.pass); return err; } static int user_handle_show_list(struct ietadm_req *req) { int i, err, retry; size_t buf_sz = 0; char *buf; req->u.acnt.auth_dir = AUTH_DIR_INCOMING; req->rcmnd = C_ACCT_LIST; do { retry = 0; buf_sz = buf_sz ? buf_sz : ISCSI_NAME_LEN; buf = calloc(buf_sz, sizeof(char *)); if (!buf) { fprintf(stderr, "Memory allocation failed\n"); return -ENOMEM; } req->u.acnt.u.list.alloc_len = buf_sz; err = ietd_request(req, buf, buf_sz); if (err) { free(buf); break; } if (req->u.acnt.u.list.overflow) { buf_sz = ISCSI_NAME_LEN * (req->u.acnt.u.list.count + req->u.acnt.u.list.overflow); retry = 1; free(buf); continue; } for (i = 0; i < req->u.acnt.u.list.count; i++) show_account(req->u.acnt.auth_dir, &buf[i * ISCSI_NAME_LEN], NULL); if (req->u.acnt.auth_dir == AUTH_DIR_INCOMING) { req->u.acnt.auth_dir = AUTH_DIR_OUTGOING; buf_sz = 0; retry = 1; } free(buf); } while (retry); return err; } static int user_handle_show(struct ietadm_req *req, char *user, char *pass) { if (pass) fprintf(stderr, "Ignoring specified password\n"); if (user) return user_handle_show_user(req, user); else return user_handle_show_list(req); } static int user_handle_new(struct ietadm_req *req, char *user, char *pass) { if (!user || !pass) { fprintf(stderr, "Username and password must be specified\n"); return -EINVAL; } req->rcmnd = C_ACCT_NEW; strncpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name) - 1); strncpy(req->u.acnt.u.user.pass, pass, sizeof(req->u.acnt.u.user.pass) - 1); return ietd_request(req, NULL, 0); } static int user_handle_del(struct ietadm_req *req, char *user, char *pass) { if (!user) { fprintf(stderr, "Username must be specified\n"); return -EINVAL; } if (pass) fprintf(stderr, "Ignoring specified password\n"); req->rcmnd = C_ACCT_DEL; strncpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name) - 1); return ietd_request(req, NULL, 0); } static int user_handle(int op, u32 set, u32 tid, char *params) { int err = -EINVAL; char *user = NULL, *pass = NULL; struct ietadm_req req; static user_handle_fn_t *user_handle_fn[] = { user_handle_new, user_handle_del, NULL, user_handle_show, }, *fn; if (set & ~(SET_TARGET | SET_USER)) goto out; memset(&req, 0, sizeof(req)); req.tid = tid; err = parse_user_params(params, &req.u.acnt.auth_dir, &user, &pass); if (err) goto out; fn = user_handle_fn[op]; if (!fn) { fprintf(stderr, "Unsupported\n"); goto out; } err = fn(&req, user, pass); out: return err; } static int conn_handle(int op, u32 set, u32 tid, u64 sid, u32 cid, char *params) { int err = -EINVAL; struct ietadm_req req; if (op == OP_NEW || op == OP_UPDATE) { fprintf(stderr, "Unsupported.\n"); goto out; } if (!((set & SET_TARGET) && (set & SET_SESSION) && (set & SET_CONNECTION))) goto out; memset(&req, 0, sizeof(req)); req.tid = tid; req.sid = sid; req.cid = cid; switch (op) { case OP_DELETE: req.rcmnd = C_CONN_DEL; break; case OP_SHOW: req.rcmnd = C_CONN_SHOW; /* TODO */ break; } err = ietd_request(&req, NULL, 0); out: return err; } static int sys_handle(int op, u32 set, char *params) { int err = -EINVAL; struct ietadm_req req; memset(&req, 0, sizeof(req)); switch (op) { case OP_NEW: break; case OP_DELETE: req.rcmnd = C_SYS_DEL; break; case OP_UPDATE: break; case OP_SHOW: break; } err = ietd_request(&req, NULL, 0); return err; } int main(int argc, char **argv) { int ch, longindex; int err = -EINVAL, op = -1; u32 tid = 0, cid = 0, lun = 0, set = 0; u64 sid = 0; char *params = NULL; while ((ch = getopt_long(argc, argv, "o:t:s:c:l:r:p:uvh", long_options, &longindex)) >= 0) { switch (ch) { case 'o': op = str_to_op(optarg); break; case 't': tid = strtoul(optarg, NULL, 10); set |= SET_TARGET; break; case 's': sid = strtoull(optarg, NULL, 10); set |= SET_SESSION; break; case 'c': cid = strtoul(optarg, NULL, 10); set |= SET_CONNECTION; break; case 'l': lun = strtoul(optarg, NULL, 10); set |= SET_LUNIT; break; case 'p': params = optarg; break; case 'r': set |= SET_REDIRECT; params = optarg; break; case 'u': set |= SET_USER; break; case 'v': printf("%s version %s\n", program_name, IET_VERSION_STRING); exit(0); break; case 'h': usage(0); break; default: usage(-1); } } if (op < 0) { fprintf(stderr, "You must specify the operation type\n"); goto out; } if (optind < argc) { fprintf(stderr, "unrecognized: "); while (optind < argc) fprintf(stderr, "%s", argv[optind++]); fprintf(stderr, "\n"); usage(-1); } if (set & SET_USER) err = user_handle(op, set, tid, params); else if (set & SET_LUNIT) err = lunit_handle(op, set, tid, lun, params); else if (set & SET_CONNECTION) err = conn_handle(op, set, tid, sid, cid, params); else if (set & SET_SESSION) err = sess_handle(op, set, tid, sid, params); else if (set & SET_TARGET) err = trgt_handle(op, set, tid, params); else if (!set) err = sys_handle(op, set, params); else usage(-1); out: return err; } iscsitarget-1.4.20.3+svn502/usr/ietadm.h000066400000000000000000000044751233224651300175510ustar00rootroot00000000000000/* * ietadm.h - ietd management program * * Copyright (C) 2004-2005 FUJITA Tomonori * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef _IET_ADM_H #define _IET_ADM_H #define IETADM_NAMESPACE "IET_ABSTRACT_NAMESPACE" struct msg_trgt { char name[ISCSI_NAME_LEN]; char alias[ISCSI_NAME_LEN]; u32 type; u32 session_partial; u32 target_partial; struct iscsi_param session_param[session_key_last]; struct iscsi_param target_param[target_key_last]; }; struct msg_acnt { u32 auth_dir; union { struct { char name[ISCSI_NAME_LEN]; char pass[ISCSI_NAME_LEN]; } user; struct { u32 alloc_len; u32 count; u32 overflow; } list; } u; }; struct msg_lunit { char args[ISCSI_ARGS_LEN]; }; struct msg_redir { char dest[NI_MAXHOST + NI_MAXSERV + 4]; }; enum ietadm_cmnd { C_TRGT_NEW, C_TRGT_DEL, C_TRGT_UPDATE, C_TRGT_SHOW, C_TRGT_REDIRECT, C_SESS_NEW, C_SESS_DEL, C_SESS_UPDATE, C_SESS_SHOW, C_CONN_NEW, C_CONN_DEL, C_CONN_UPDATE, C_CONN_SHOW, C_LUNIT_NEW, C_LUNIT_DEL, C_LUNIT_UPDATE, C_LUNIT_SHOW, C_ACCT_NEW, C_ACCT_DEL, C_ACCT_UPDATE, C_ACCT_SHOW, C_SYS_NEW, C_SYS_DEL, C_SYS_UPDATE, C_SYS_SHOW, C_ACCT_LIST, }; struct ietadm_req { enum ietadm_cmnd rcmnd; u32 tid; u64 sid; u32 cid; u32 lun; union { struct msg_trgt trgt; struct msg_acnt acnt; struct msg_lunit lunit; struct msg_redir redir; } u; }; struct ietadm_rsp { int err; }; #endif iscsitarget-1.4.20.3+svn502/usr/ietd.c000066400000000000000000000316011233224651300172150ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsid.h" #include "ietadm.h" static char* server_address; uint16_t server_port = ISCSI_LISTEN_PORT; struct pollfd poll_array[POLL_MAX]; static struct connection *incoming[INCOMING_MAX]; static int incoming_cnt; int ctrl_fd, ipc_fd, nl_fd; static char program_name[] = "iscsid"; static struct option const long_options[] = { {"config", required_argument, 0, 'c'}, {"foreground", no_argument, 0, 'f'}, {"debug", required_argument, 0, 'd'}, {"uid", required_argument, 0, 'u'}, {"gid", required_argument, 0, 'g'}, {"address", required_argument, 0, 'a'}, {"port", required_argument, 0, 'p'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}, }; /* This will be configurable by command line options */ extern struct config_operations plain_ops; struct config_operations *cops = &plain_ops; static void usage(int status) { if (status != 0) fprintf(stderr, "Try `%s --help' for more information.\n", program_name); else { printf("Usage: %s [OPTION]\n", program_name); printf("\ iSCSI target daemon.\n\ -c, --config=[path] Execute in the config file.\n"); printf("\ -f, --foreground make the program run in the foreground\n\ -d, --debug debuglevel print debugging information\n\ -u, --uid=uid run as uid, default is current user\n\ -g, --gid=gid run as gid, default is current user group\n\ -a, --address=address listen on specified local address instead of all\n\ -p, --port=port listen on specified port instead of 3260\n\ -h, --help display this help and exit\n\ "); } exit(1); } static int check_version(void) { struct module_info info; int err; memset(&info, 0x0, sizeof(info)); err = ki->module_info(&info); if (err) return 0; return !strncmp(info.version, IET_VERSION_STRING, sizeof(info.version)); } static void set_non_blocking(int fd) { int res = fcntl(fd, F_GETFL); if (res != -1) { res = fcntl(fd, F_SETFL, res | O_NONBLOCK); if (res) log_warning("unable to set fd flags (%s)!", strerror(errno)); } else log_warning("unable to get fd flags (%s)!", strerror(errno)); } static void create_listen_socket(struct pollfd *array) { struct addrinfo hints, *res, *res0; char servname[64]; int i, sock, opt; memset(servname, 0, sizeof(servname)); snprintf(servname, sizeof(servname), "%d", server_port); memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(server_address, servname, &hints, &res0)) { log_error("unable to get address info (%s)!", (errno == EAI_SYSTEM) ? strerror(errno) : gai_strerror(errno)); exit(1); } for (i = 0, res = res0; res && i < LISTEN_MAX; i++, res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { log_error("unable to create server socket (%s) %d %d %d!", strerror(errno), res->ai_family, res->ai_socktype, res->ai_protocol); continue; } opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt))) log_warning("unable to set SO_KEEPALIVE on server socket (%s)!", strerror(errno)); opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) log_warning("unable to set SO_REUSEADDR on server socket (%s)!", strerror(errno)); opt = 1; if (res->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))) continue; if (bind(sock, res->ai_addr, res->ai_addrlen)) { log_error("unable to bind server socket (%s)!", strerror(errno)); continue; } if (listen(sock, INCOMING_MAX)) { log_error("unable to listen to server socket (%s)!", strerror(errno)); continue; } set_non_blocking(sock); array[i].fd = sock; array[i].events = POLLIN; } freeaddrinfo(res0); } static void accept_connection(int listen) { struct sockaddr_storage from; socklen_t namesize; struct pollfd *pollfd; struct connection *conn; int fd, i; namesize = sizeof(from); if ((fd = accept(listen, (struct sockaddr *) &from, &namesize)) < 0) { if (errno != EINTR && errno != EAGAIN) { perror("accept(incoming_socket)"); exit(1); } return; } for (i = 0; i < INCOMING_MAX; i++) { if (!incoming[i]) break; } if (i >= INCOMING_MAX) { log_error("unable to find incoming slot? %d\n", i); exit(1); } if (!(conn = conn_alloc())) { log_error("fail to allocate %s", "conn\n"); exit(1); } conn->fd = fd; incoming[i] = conn; conn_read_pdu(conn); set_non_blocking(fd); pollfd = &poll_array[POLL_INCOMING + i]; pollfd->fd = fd; pollfd->events = POLLIN; pollfd->revents = 0; incoming_cnt++; if (incoming_cnt >= INCOMING_MAX) poll_array[POLL_LISTEN].events = 0; } static void __set_fd(int idx, int fd) { poll_array[idx].fd = fd; poll_array[idx].events = fd ? POLLIN : 0; } void isns_set_fd(int isns, int scn_listen, int scn) { __set_fd(POLL_ISNS, isns); __set_fd(POLL_SCN_LISTEN, scn_listen); __set_fd(POLL_SCN, scn); } void event_loop(int timeout) { int res, i, opt; struct connection *conn; struct pollfd *pollfd; create_listen_socket(poll_array + POLL_LISTEN); poll_array[POLL_IPC].fd = ipc_fd; poll_array[POLL_IPC].events = POLLIN; poll_array[POLL_NL].fd = nl_fd; poll_array[POLL_NL].events = POLLIN; for (i = 0; i < INCOMING_MAX; i++) { poll_array[POLL_INCOMING + i].fd = -1; poll_array[POLL_INCOMING + i].events = 0; incoming[i] = NULL; } while (1) { res = poll(poll_array, POLL_MAX, timeout); if (res == 0) { isns_handle(1, &timeout); continue; } else if (res < 0) { if (res < 0 && errno != EINTR) { perror("poll()"); exit(1); } continue; } for (i = 0; i < LISTEN_MAX; i++) { if (poll_array[POLL_LISTEN + i].revents && incoming_cnt < INCOMING_MAX) accept_connection(poll_array[POLL_LISTEN + i].fd); } if (poll_array[POLL_NL].revents) handle_iscsi_events(nl_fd); if (poll_array[POLL_IPC].revents) ietadm_request_handle(ipc_fd); if (poll_array[POLL_ISNS].revents) isns_handle(0, &timeout); if (poll_array[POLL_SCN_LISTEN].revents) isns_scn_handle(1); if (poll_array[POLL_SCN].revents) isns_scn_handle(0); for (i = 0; i < INCOMING_MAX; i++) { conn = incoming[i]; pollfd = &poll_array[POLL_INCOMING + i]; if (!conn || !pollfd->revents) continue; pollfd->revents = 0; switch (conn->iostate) { case IOSTATE_READ_BHS: case IOSTATE_READ_AHS_DATA: read_again: res = read(pollfd->fd, conn->buffer, conn->rwsize); if (res <= 0) { if (res == 0 || (errno != EINTR && errno != EAGAIN)) conn->state = STATE_CLOSE; else if (errno == EINTR) goto read_again; break; } conn->rwsize -= res; conn->buffer += res; if (conn->rwsize) break; switch (conn->iostate) { case IOSTATE_READ_BHS: conn->iostate = IOSTATE_READ_AHS_DATA; conn->req.ahssize = conn->req.bhs.ahslength * 4; conn->req.datasize = ((conn->req.bhs.datalength[0] << 16) + (conn->req.bhs.datalength[1] << 8) + conn->req.bhs.datalength[2]); conn->rwsize = (conn->req.ahssize + conn->req.datasize + 3) & -4; if (conn->rwsize > INCOMING_BUFSIZE) { log_warning("Recv PDU with " "invalid size %d " "(max: %d)", conn->rwsize, INCOMING_BUFSIZE); conn->state = STATE_CLOSE; goto conn_close; } if (conn->rwsize) { if (!conn->req_buffer) { conn->req_buffer = malloc(INCOMING_BUFSIZE); if (!conn->req_buffer) { log_error("Failed to alloc recv buffer"); conn->state = STATE_CLOSE; goto conn_close; } } conn->buffer = conn->req_buffer; conn->req.ahs = conn->buffer; conn->req.data = conn->buffer + conn->req.ahssize; goto read_again; } case IOSTATE_READ_AHS_DATA: conn_write_pdu(conn); pollfd->events = POLLOUT; log_pdu(2, &conn->req); if (!cmnd_execute(conn)) conn->state = STATE_CLOSE; break; } break; case IOSTATE_WRITE_BHS: case IOSTATE_WRITE_AHS: case IOSTATE_WRITE_DATA: write_again: opt = 1; setsockopt(pollfd->fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); res = write(pollfd->fd, conn->buffer, conn->rwsize); if (res < 0) { if (errno != EINTR && errno != EAGAIN) conn->state = STATE_CLOSE; else if (errno == EINTR) goto write_again; break; } conn->rwsize -= res; conn->buffer += res; if (conn->rwsize) goto write_again; switch (conn->iostate) { case IOSTATE_WRITE_BHS: if (conn->rsp.ahssize) { conn->iostate = IOSTATE_WRITE_AHS; conn->buffer = conn->rsp.ahs; conn->rwsize = conn->rsp.ahssize; goto write_again; } case IOSTATE_WRITE_AHS: if (conn->rsp.datasize) { int o; conn->iostate = IOSTATE_WRITE_DATA; conn->buffer = conn->rsp.data; conn->rwsize = conn->rsp.datasize; o = conn->rwsize & 3; if (o) { for (o = 4 - o; o; o--) *((u8 *)conn->buffer + conn->rwsize++) = 0; } goto write_again; } case IOSTATE_WRITE_DATA: opt = 0; setsockopt(pollfd->fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); cmnd_finish(conn); switch (conn->state) { case STATE_KERNEL: conn_take_fd(conn, pollfd->fd); conn->state = STATE_CLOSE; break; case STATE_EXIT: case STATE_CLOSE: break; default: conn_read_pdu(conn); pollfd->events = POLLIN; break; } break; } break; default: log_error("illegal iostate %d for port %d!\n", conn->iostate, i); exit(1); } conn_close: if (conn->state == STATE_CLOSE) { struct session *session = conn->session; log_debug(1, "connection closed"); conn_free_pdu(conn); conn_free(conn); close(pollfd->fd); pollfd->fd = -1; incoming[i] = NULL; incoming_cnt--; if ((poll_array[POLL_LISTEN].events == 0) && (incoming_cnt < INCOMING_MAX)) poll_array[POLL_LISTEN].events = POLLIN; if (session && session->conn_cnt <= 0) session_remove(session); } } } } int main(int argc, char **argv) { int ch, longindex, timeout = -1; char *config = NULL, pid_buf[64]; uid_t uid = 0; gid_t gid = 0; int pid_fd; /* otherwise we would die in some later write() during the event_loop * instead of getting EPIPE! */ signal(SIGPIPE, SIG_IGN); while ((ch = getopt_long(argc, argv, "c:fd:s:u:g:a:p:vh", long_options, &longindex)) >= 0) { switch (ch) { case 'c': config = optarg; break; case 'f': log_daemon = 0; break; case 'd': log_level = atoi(optarg); break; case 'u': uid = strtoul(optarg, NULL, 10); break; case 'g': gid = strtoul(optarg, NULL, 10); break; case 'a': server_address = strdup(optarg); break; case 'p': server_port = (uint16_t)strtoul(optarg, NULL, 10); break; case 'v': printf("%s version %s\n", program_name, IET_VERSION_STRING); exit(0); break; case 'h': usage(0); break; default: usage(1); break; } } if (log_daemon) { pid_t pid; log_init(); pid = fork(); if (pid < 0) { log_error("error starting daemon: %m"); exit(-1); } else if (pid) exit(0); close(0); open("/dev/null", O_RDWR); dup2(0, 1); dup2(0, 2); setsid(); if (chdir("/") < 0) { log_error("failed to set working dir to /: %m"); exit(-1); } } pid_fd = open("/var/run/ietd.pid", O_WRONLY|O_CREAT, 0644); if (pid_fd < 0) { log_error("unable to create pid file: %m"); exit(-1); } if (lockf(pid_fd, F_TLOCK, 0) < 0) { log_error("unable to lock pid file: %m"); exit(-1); } if (ftruncate(pid_fd, 0) < 0) { log_error("failed to ftruncate the PID file: %m"); exit(-1); } sprintf(pid_buf, "%d\n", getpid()); if (write(pid_fd, pid_buf, strlen(pid_buf)) < strlen(pid_buf)) { log_error("failed to write PID to PID file: %m"); exit(-1); } if ((ipc_fd = ietadm_request_listen()) < 0) { log_error("unable to open ipc fd: %m"); exit(-1); } if ((ctrl_fd = ki->ctldev_open()) < 0) { log_error("unable to open ctldev fd: %m"); exit(-1); } if (!check_version()) { log_error("kernel module version mismatch!"); exit(-1); } if ((nl_fd = nl_open()) < 0) { log_error("unable to open netlink fd: %m"); exit(-1); } if (gid && setgid(gid) < 0) { log_error("unable to setgid: %m"); exit(-1); } if (uid && setuid(uid) < 0) { log_error("unable to setuid: %m"); exit(-1); } cops->init(config, &timeout); event_loop(timeout); return 0; } iscsitarget-1.4.20.3+svn502/usr/iscsi_hdr.h000066400000000000000000000127471233224651300202560ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #ifndef ISCSI_HDR_H #define ISCSI_HDR_H #define ISCSI_VERSION 0 #define __packed __attribute__ ((packed)) struct iscsi_hdr { u8 opcode; /* 0 */ u8 flags; u8 spec1[2]; u8 ahslength; /* 4 */ u8 datalength[3]; u16 lun[4]; /* 8 */ u32 itt; /* 16 */ u32 ttt; /* 20 */ u32 sn; /* 24 */ u32 exp_sn; /* 28 */ u32 max_sn; /* 32 */ u32 spec3[3]; /* 36 */ } __packed; /* 48 */ /* Opcode encoding bits */ #define ISCSI_OP_RETRY 0x80 #define ISCSI_OP_IMMEDIATE 0x40 #define ISCSI_OPCODE_MASK 0x3F /* Client to Server Message Opcode values */ #define ISCSI_OP_NOP_OUT 0x00 #define ISCSI_OP_SCSI_CMD 0x01 #define ISCSI_OP_SCSI_TASK_MGT_MSG 0x02 #define ISCSI_OP_LOGIN_CMD 0x03 #define ISCSI_OP_TEXT_CMD 0x04 #define ISCSI_OP_SCSI_DATA 0x05 #define ISCSI_OP_LOGOUT_CMD 0x06 #define ISCSI_OP_SNACK_CMD 0x10 /* Server to Client Message Opcode values */ #define ISCSI_OP_NOP_IN 0x20 #define ISCSI_OP_SCSI_RSP 0x21 #define ISCSI_OP_SCSI_TASK_MGT_RSP 0x22 #define ISCSI_OP_LOGIN_RSP 0x23 #define ISCSI_OP_TEXT_RSP 0x24 #define ISCSI_OP_SCSI_DATA_RSP 0x25 #define ISCSI_OP_LOGOUT_RSP 0x26 #define ISCSI_OP_R2T_RSP 0x31 #define ISCSI_OP_ASYNC_EVENT 0x32 #define ISCSI_OP_REJECT_MSG 0x3f struct iscsi_ahs_hdr { u16 ahslength; u8 ahstype; } __packed; #define ISCSI_AHSTYPE_CDB 1 #define ISCSI_AHSTYPE_RLENGTH 2 union iscsi_sid { struct { u8 isid[6]; /* Initiator Session ID */ u16 tsih; /* Target Session ID */ } id; u64 id64; } __packed; struct iscsi_text_req_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 ttt; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd3[4]; } __packed; struct iscsi_text_rsp_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 ttt; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 rsvd3[3]; } __packed; struct iscsi_login_req_hdr { u8 opcode; u8 flags; u8 max_version; /* Max. version supported */ u8 min_version; /* Min. version supported */ u8 ahslength; u8 datalength[3]; union iscsi_sid sid; u32 itt; /* Initiator Task Tag */ u16 cid; /* Connection ID */ u16 rsvd1; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd2[4]; } __packed; struct iscsi_login_rsp_hdr { u8 opcode; u8 flags; u8 max_version; /* Max. version supported */ u8 active_version; /* Active version */ u8 ahslength; u8 datalength[3]; union iscsi_sid sid; u32 itt; /* Initiator Task Tag */ u32 rsvd1; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u8 status_class; /* see Login RSP ststus classes below */ u8 status_detail; /* see Login RSP Status details below */ u8 rsvd2[10]; } __packed; #define ISCSI_FLG_CONTINUE 0x40 #define ISCSI_FLG_FINAL 0x80 #define ISCSI_FLG_TRANSIT 0x80 #define ISCSI_FLG_CSG_SECURITY 0x00 #define ISCSI_FLG_CSG_LOGIN 0x04 #define ISCSI_FLG_CSG_FULL_FEATURE 0x0c #define ISCSI_FLG_CSG_MASK 0x0c #define ISCSI_FLG_NSG_SECURITY 0x00 #define ISCSI_FLG_NSG_LOGIN 0x01 #define ISCSI_FLG_NSG_FULL_FEATURE 0x03 #define ISCSI_FLG_NSG_MASK 0x03 /* Login Status response classes */ #define ISCSI_STATUS_SUCCESS 0x00 #define ISCSI_STATUS_REDIRECT 0x01 #define ISCSI_STATUS_INITIATOR_ERR 0x02 #define ISCSI_STATUS_TARGET_ERR 0x03 /* Login Status response detail codes */ /* Class-0 (Success) */ #define ISCSI_STATUS_ACCEPT 0x00 /* Class-1 (Redirection) */ #define ISCSI_STATUS_TGT_MOVED_TEMP 0x01 #define ISCSI_STATUS_TGT_MOVED_PERM 0x02 /* Class-2 (Initiator Error) */ #define ISCSI_STATUS_INIT_ERR 0x00 #define ISCSI_STATUS_AUTH_FAILED 0x01 #define ISCSI_STATUS_TGT_FORBIDDEN 0x02 #define ISCSI_STATUS_TGT_NOT_FOUND 0x03 #define ISCSI_STATUS_TGT_REMOVED 0x04 #define ISCSI_STATUS_NO_VERSION 0x05 #define ISCSI_STATUS_TOO_MANY_CONN 0x06 #define ISCSI_STATUS_MISSING_FIELDS 0x07 #define ISCSI_STATUS_CONN_ADD_FAILED 0x08 #define ISCSI_STATUS_INV_SESSION_TYPE 0x09 #define ISCSI_STATUS_SESSION_NOT_FOUND 0x0a #define ISCSI_STATUS_INV_REQ_TYPE 0x0b /* Class-3 (Target Error) */ #define ISCSI_STATUS_TARGET_ERROR 0x00 #define ISCSI_STATUS_SVC_UNAVAILABLE 0x01 #define ISCSI_STATUS_NO_RESOURCES 0x02 #define ISCSI_RESERVED_TAG 0xffffffff struct iscsi_logout_req_hdr { u8 opcode; u8 flags; u16 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u16 cid; u16 rsvd3; u32 cmd_sn; u32 exp_stat_sn; u32 rsvd4[4]; } __packed; struct iscsi_logout_rsp_hdr { u8 opcode; u8 flags; u8 response; u8 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 itt; u32 rsvd3; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 rsvd4; u16 time2wait; u16 time2retain; u32 rsvd5; } __packed; #define ISCSI_REASON_NO_FULL_FEATURE_PHASE 0x01 #define ISCSI_REASON_DATA_DIGEST_ERROR 0x02 #define ISCSI_REASON_DATA_SNACK_REJECT 0x03 #define ISCSI_REASON_PROTOCOL_ERROR 0x04 #define ISCSI_REASON_UNSUPPORTED_COMMAND 0x05 #define ISCSI_REASON_IMMEDIATE_COMMAND_REJECT 0x06 #define ISCSI_REASON_TASK_IN_PROGRESS 0x07 #define ISCSI_REASON_INVALID_SNACK 0x08 #define ISCSI_REASON_INVALID_PDU_FIELD 0x09 #define ISCSI_REASON_BOOKMARK_REJECT 0x0a #define ISCSI_REASON_NEGOTIATION_RESET 0x0b #define ISCSI_REASON_WAITING_LOGOUT 0x0c struct iscsi_reject_hdr { u8 opcode; u8 flags; u8 reason; u8 rsvd1; u8 ahslength; u8 datalength[3]; u32 rsvd2[2]; u32 ffffffff; u32 rsvd3; u32 stat_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 data_sn; u32 rsvd4[2]; } __packed; #endif /* ISCSI_HDR_H */ iscsitarget-1.4.20.3+svn502/usr/iscsid.c000066400000000000000000000536761233224651300175660ustar00rootroot00000000000000/* * iscsid.c - ietd iSCSI protocol processing * * Copyright (C) 2002-2003 Ardis Technolgies * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "iscsid.h" static u32 ttt; static u32 get_next_ttt(struct connection *conn __attribute__((unused))) { ttt += 1; return (ttt == ISCSI_RESERVED_TAG) ? ++ttt : ttt; } static struct iscsi_key login_keys[] = { {"InitiatorName",}, {"InitiatorAlias",}, {"SessionType",}, {"TargetName",}, {NULL, 0, 0, 0, NULL}, }; char *text_key_find(struct connection *conn, char *searchKey) { char *data, *key, *value; int keylen, datasize; keylen = strlen(searchKey); data = conn->req.data; datasize = conn->req.datasize; while (1) { for (key = data; datasize > 0 && *data != '='; data++, datasize--) ; if (!datasize) return NULL; data++; datasize--; for (value = data; datasize > 0 && *data != 0; data++, datasize--) ; if (!datasize) return NULL; data++; datasize--; if (keylen == value - key - 1 && !strncmp(key, searchKey, keylen)) return value; } } static char *next_key(char **data, int *datasize, char **value) { char *key, *p, *q; int size = *datasize; key = p = *data; for (; size > 0 && *p != '='; p++, size--) ; if (!size) return NULL; *p++ = 0; size--; for (q = p; size > 0 && *p != 0; p++, size--) ; if (!size) return NULL; p++; size--; *data = p; *value = q; *datasize = size; return key; } static struct buf_segment * conn_alloc_buf_segment(struct connection *conn, size_t sz) { struct buf_segment *seg = malloc(sizeof *seg + sz); if (seg) { seg->len = 0; memset(seg->data, 0x0, sz); list_add_tail(&seg->entry, &conn->rsp_buf_list); log_debug(2, "alloc'ed new buf_segment"); } return seg; } void text_key_add(struct connection *conn, char *key, char *value) { struct buf_segment *seg; int keylen = strlen(key); int valuelen = strlen(value); int len = keylen + valuelen + 2; int off = 0; int sz = 0; int stage = 0; size_t data_sz; data_sz = (conn->state == STATE_FULL) ? conn->session_param[key_max_xmit_data_length].val : INCOMING_BUFSIZE; seg = list_empty(&conn->rsp_buf_list) ? NULL : list_entry(conn->rsp_buf_list.q_back, struct buf_segment, entry); while (len) { if (!seg || seg->len == data_sz) { seg = conn_alloc_buf_segment(conn, data_sz); if (!seg) { log_error("Failed to alloc text buf segment\n"); conn_free_rsp_buf_list(conn); break; } } switch (stage) { case 0: sz = min_t(int, data_sz - seg->len, keylen - off); strncpy(seg->data + seg->len, key + off, sz); if (sz == data_sz - seg->len) { off += sz; if (keylen - off == 0) { off = 0; stage++; } } else { off = 0; stage++; } break; case 1: seg->data[seg->len] = '='; off = 0; sz = 1; stage++; break; case 2: sz = min_t(int, data_sz - seg->len, valuelen - off); strncpy(seg->data + seg->len, value + off, sz); off += sz; if (valuelen - off == 0) { off = 0; stage++; } break; case 3: seg->data[seg->len] = 0; sz = 1; break; } log_debug(1, "wrote: %s", seg->data + seg->len); seg->len += sz; len -= sz; } } static void text_key_add_reject(struct connection *conn, char *key) { text_key_add(conn, key, "Reject"); } static int account_empty(u32 tid, int dir) { char pass[ISCSI_NAME_LEN]; memset(pass, 0, sizeof(pass)); return cops->account_query(tid, dir, pass, pass) < 0 ? 1 : 0; } static void login_rsp_err(struct connection *conn, int status_class, int status_detail) { struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs; rsp->flags = 0; rsp->status_class = status_class; rsp->status_detail = status_detail; conn->state = STATE_EXIT; } static inline void login_rsp_ini_err(struct connection *conn, int status_detail) { login_rsp_err(conn, ISCSI_STATUS_INITIATOR_ERR, status_detail); } static inline void login_rsp_tgt_err(struct connection *conn, int status_detail) { login_rsp_err(conn, ISCSI_STATUS_TARGET_ERR, status_detail); } static void text_scan_security(struct connection *conn) { char *key, *value, *data, *nextValue; int datasize; data = conn->req.data; datasize = conn->req.datasize; while ((key = next_key(&data, &datasize, &value))) { if (!(param_index_by_name(key, login_keys) < 0)) ; else if (!strcmp(key, "AuthMethod")) { do { nextValue = strchr(value, ','); if (nextValue) *nextValue++ = 0; if (!strcmp(value, "None")) { if (!account_empty(conn->tid, AUTH_DIR_INCOMING)) continue; conn->auth_method = AUTH_NONE; text_key_add(conn, key, "None"); break; } else if (!strcmp(value, "CHAP")) { if (account_empty(conn->tid, AUTH_DIR_INCOMING)) continue; conn->auth_method = AUTH_CHAP; text_key_add(conn, key, "CHAP"); break; } } while ((value = nextValue)); if (conn->auth_method == AUTH_UNKNOWN) text_key_add_reject(conn, key); } else text_key_add(conn, key, "NotUnderstood"); } if (conn->auth_method == AUTH_UNKNOWN) login_rsp_ini_err(conn, ISCSI_STATUS_AUTH_FAILED); } static void text_scan_login(struct connection *conn) { char *key, *value, *data; int datasize, idx; data = conn->req.data; datasize = conn->req.datasize; while ((key = next_key(&data, &datasize, &value))) { if (!(param_index_by_name(key, login_keys) < 0)) ; else if (!strcmp(key, "AuthMethod")) ; else if (!((idx = param_index_by_name(key, session_keys)) < 0)) { int err; unsigned int val; char buf[32]; if (idx == key_max_xmit_data_length) { text_key_add(conn, key, "NotUnderstood"); continue; } if (idx == key_max_recv_data_length) idx = key_max_xmit_data_length; if (param_str_to_val(session_keys, idx, value, &val) < 0) { if (conn->session_param[idx].state == KEY_STATE_START) { text_key_add_reject(conn, key); continue; } else goto init_err; } err = param_check_val(session_keys, idx, &val); param_set_val(session_keys, conn->session_param, idx, &val); switch (conn->session_param[idx].state) { case KEY_STATE_START: if (idx == key_max_xmit_data_length) break; memset(buf, 0, sizeof(buf)); param_val_to_str(session_keys, idx, val, buf); text_key_add(conn, key, buf); break; case KEY_STATE_REQUEST: if (val != conn->session_param[idx].val) { log_warning("%s %u %u\n", key, val, conn->session_param[idx].val); goto init_err; } break; case KEY_STATE_DONE: break; } conn->session_param[idx].state = KEY_STATE_DONE; } else text_key_add(conn, key, "NotUnderstood"); } return; init_err: login_rsp_ini_err(conn, ISCSI_STATUS_INIT_ERR); return; } static int text_check_param(struct connection *conn) { struct iscsi_param *p = conn->session_param; char buf[32]; int i, cnt; for (i = 0, cnt = 0; session_keys[i].name; i++) { if (p[i].state == KEY_STATE_START && p[i].val != session_keys[i].def) { switch (conn->state) { case STATE_LOGIN_FULL: case STATE_SECURITY_FULL: if (i == key_max_xmit_data_length) { if (p[i].val > session_keys[i].def) p[i].val = session_keys[i].def; p[i].state = KEY_STATE_DONE; continue; } break; case STATE_LOGIN: if (i == key_max_xmit_data_length) continue; memset(buf, 0, sizeof(buf)); param_val_to_str(session_keys, i, p[i].val, buf); text_key_add(conn, session_keys[i].name, buf); if (i == key_max_recv_data_length) { p[i].state = KEY_STATE_DONE; continue; } p[i].state = KEY_STATE_REQUEST; break; default: if (i == key_max_xmit_data_length) continue; } cnt++; } } return cnt; } static void login_start(struct connection *conn) { struct iscsi_login_req_hdr *req = (struct iscsi_login_req_hdr *)&conn->req.bhs; char *name, *session_type, *target_name; struct sockaddr_storage ss; socklen_t slen = sizeof(struct sockaddr_storage); memset(&ss, 0, sizeof(ss)); conn->cid = be16_to_cpu(req->cid); conn->sid.id64 = req->sid.id64; if (!conn->sid.id64) { login_rsp_ini_err(conn, ISCSI_STATUS_MISSING_FIELDS); return; } name = text_key_find(conn, "InitiatorName"); if (!name) { login_rsp_ini_err(conn, ISCSI_STATUS_MISSING_FIELDS); return; } conn->initiator = strdup(name); session_type = text_key_find(conn, "SessionType"); target_name = text_key_find(conn, "TargetName"); conn->auth_method = -1; conn->session_type = SESSION_NORMAL; if (getsockname(conn->fd, (struct sockaddr *) &ss, &slen)) log_error("%s: Failed to get local address.", __FUNCTION__); if (session_type) { if (!strcmp(session_type, "Discovery")) conn->session_type = SESSION_DISCOVERY; else if (strcmp(session_type, "Normal")) { login_rsp_ini_err(conn, ISCSI_STATUS_INV_SESSION_TYPE); return; } } if (conn->session_type == SESSION_NORMAL) { if (!target_name) { login_rsp_ini_err(conn, ISCSI_STATUS_MISSING_FIELDS); return; } struct target * const target = target_find_by_name(target_name); if (!target || !cops->initiator_allow(target->tid, conn->fd, name) || !cops->target_allow(target->tid, (struct sockaddr *) &ss) || !isns_scn_allow(target->tid, name)) { login_rsp_ini_err(conn, ISCSI_STATUS_TGT_NOT_FOUND); return; } if (target_redirected(target, conn, (struct sockaddr *) &ss)) { log_debug(1, "redirected target %s login to %s:%s", target->name, target->redirect.addr, strlen(target->redirect.port) ? target->redirect.port : "3260"); login_rsp_err(conn, ISCSI_STATUS_REDIRECT, target->redirect.type); return; } conn->tid = target->tid; ++target->nr_sessions; if (target->max_nr_sessions && (target->nr_sessions > target->max_nr_sessions)) { --target->nr_sessions; log_debug(1, "rejecting session for target '%s': " "too many sessions", target_name); login_rsp_ini_err(conn, ISCSI_STATUS_TOO_MANY_CONN); return; } if (ki->param_get(conn->tid, 0, key_session, conn->session_param)) login_rsp_ini_err(conn, ISCSI_STATUS_SVC_UNAVAILABLE); } conn->exp_cmd_sn = be32_to_cpu(req->cmd_sn); log_debug(1, "exp_cmd_sn: %d,%d", conn->exp_cmd_sn, req->cmd_sn); text_key_add(conn, "TargetPortalGroupTag", "1"); } static void login_finish(struct connection *conn) { struct iscsi_login_req_hdr *req = (struct iscsi_login_req_hdr *)&conn->req.bhs; struct session *session = session_find_name(conn->tid, conn->initiator, req->sid); switch (conn->session_type) { case SESSION_NORMAL: if (session) { if (!req->sid.id.tsih) { /* session reinstatement */ log_debug(1, "session %#" PRIx64 " reinstated", req->sid.id64); if (session_remove(session)) { login_rsp_tgt_err(conn, ISCSI_STATUS_TARGET_ERROR); return; } session = NULL; } else if (req->sid.id.tsih != session->sid.id.tsih) { /* fail the login */ login_rsp_ini_err(conn, ISCSI_STATUS_SESSION_NOT_FOUND); return; } /* add connection to existing session */ log_debug(1, "connection %u added to session %#" PRIx64, conn->cid, req->sid.id64); conn->session = session; } else if (req->sid.id.tsih) { /* fail the login */ login_rsp_ini_err(conn, ISCSI_STATUS_SESSION_NOT_FOUND); return; } /* create or re-create in case the session closed */ if (session_create(conn)) { login_rsp_tgt_err(conn, ISCSI_STATUS_TARGET_ERROR); return; } break; case SESSION_DISCOVERY: /* set a dummy tsih value */ conn->sid.id.tsih = 1; break; } } static void cmnd_reject(struct connection *conn, u8 reason) { struct iscsi_reject_hdr *rej = (struct iscsi_reject_hdr *)&conn->rsp.bhs; size_t data_sz = sizeof(struct iscsi_hdr); struct buf_segment *seg; conn_free_rsp_buf_list(conn); seg = conn_alloc_buf_segment(conn, data_sz); memset(rej, 0x0, sizeof *rej); rej->opcode = ISCSI_OP_REJECT_MSG; rej->reason = reason; rej->ffffffff = ISCSI_RESERVED_TAG; rej->flags |= ISCSI_FLG_FINAL; rej->stat_sn = cpu_to_be32(conn->stat_sn++); rej->exp_cmd_sn = cpu_to_be32(conn->exp_cmd_sn); conn->max_cmd_sn = conn->exp_cmd_sn + 1; rej->max_cmd_sn = cpu_to_be32(conn->max_cmd_sn); if (!seg) { log_error("Failed to alloc data segment for Reject PDU\n"); return; } memcpy(seg->data, &conn->req.bhs, data_sz); seg->len = data_sz; } static int cmnd_exec_auth(struct connection *conn) { int res; switch (conn->auth_method) { case AUTH_CHAP: res = cmnd_exec_auth_chap(conn); break; case AUTH_NONE: res = 0; break; default: log_error("Unknown auth. method %d", conn->auth_method); res = -3; } return res; } static void cmnd_exec_login(struct connection *conn) { struct iscsi_login_req_hdr *req = (struct iscsi_login_req_hdr *)&conn->req.bhs; struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs; int stay = 0, nsg_disagree = 0; memset(rsp, 0, BHS_SIZE); if ((req->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_LOGIN_CMD || !(req->opcode & ISCSI_OP_IMMEDIATE)) { cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); return; } rsp->opcode = ISCSI_OP_LOGIN_RSP; rsp->max_version = ISCSI_VERSION; rsp->active_version = ISCSI_VERSION; rsp->itt = req->itt; rsp->sid = req->sid; if (/*req->max_version < ISCSI_VERSION ||*/ req->min_version > ISCSI_VERSION) { login_rsp_ini_err(conn, ISCSI_STATUS_NO_VERSION); return; } switch (req->flags & ISCSI_FLG_CSG_MASK) { case ISCSI_FLG_CSG_SECURITY: log_debug(1, "Login request (security negotiation): %d", conn->state); rsp->flags = ISCSI_FLG_CSG_SECURITY; switch (conn->state) { case STATE_FREE: conn->state = STATE_SECURITY; login_start(conn); if (rsp->status_class) return; //else fall through case STATE_SECURITY: text_scan_security(conn); if (rsp->status_class) return; if (conn->auth_method != AUTH_NONE) { conn->state = STATE_SECURITY_AUTH; conn->auth_state = AUTH_STATE_START; } break; case STATE_SECURITY_AUTH: switch (cmnd_exec_auth(conn)) { case 0: break; default: case -1: goto init_err; case -2: goto auth_err; } break; default: goto init_err; } break; case ISCSI_FLG_CSG_LOGIN: log_debug(1, "Login request (operational negotiation): %d", conn->state); rsp->flags = ISCSI_FLG_CSG_LOGIN; switch (conn->state) { case STATE_FREE: conn->state = STATE_LOGIN; login_start(conn); if (!account_empty(conn->tid, AUTH_DIR_INCOMING)) goto auth_err; if (rsp->status_class) return; text_scan_login(conn); if (rsp->status_class) return; stay = text_check_param(conn); break; case STATE_LOGIN: text_scan_login(conn); if (rsp->status_class) return; stay = text_check_param(conn); break; default: goto init_err; } break; default: goto init_err; } if (rsp->status_class) return; if (conn->state != STATE_SECURITY_AUTH && req->flags & ISCSI_FLG_TRANSIT) { int nsg = req->flags & ISCSI_FLG_NSG_MASK; switch (nsg) { case ISCSI_FLG_NSG_LOGIN: switch (conn->state) { case STATE_SECURITY: case STATE_SECURITY_DONE: conn->state = STATE_SECURITY_LOGIN; break; default: goto init_err; } break; case ISCSI_FLG_NSG_FULL_FEATURE: switch (conn->state) { case STATE_SECURITY: case STATE_SECURITY_DONE: if ((nsg_disagree = text_check_param(conn))) { conn->state = STATE_LOGIN; nsg = ISCSI_FLG_NSG_LOGIN; break; } conn->state = STATE_SECURITY_FULL; break; case STATE_LOGIN: if (stay) nsg = ISCSI_FLG_NSG_LOGIN; else conn->state = STATE_LOGIN_FULL; break; default: goto init_err; } if (!stay && !nsg_disagree) { text_check_param(conn); login_finish(conn); } break; default: goto init_err; } rsp->flags |= nsg | (stay ? 0 : ISCSI_FLG_TRANSIT); } /* * TODO: support Logical Text Data Segments > INCOMING_BUFSIZE (i.e. * key=value pairs spanning several PDUs) during login phase */ if (!list_empty(&conn->rsp_buf_list) && !list_length_is_one(&conn->rsp_buf_list)) { log_error("Target error: \'key=value\' pairs spanning several " "Login PDUs are not implemented, yet\n"); goto target_err; } rsp->sid = conn->sid; rsp->stat_sn = cpu_to_be32(conn->stat_sn++); rsp->exp_cmd_sn = cpu_to_be32(conn->exp_cmd_sn); conn->max_cmd_sn = conn->exp_cmd_sn + 1; rsp->max_cmd_sn = cpu_to_be32(conn->max_cmd_sn); return; init_err: login_rsp_ini_err(conn, ISCSI_STATUS_INIT_ERR); return; auth_err: login_rsp_ini_err(conn, ISCSI_STATUS_AUTH_FAILED); return; target_err: login_rsp_tgt_err(conn, ISCSI_STATUS_TARGET_ERROR); return; } static void text_scan_text(struct connection *conn) { char *key, *value, *data; int datasize; data = conn->req.data; datasize = conn->req.datasize; while ((key = next_key(&data, &datasize, &value))) { if (!strcmp(key, "SendTargets")) { if (value[0] == 0) continue; target_list_build(conn, strcmp(value, "All") ? value : NULL); } else text_key_add(conn, key, "NotUnderstood"); } } static void cmnd_exec_text(struct connection *conn) { struct iscsi_text_req_hdr *req = (struct iscsi_text_req_hdr *)&conn->req.bhs; struct iscsi_text_rsp_hdr *rsp = (struct iscsi_text_rsp_hdr *)&conn->rsp.bhs; memset(rsp, 0, BHS_SIZE); rsp->opcode = ISCSI_OP_TEXT_RSP; rsp->itt = req->itt; conn->exp_cmd_sn = be32_to_cpu(req->cmd_sn); if (!(req->opcode & ISCSI_OP_IMMEDIATE)) conn->exp_cmd_sn++; log_debug(1, "Text request: %d", conn->state); if (req->ttt == ISCSI_RESERVED_TAG) { conn_free_rsp_buf_list(conn); text_scan_text(conn); if (!list_empty(&conn->rsp_buf_list) && !list_length_is_one(&conn->rsp_buf_list)) conn->ttt = get_next_ttt(conn); else conn->ttt = ISCSI_RESERVED_TAG; } else if (list_empty(&conn->rsp_buf_list) || conn->ttt != req->ttt) { log_error("Rejecting unexpected text request. TTT recv %#x, " "expected %#x; %stext segments queued\n", req->ttt, conn->ttt, list_empty(&conn->rsp_buf_list) ? "no " : ""); cmnd_reject(conn, ISCSI_REASON_INVALID_PDU_FIELD); return; } if (list_empty(&conn->rsp_buf_list) || list_length_is_one(&conn->rsp_buf_list)) { rsp->flags = ISCSI_FLG_FINAL; conn->ttt = ISCSI_RESERVED_TAG; } rsp->ttt = conn->ttt; rsp->stat_sn = cpu_to_be32(conn->stat_sn++); rsp->exp_cmd_sn = cpu_to_be32(conn->exp_cmd_sn); conn->max_cmd_sn = conn->exp_cmd_sn + 1; rsp->max_cmd_sn = cpu_to_be32(conn->max_cmd_sn); } static void cmnd_exec_logout(struct connection *conn) { struct iscsi_logout_req_hdr *req = (struct iscsi_logout_req_hdr *)&conn->req.bhs; struct iscsi_logout_rsp_hdr *rsp = (struct iscsi_logout_rsp_hdr *)&conn->rsp.bhs; memset(rsp, 0, BHS_SIZE); rsp->opcode = ISCSI_OP_LOGOUT_RSP; rsp->flags = ISCSI_FLG_FINAL; rsp->itt = req->itt; conn->exp_cmd_sn = be32_to_cpu(req->cmd_sn); if (!(req->opcode & ISCSI_OP_IMMEDIATE)) conn->exp_cmd_sn++; rsp->stat_sn = cpu_to_be32(conn->stat_sn++); rsp->exp_cmd_sn = cpu_to_be32(conn->exp_cmd_sn); conn->max_cmd_sn = conn->exp_cmd_sn + 1; rsp->max_cmd_sn = cpu_to_be32(conn->max_cmd_sn); } int cmnd_execute(struct connection *conn) { struct buf_segment *seg; struct iscsi_login_rsp_hdr *login_rsp; switch (conn->req.bhs.opcode & ISCSI_OPCODE_MASK) { case ISCSI_OP_LOGIN_CMD: if (conn->state == STATE_FULL) { cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); break; } cmnd_exec_login(conn); login_rsp = (struct iscsi_login_rsp_hdr *) &conn->rsp.bhs; if (login_rsp->status_class && login_rsp->status_class != ISCSI_STATUS_REDIRECT) conn_free_rsp_buf_list(conn); break; case ISCSI_OP_TEXT_CMD: if (conn->state != STATE_FULL) cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); else cmnd_exec_text(conn); break; case ISCSI_OP_LOGOUT_CMD: if (conn->state != STATE_FULL) cmnd_reject(conn, ISCSI_REASON_PROTOCOL_ERROR); else cmnd_exec_logout(conn); break; default: cmnd_reject(conn, ISCSI_REASON_UNSUPPORTED_COMMAND); return 0; } if (!list_empty(&conn->rsp_buf_list)) { seg = list_entry(conn->rsp_buf_list.q_forw, struct buf_segment, entry); list_del_init(&seg->entry); conn->rsp.datasize = seg->len; conn->rsp.data = seg->data; } else { conn->rsp.datasize = 0; conn->rsp.data = NULL; } conn->rsp.bhs.ahslength = conn->rsp.ahssize / 4; conn->rsp.bhs.datalength[0] = conn->rsp.datasize >> 16; conn->rsp.bhs.datalength[1] = conn->rsp.datasize >> 8; conn->rsp.bhs.datalength[2] = conn->rsp.datasize; log_pdu(2, &conn->rsp); return 1; } void cmnd_finish(struct connection *conn) { struct buf_segment *seg; if (conn->rsp.data) { seg = container_of(conn->rsp.data, struct buf_segment, data); list_del(&seg->entry); free(seg); conn->rsp.data = NULL; } switch (conn->state) { case STATE_EXIT: conn->state = STATE_CLOSE; break; case STATE_SECURITY_LOGIN: conn->state = STATE_LOGIN; break; case STATE_SECURITY_FULL: //fall through case STATE_LOGIN_FULL: if (conn->session_type == SESSION_NORMAL) conn->state = STATE_KERNEL; else conn->state = STATE_FULL; break; } } iscsitarget-1.4.20.3+svn502/usr/iscsid.h000066400000000000000000000155101233224651300175540ustar00rootroot00000000000000/* * iscsid.h - ietd iSCSI protocol processing * * Copyright (C) 2002-2003 Ardis Technolgies * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef ISCSID_H #define ISCSID_H #include #include #include #include #include "types.h" #include "iscsi_hdr.h" #include "iet_u.h" #include "param.h" #include "config.h" #include "misc.h" #define PROC_SESSION "/proc/net/iet/session" struct buf_segment { struct __qelem entry; unsigned int len; char data[0]; }; struct PDU { struct iscsi_hdr bhs; void *ahs; unsigned int ahssize; void *data; unsigned int datasize; }; #define KEY_STATE_START 0 #define KEY_STATE_REQUEST 1 #define KEY_STATE_DONE 2 struct session { struct __qelem slist; char *initiator; struct target *target; union iscsi_sid sid; int conn_cnt; }; struct connection { int state; int iostate; int fd; struct session *session; u32 tid; struct iscsi_param session_param[session_key_last]; char *initiator; union iscsi_sid sid; u16 cid; int session_type; int auth_method; u32 stat_sn; u32 exp_stat_sn; u32 cmd_sn; u32 exp_cmd_sn; u32 max_cmd_sn; u32 ttt; struct PDU req; void *req_buffer; struct PDU rsp; struct __qelem rsp_buf_list; unsigned char *buffer; int rwsize; int auth_state; union { struct { int digest_alg; int id; int challenge_size; unsigned char *challenge; } chap; } auth; }; #define IOSTATE_FREE 0 #define IOSTATE_READ_BHS 1 #define IOSTATE_READ_AHS_DATA 2 #define IOSTATE_WRITE_BHS 3 #define IOSTATE_WRITE_AHS 4 #define IOSTATE_WRITE_DATA 5 #define STATE_FREE 0 #define STATE_SECURITY 1 #define STATE_SECURITY_AUTH 2 #define STATE_SECURITY_DONE 3 #define STATE_SECURITY_LOGIN 4 #define STATE_SECURITY_FULL 5 #define STATE_LOGIN 6 #define STATE_LOGIN_FULL 7 #define STATE_FULL 8 #define STATE_KERNEL 9 #define STATE_CLOSE 10 #define STATE_EXIT 11 #define AUTH_STATE_START 0 #define AUTH_STATE_CHALLENGE 1 /* don't touch these */ #define AUTH_DIR_INCOMING 0 #define AUTH_DIR_OUTGOING 1 #define SESSION_NORMAL 0 #define SESSION_DISCOVERY 1 #define AUTH_UNKNOWN -1 #define AUTH_NONE 0 #define AUTH_CHAP 1 #define DIGEST_UNKNOWN -1 #define BHS_SIZE 48 #define INCOMING_BUFSIZE 8192 #define LISTEN_MAX 8 #define INCOMING_MAX 32 enum { POLL_LISTEN, POLL_IPC = POLL_LISTEN + LISTEN_MAX, POLL_NL, POLL_ISNS, POLL_SCN_LISTEN, POLL_SCN, POLL_INCOMING, POLL_MAX = POLL_INCOMING + INCOMING_MAX, }; struct target { struct __qelem tlist; struct __qelem sessions_list; u32 tid; char name[ISCSI_NAME_LEN]; char *alias; struct redirect_addr { char addr[NI_MAXHOST + 1]; char port[NI_MAXSERV + 1]; u8 type; } redirect; int max_nr_sessions; int nr_sessions; struct __qelem isns_head; }; /* chap.c */ extern int cmnd_exec_auth_chap(struct connection *conn); /* conn.c */ extern struct connection *conn_alloc(void); extern void conn_free(struct connection *conn); extern int conn_test(struct connection *conn); extern void conn_take_fd(struct connection *conn, int fd); extern void conn_read_pdu(struct connection *conn); extern void conn_write_pdu(struct connection *conn); extern void conn_free_pdu(struct connection *conn); extern void conn_free_rsp_buf_list(struct connection *conn); /* ietd.c */ extern uint16_t server_port; extern void isns_set_fd(int isns, int scn_listen, int scn); /* iscsid.c */ extern int iscsi_debug; extern int cmnd_execute(struct connection *conn); extern void cmnd_finish(struct connection *conn); extern char *text_key_find(struct connection *conn, char *searchKey); extern void text_key_add(struct connection *conn, char *key, char *value); /* log.c */ extern int log_daemon; extern int log_level; extern void log_init(void); extern void log_warning(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); extern void log_error(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); extern void log_debug(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); extern void log_pdu(int level, struct PDU *pdu); /* session.c */ extern struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid sid); extern struct session *session_find_id(u32 tid, u64 sid); extern int session_create(struct connection *conn); extern int session_remove(struct session *session); /* target.c */ extern struct __qelem targets_list; extern int target_add(u32 *, char *); extern int target_del(u32); extern int target_redirected(struct target *, struct connection *, struct sockaddr *); extern struct target * target_find_by_name(const char *name); extern struct target * target_find_by_id(u32); extern void target_list_build(struct connection *, char *); /* message.c */ extern int ietadm_request_listen(void); extern int ietadm_request_handle(int accept_fd); /* ctldev.c */ struct iscsi_kernel_interface { int (*ctldev_open) (void); int (*module_info) (struct module_info *); int (*lunit_create) (u32 tid, u32 lun, char *args); int (*lunit_destroy) (u32 tid, u32 lun); int (*param_get) (u32, u64, int, struct iscsi_param *); int (*param_set) (u32, u64, int, u32, struct iscsi_param *); int (*target_create) (u32 *, char *); int (*target_destroy) (u32); int (*session_create) (u32, u64, u32, u32, char *); int (*session_destroy) (u32, u64); int (*session_info) (struct session_info *); int (*conn_create) (u32, u64, u32, u32, u32, int, u32, u32); int (*conn_destroy) (u32 tid, u64 sid, u32 cid); }; extern struct iscsi_kernel_interface *ki; /* event.c */ extern void handle_iscsi_events(int fd); extern int nl_open(void); /* param.c */ extern int param_index_by_name(char *name, struct iscsi_key *keys); /* isns.c */ extern int isns_init(char *addr, int isns_ac); extern int isns_handle(int is_timeout, int *timeout); extern int isns_scn_handle(int accept); extern int isns_scn_allow(u32 tid, char *name); extern int isns_target_register(char *name); extern int isns_target_deregister(char *name); extern void isns_exit(void); #endif /* ISCSID_H */ iscsitarget-1.4.20.3+svn502/usr/isns.c000066400000000000000000000554551233224651300172610ustar00rootroot00000000000000/* * iSNS functions * * Copyright (C) 2006 FUJITA Tomonori * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include "iscsid.h" #include "isns_proto.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define BUFSIZE (1 << 18) struct isns_io { char *buf; int offset; }; struct isns_qry_mgmt { char name[ISCSI_NAME_LEN]; uint16_t transaction; struct __qelem qlist; }; struct isns_initiator { char name[ISCSI_NAME_LEN]; struct __qelem ilist; }; static LIST_HEAD(qry_list); static uint16_t scn_listen_port; static int use_isns, use_isns_ac, isns_fd, scn_listen_fd, scn_fd; static struct isns_io isns_rx, scn_rx; static char *rxbuf; static uint16_t transaction; static uint32_t current_timeout = 30; /* seconds */ static char eid[ISCSI_NAME_LEN]; static uint8_t ip[16]; /* IET supports only one portal */ static struct sockaddr_storage ss; int isns_scn_allow(uint32_t tid, char *name) { struct isns_initiator *ini; struct target *target = target_find_by_id(tid); if (!use_isns || !use_isns_ac) return 1; if (!target) return 0; list_for_each_entry(ini, &target->isns_head, ilist) { if (!strcmp(ini->name, name)) return 1; } return 0; } static int isns_get_ip(int fd) { int err, i; uint32_t addr; union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } l; socklen_t slen = sizeof(l.s); err = getsockname(fd, &l.s, &slen); if (err) { log_error("getsockname error %s!", gai_strerror(err)); return err; } err = getnameinfo(&l.s, sizeof(l.s), eid, sizeof(eid), NULL, 0, 0); if (err) { log_error("getaddrinfo error %s!", gai_strerror(err)); return err; } switch (l.ss.ss_family) { case AF_INET: addr = ((&l.s4)->sin_addr.s_addr); ip[10] = ip[11] = 0xff; ip[15] = 0xff & (addr >> 24); ip[14] = 0xff & (addr >> 16); ip[13] = 0xff & (addr >> 8); ip[12] = 0xff & addr; break; case AF_INET6: for (i = 0; i < ARRAY_SIZE(ip); i++) ip[i] = (&l.s6)->sin6_addr.s6_addr[i]; break; } return 0; } static int isns_connect(void) { int fd, err; fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { log_error("unable to create (%s) %d!", strerror(errno), ss.ss_family); return -1; } err = connect(fd, (struct sockaddr *) &ss, sizeof(ss)); if (err < 0) { log_error("unable to connect (%s) %d!", strerror(errno), ss.ss_family); close(fd); return -1; } log_error("%s %d: new connection %d", __FUNCTION__, __LINE__, fd); if (!strlen(eid)) { err = isns_get_ip(fd); if (err) { close(fd); return -1; } } isns_fd = fd; isns_set_fd(fd, scn_listen_fd, scn_fd); return fd; } static void isns_hdr_init(struct isns_hdr *hdr, uint16_t function, uint16_t length, uint16_t flags, uint16_t trans, uint16_t sequence) { hdr->version = htons(0x0001); hdr->function = htons(function); hdr->length = htons(length); hdr->flags = htons(flags); hdr->transaction = htons(trans); hdr->sequence = htons(sequence); } static int isns_tlv_set(struct isns_tlv **tlv, uint32_t tag, uint32_t length, void *value) { if (length) memcpy((*tlv)->value, value, length); if (length % ISNS_ALIGN) length += (ISNS_ALIGN - (length % ISNS_ALIGN)); (*tlv)->tag = htonl(tag); (*tlv)->length = htonl(length); length += sizeof(struct isns_tlv); *tlv = (struct isns_tlv *) ((char *) *tlv + length); return length; } static int isns_scn_deregister(char *name) { int err; uint16_t flags, length = 0; char buf[2048]; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; if (!isns_fd) if (isns_connect() < 0) return 0; memset(buf, 0, sizeof(buf)); tlv = (struct isns_tlv *) hdr->pdu; length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_SCN_DEREG, length, flags, ++transaction, 0); err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); return 0; } #if __BYTE_ORDER == __LITTLE_ENDIAN #define set_scn_flag(x) \ { \ x = (x & 0x55555555) << 1 | (x & 0xaaaaaaaa) >> 1; \ x = (x & 0x33333333) << 2 | (x & 0xcccccccc) >> 2; \ x = (x & 0x0f0f0f0f) << 4 | (x & 0xf0f0f0f0) >> 4; \ x = (x & 0x00ff00ff) << 8 | (x & 0xff00ff00) >> 8; \ x = (x & 0x0000ffff) << 16 | (x & 0xffff0000) >> 16; \ } #else #define set_scn_flag(x) (x) #endif static int isns_scn_register(void) { int err; uint16_t flags, length = 0; uint32_t scn_flags; char buf[4096]; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; struct target *target; if (list_empty(&targets_list)) return 0; if (!isns_fd) if (isns_connect() < 0) return 0; memset(buf, 0, sizeof(buf)); tlv = (struct isns_tlv *) hdr->pdu; target = list_entry(targets_list.q_forw, struct target, tlist); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(target->name) + 1, target->name); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(target->name) + 1, target->name); length += isns_tlv_set(&tlv, 0, 0, 0); scn_flags = ISNS_SCN_FLAG_INITIATOR | ISNS_SCN_FLAG_OBJECT_REMOVE | ISNS_SCN_FLAG_OBJECT_ADDED | ISNS_SCN_FLAG_OBJECT_UPDATED; set_scn_flag(scn_flags); scn_flags = htonl(scn_flags); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_SCN_BITMAP, sizeof(scn_flags), &scn_flags); flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_SCN_REG, length, flags, ++transaction, 0); err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); return 0; } static int isns_attr_query(char *name) { int err; uint16_t flags, length = 0; char buf[4096]; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; struct target *target; uint32_t node = htonl(ISNS_NODE_INITIATOR); struct isns_qry_mgmt *mgmt; if (list_empty(&targets_list)) return 0; if (!isns_fd) if (isns_connect() < 0) return 0; mgmt = malloc(sizeof(*mgmt)); if (!mgmt) return 0; insque(&mgmt->qlist, &qry_list); memset(buf, 0, sizeof(buf)); tlv = (struct isns_tlv *) hdr->pdu; if (name) snprintf(mgmt->name, sizeof(mgmt->name), "%s", name); else { mgmt->name[0] = '\0'; target = list_entry(targets_list.q_forw, struct target, tlist); name = target->name; } length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, sizeof(node), &node); length += isns_tlv_set(&tlv, 0, 0, 0); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, 0, 0); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, 0, 0); length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS, 0, 0); flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_QRY, length, flags, ++transaction, 0); mgmt->transaction = transaction; err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); return 0; } static int isns_deregister(void) { int err; uint16_t flags, length = 0; char buf[4096]; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; struct target *target; if (list_empty(&targets_list)) return 0; if (!isns_fd) if (isns_connect() < 0) return 0; memset(buf, 0, sizeof(buf)); tlv = (struct isns_tlv *) hdr->pdu; target = list_entry(targets_list.q_forw, struct target, tlist); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(target->name) + 1, target->name); length += isns_tlv_set(&tlv, 0, 0, 0); length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER, strlen(eid) + 1, eid); flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_DEV_DEREG, length, flags, ++transaction, 0); err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); return 0; } int isns_target_register(char *name) { char buf[4096]; uint16_t flags = 0, length = 0; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; uint32_t port = htonl(server_port); uint32_t node = htonl(ISNS_NODE_TARGET); uint32_t type = htonl(2); struct target *target; int err, initial = list_length_is_one(&targets_list); if (!use_isns) return 0; if (!isns_fd) if (isns_connect() < 0) return 0; memset(buf, 0, sizeof(buf)); tlv = (struct isns_tlv *) hdr->pdu; target = list_entry(targets_list.q_back, struct target, tlist); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(target->name) + 1, target->name); length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER, strlen(eid) + 1, eid); length += isns_tlv_set(&tlv, 0, 0, 0); length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER, strlen(eid) + 1, eid); if (initial) { length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_PROTOCOL, sizeof(type), &type); length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS, sizeof(ip), &ip); length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT, sizeof(port), &port); flags = ISNS_FLAG_REPLACE; if (scn_listen_port) { uint32_t sport = htonl(scn_listen_port); length += isns_tlv_set(&tlv, ISNS_ATTR_SCN_PORT, sizeof(sport), &sport); } } length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, sizeof(node), &node); flags |= ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_REG, length, flags, ++transaction, 0); err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); if (scn_listen_port) isns_scn_register(); isns_attr_query(name); return 0; } static void free_all_acl(struct target *target) { struct isns_initiator *ini; while (!list_empty(&target->isns_head)) { ini = list_entry(target->isns_head.q_forw, typeof(*ini), ilist); remque(&ini->ilist); free(ini); } } int isns_target_deregister(char *name) { char buf[4096]; uint16_t flags, length = 0; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; int err, last = list_empty(&targets_list); struct target *target; target = target_find_by_name(name); if (target) free_all_acl(target); if (!use_isns) return 0; if (!isns_fd) if (isns_connect() < 0) return 0; isns_scn_deregister(name); memset(buf, 0, sizeof(buf)); tlv = (struct isns_tlv *) hdr->pdu; length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); length += isns_tlv_set(&tlv, 0, 0, 0); if (last) length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER, strlen(eid) + 1, eid); else length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_DEV_DEREG, length, flags, ++transaction, 0); err = write(isns_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); return 0; } static int recv_hdr(int fd, struct isns_io *rx, struct isns_hdr *hdr) { int err; if (rx->offset < sizeof(*hdr)) { err = read(fd, rx->buf + rx->offset, sizeof(*hdr) - rx->offset); if (err < 0) { if (errno == EAGAIN || errno == EINTR) return -EAGAIN; log_error("header read error %d %d %d %d", fd, err, errno, rx->offset); return -1; } else if (err == 0) return -1; log_debug(1, "header %d %d bytes!", fd, err); rx->offset += err; if (rx->offset < sizeof(*hdr)) { log_debug(1, "header wait %d %d", rx->offset, err); return -EAGAIN; } } return 0; } #define get_hdr_param(hdr, function, length, flags, transaction, sequence) \ { \ function = ntohs(hdr->function); \ length = ntohs(hdr->length); \ flags = ntohs(hdr->flags); \ transaction = ntohs(hdr->transaction); \ sequence = ntohs(hdr->sequence); \ } static int recv_pdu(int fd, struct isns_io *rx, struct isns_hdr *hdr) { uint16_t function, length, flags, transaction, sequence; int err; err = recv_hdr(fd, rx, hdr); if (err) return err; /* Now we got a complete header */ get_hdr_param(hdr, function, length, flags, transaction, sequence); log_debug(1, "got a header %x %u %x %u %u", function, length, flags, transaction, sequence); if (length + sizeof(*hdr) > BUFSIZE) { log_error("FIXME we cannot handle this yet %u!", length); return -1; } if (rx->offset < length + sizeof(*hdr)) { err = read(fd, rx->buf + rx->offset, length + sizeof(*hdr) - rx->offset); if (err < 0) { if (errno == EAGAIN || errno == EINTR) return -EAGAIN; log_error("pdu read error %d %d %d %d", fd, err, errno, rx->offset); return -1; } else if (err == 0) return -1; log_debug(1, "pdu %u %u", fd, err); rx->offset += err; if (rx->offset < length + sizeof(*hdr)) { log_error("pdu wait %d %d", rx->offset, err); return -EAGAIN; } } /* Now we got everything. */ rx->offset = 0; return 0; } #define print_unknown_pdu(hdr) \ { \ uint16_t function, length, flags, transaction, sequence; \ get_hdr_param(hdr, function, length, flags, transaction, \ sequence) \ log_error("%s %d: unknown function %x %u %x %u %u", \ __FUNCTION__, __LINE__, \ function, length, flags, transaction, sequence); \ } static char *print_scn_pdu(struct isns_hdr *hdr) { struct isns_tlv *tlv = (struct isns_tlv *) hdr->pdu; uint16_t length = ntohs(hdr->length); char *name = NULL; while (length) { uint32_t vlen = ntohl(tlv->length); if (vlen + sizeof(*tlv) > length) vlen = length - sizeof(*tlv); switch (ntohl(tlv->tag)) { case ISNS_ATTR_ISCSI_NAME: if (vlen) { size_t slen = vlen - 1; if (slen > ISCSI_NAME_LEN) slen = ISCSI_NAME_LEN; *((char *) tlv->value + slen) = 0; log_error("scn name: %u, %s", vlen, (char *) tlv->value); if (!name) name = (char *) tlv->value; } break; case ISNS_ATTR_TIMESTAMP: /* if (vlen == 8) log_error("%u : %u : %" PRIx64, ntohl(tlv->tag), vlen, *((uint64_t *) tlv->value)); */ break; case ISNS_ATTR_ISCSI_SCN_BITMAP: if (vlen == 4) log_error("scn bitmap : %x", *((uint32_t *) tlv->value)); break; } length -= (sizeof(*tlv) + vlen); tlv = (struct isns_tlv *) ((char *) tlv->value + vlen); } return name; } static void qry_rsp_handle(struct isns_hdr *hdr) { struct isns_tlv *tlv; uint16_t length = ntohs(hdr->length); uint16_t transaction = ntohs(hdr->transaction); uint32_t status = (uint32_t) (*hdr->pdu); struct isns_qry_mgmt *mgmt, *n; struct target *target; struct isns_initiator *ini; char *name = NULL; list_for_each_entry_safe(mgmt, n, &qry_list, qlist) { if (mgmt->transaction == transaction) { remque(&mgmt->qlist); goto found; } } log_error("%s %d: transaction not found %u", __FUNCTION__, __LINE__, transaction); return; found: if (status) { log_error("%s %d: error response %u", __FUNCTION__, __LINE__, status); goto free_qry_mgmt; } if (!strlen(mgmt->name)) { log_debug(1, "%s %d: skip %u", __FUNCTION__, __LINE__, transaction); goto free_qry_mgmt; } target = target_find_by_name(mgmt->name); if (!target) { log_error("%s %d: invalid tid %s", __FUNCTION__, __LINE__, mgmt->name); goto free_qry_mgmt; } free_all_acl(target); /* skip status */ tlv = (struct isns_tlv *) ((char *) hdr->pdu + 4); if (length < 4) goto free_qry_mgmt; length -= 4; while (length) { uint32_t vlen = ntohl(tlv->length); if (vlen + sizeof(*tlv) > length) vlen = length - sizeof(*tlv); switch (ntohl(tlv->tag)) { case ISNS_ATTR_ISCSI_NAME: if (vlen) { size_t slen = vlen - 1; if (slen > ISCSI_NAME_LEN) slen = ISCSI_NAME_LEN; *((char *) tlv->value + slen) = 0; name = (char *) tlv->value; } else name = NULL; break; case ISNS_ATTR_ISCSI_NODE_TYPE: if (vlen == 4 && name && ntohl(*(tlv->value)) == ISNS_NODE_INITIATOR) { log_error("%s %d: %s", __FUNCTION__, __LINE__, (char *) name); ini = malloc(sizeof(*ini)); if (!ini) goto free_qry_mgmt; snprintf(ini->name, sizeof(ini->name), "%s", name); insque(&ini->ilist, &target->isns_head); } else name = NULL; break; default: name = NULL; break; } length -= (sizeof(*tlv) + vlen); tlv = (struct isns_tlv *) ((char *) tlv->value + vlen); } free_qry_mgmt: free(mgmt); } int isns_handle(int is_timeout, int *timeout) { int err; struct isns_io *rx = &isns_rx; struct isns_hdr *hdr = (struct isns_hdr *) rx->buf; uint16_t function; char *name = NULL; if (is_timeout) return isns_attr_query(NULL); err = recv_pdu(isns_fd, rx, hdr); if (err) { if (err == -EAGAIN) return err; log_debug(1, "%s %d: close connection %d", __FUNCTION__, __LINE__, isns_fd); close(isns_fd); isns_fd = 0; isns_set_fd(0, scn_listen_fd, scn_fd); return err; } function = ntohs(hdr->function); switch (function) { case ISNS_FUNC_DEV_ATTR_REG_RSP: break; case ISNS_FUNC_DEV_ATTR_QRY_RSP: qry_rsp_handle(hdr); break; case ISNS_FUNC_DEV_DEREG_RSP: case ISNS_FUNC_SCN_REG_RSP: break; case ISNS_FUNC_SCN: name = print_scn_pdu(hdr); if (name) { log_error("%s %d: %s", __FUNCTION__, __LINE__, name); isns_attr_query(name); } break; default: print_unknown_pdu(hdr); } return 0; } static int scn_accept_connection(void) { struct sockaddr_storage from; socklen_t slen; int fd, err, opt = 1; slen = sizeof(from); fd = accept(scn_listen_fd, (struct sockaddr *) &from, &slen); if (fd < 0) { log_error("%s %d: accept error %s", __FUNCTION__, __LINE__, strerror(errno)); return -errno; } log_error("Accept scn connection %d", fd); err = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); if (err) log_error("%s %d: %s\n", __FUNCTION__, __LINE__, strerror(errno)); /* not critical, so ignore. */ scn_fd = fd; isns_set_fd(isns_fd, scn_listen_fd, scn_fd); return 0; } static void send_scn_rsp(char *name, uint16_t transaction) { char buf[1024]; struct isns_hdr *hdr = (struct isns_hdr *) buf; struct isns_tlv *tlv; uint16_t flags, length = 0; int err; memset(buf, 0, sizeof(buf)); *((uint32_t *) hdr->pdu) = 0; tlv = (struct isns_tlv *) ((char *) hdr->pdu + 4); length +=4; length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name) + 1, name); flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; isns_hdr_init(hdr, ISNS_FUNC_SCN_RSP, length, flags, transaction, 0); err = write(scn_fd, buf, length + sizeof(struct isns_hdr)); if (err < 0) log_error("%s %d: %s", __FUNCTION__, __LINE__, strerror(errno)); } int isns_scn_handle(int is_accept) { int err; struct isns_io *rx = &scn_rx; struct isns_hdr *hdr = (struct isns_hdr *) rx->buf; uint16_t function, transaction; char *name = NULL; log_error("%s %d: %d", __FUNCTION__, __LINE__, is_accept); if (is_accept) return scn_accept_connection(); err = recv_pdu(scn_fd, rx, hdr); if (err) { if (err == -EAGAIN) return err; log_debug(1, "%s %d: close connection %d", __FUNCTION__, __LINE__, scn_fd); close(scn_fd); scn_fd = 0; isns_set_fd(isns_fd, scn_listen_fd, 0); return err; } function = ntohs(hdr->function); transaction = ntohs(hdr->transaction); switch (function) { case ISNS_FUNC_SCN: name = print_scn_pdu(hdr); break; default: print_unknown_pdu(hdr); } if (name) { send_scn_rsp(name, transaction); isns_attr_query(name); } return 0; } static int scn_init(char *addr) { int fd, opt, err; union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } l; socklen_t slen; fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { log_error("%s %d: %s\n", __FUNCTION__, __LINE__, strerror(errno)); return -errno; } opt = 1; if (ss.ss_family == AF_INET6) { err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); if (err) log_error("%s %d: %s\n", __FUNCTION__, __LINE__, strerror(errno)); goto out; } err = listen(fd, 5); if (err) { log_error("%s %d: %s\n", __FUNCTION__, __LINE__, strerror(errno)); goto out; } slen = sizeof(l.s); err = getsockname(fd, &l.s, &slen); if (err) { log_error("%s %d: %s\n", __FUNCTION__, __LINE__, strerror(errno)); goto out; } /* protocol independent way ? */ if (l.ss.ss_family == AF_INET6) scn_listen_port = ntohs((&l.s6)->sin6_port); else scn_listen_port = ntohs((&l.s4)->sin_port); log_error("scn listen port %u %d %d\n", scn_listen_port, fd, err); out: if (err) close(fd); else { scn_listen_fd = fd; isns_set_fd(isns_fd, scn_listen_fd, scn_fd); } return err; } int isns_init(char *addr, int isns_ac) { int err; char port[8]; struct addrinfo hints, *res; snprintf(port, sizeof(port), "%d", ISNS_PORT); memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(addr, (char *) &port, &hints, &res); if (err) { log_error("getaddrinfo error %s, %s", gai_strerror(err), addr); return -1; } memcpy(&ss, res->ai_addr, sizeof(*res->ai_addr)); freeaddrinfo(res); rxbuf = calloc(2, BUFSIZE); if (!rxbuf) { log_error("oom!"); return -1; } scn_init(addr); isns_rx.buf = rxbuf; isns_rx.offset = 0; scn_rx.buf = rxbuf + BUFSIZE; scn_rx.offset = 0; use_isns = 1; use_isns_ac = isns_ac; return current_timeout * 1000; } void isns_exit(void) { struct target *target; if (!use_isns) return; list_for_each_entry(target, &targets_list, tlist) isns_scn_deregister(target->name); isns_deregister(); /* we can't receive events any more. */ isns_set_fd(0, 0, 0); if (isns_fd) close(isns_fd); if (scn_listen_fd) close(scn_listen_fd); if (scn_fd) close(scn_fd); free(rxbuf); } iscsitarget-1.4.20.3+svn502/usr/isns_proto.h000066400000000000000000000155331233224651300205020ustar00rootroot00000000000000/* * iSNS protocol data types * * Copyright (C) 2006 FUJITA Tomonori * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef ISNS_PROTO_H #define ISNS_PROTO_H #define ISNS_PORT 3205 #define ISNS_ALIGN 4 struct isns_hdr { uint16_t version; uint16_t function; uint16_t length; uint16_t flags; uint16_t transaction; uint16_t sequence; uint32_t pdu[0]; } __attribute__ ((packed)); struct isns_tlv { uint32_t tag; uint32_t length; uint32_t value[0]; } __attribute__ ((packed)); /* Commands and responses (4.1.3) */ #define ISNS_FUNC_DEV_ATTR_REG 0x0001 #define ISNS_FUNC_DEV_ATTR_QRY 0x0002 #define ISNS_FUNC_DEV_GET_NEXT 0x0003 #define ISNS_FUNC_DEV_DEREG 0x0004 #define ISNS_FUNC_SCN_REG 0x0005 #define ISNS_FUNC_SCN_DEREG 0x0006 #define ISNS_FUNC_SCN_EVENT 0x0007 #define ISNS_FUNC_SCN 0x0008 #define ISNS_FUNC_DD_REG 0x0009 #define ISNS_FUNC_DD_DEREG 0x000a #define ISNS_FUNC_DDS_REG 0x000b #define ISNS_FUNC_DDS_DEREG 0x000c #define ISNS_FUNC_ESI 0x000d #define ISNS_FUNC_HEARTBEAT 0x000e #define ISNS_FUNC_DEV_ATTR_REG_RSP 0x8001 #define ISNS_FUNC_DEV_ATTR_QRY_RSP 0x8002 #define ISNS_FUNC_DEV_GET_NEXT_RSP 0x8003 #define ISNS_FUNC_DEV_DEREG_RSP 0x8004 #define ISNS_FUNC_SCN_REG_RSP 0x8005 #define ISNS_FUNC_SCN_DEREG_RSP 0x8006 #define ISNS_FUNC_SCN_EVENT_RSP 0x8007 #define ISNS_FUNC_SCN_RSP 0x8008 #define ISNS_FUNC_DD_REG_RSP 0x8009 #define ISNS_FUNC_DD_DEREG_RSP 0x800a #define ISNS_FUNC_DDS_REG_RSP 0x800b #define ISNS_FUNC_DDS_DEREG_RSP 0x800c #define ISNS_FUNC_ESI_RSP 0x800d /* iSNSP flags (5.1.4) */ #define ISNS_FLAG_CLIENT (1U << 15) #define ISNS_FLAG_SERVER (1U << 14) #define ISNS_FLAG_AUTH (1U << 13) #define ISNS_FLAG_REPLACE (1U << 12) #define ISNS_FLAG_LAST_PDU (1U << 11) #define ISNS_FLAG_FIRST_PDU (1U << 10) /* Response Status Codes (5.4) */ #define ISNS_STATUS_SUCCESS 0 #define ISNS_STATUS_UNKNOWN_ERROR 1 #define ISNS_STATUS_FORMAT_ERROR 2 #define ISNS_STATUS_INVALID_REGISTRATION 3 #define ISNS_STATUS_RESERVED 4 #define ISNS_STATUS_INVALID_QUERY 5 #define ISNS_STATUS_SOURCE_UNKNOWN 6 #define ISNS_STATUS_SOURCE_ABSENT 7 #define ISNS_STATUS_SOURCE_UNAUTHORIZED 8 #define ISNS_STATUS_NO_SUCH_ENTRY 9 #define ISNS_STATUS_VERSION_NOT_SUPPORTED 10 #define ISNS_STATUS_INTERNAL_ERROR 11 #define ISNS_STATUS_BUSY 12 #define ISNS_STATUS_OPTION_NOT_UNDERSTOOD 13 #define ISNS_STATUS_INVALID_UPDATE 14 #define ISNS_STATUS_MESSAGE_NOT_SUPPORTED 15 #define ISNS_STATUS_SCN_EVENT_REJECTED 16 #define ISNS_STATUS_SCN_REGISTRATION_REJECTED 17 #define ISNS_STATUS_ATTRIBUTE_NOT_IMPLEMENTED 18 #define ISNS_STATUS_FC_DOMAIN_ID_NOT_AVAILABLE 19 #define ISNS_STATUS_FC_DOMAIN_ID_NOT_ALLOCATED 20 #define ISNS_STATUS_ESI_NOT_AVAILABLE 21 #define ISNS_STATUS_INVALIDE_DEREGISTRATION 22 #define ISNS_STATUS_REGISTRATION_NOT_SUPPORTED 23 /* Node type (5.4.2) */ #define ISNS_NODE_CONTROL (1U << 2) #define ISNS_NODE_INITIATOR (1U << 1) #define ISNS_NODE_TARGET (1U << 0) /* Attributes (6.1) */ #define ISNS_ATTR_DELIMITER 0 #define ISNS_ATTR_ENTITY_IDENTIFIER 1 #define ISNS_ATTR_ENTITY_PROTOCOL 2 #define ISNS_ATTR_MANAGEMENT_IP_ADDRESS 3 #define ISNS_ATTR_TIMESTAMP 4 #define ISNS_ATTR_PROTOCOL_VERSION_RANGE 5 #define ISNS_ATTR_REGISTRATION_PERIOD 6 #define ISNS_ATTR_ENTITY_INDEX 7 #define ISNS_ATTR_ENTITY_NEXT_INDEX 8 #define ISNS_ATTR_ISAKMP_PHASE1 11 #define ISNS_ATTR_CERTIFICATE 12 #define ISNS_ATTR_PORTAL_IP_ADDRESS 16 #define ISNS_ATTR_PORTAL_PORT 17 #define ISNS_ATTR_PORTAL_SYMBOLIC_NAME 18 #define ISNS_ATTR_ESI_INTERVAL 19 #define ISNS_ATTR_ESI_PORT 20 #define ISNS_ATTR_PORTAL_INDEX 22 #define ISNS_ATTR_SCN_PORT 23 #define ISNS_ATTR_PORTAL_NEXT_INDEX 24 #define ISNS_ATTR_PORTAL_SECURITY_BITMAP 27 #define ISNS_ATTR_PORTAL_ISAKMP_PHASE1 28 #define ISNS_ATTR_PORTAL_ISAKMP_PHASE2 29 #define ISNS_ATTR_PORTAL_CERTIFICATE 31 #define ISNS_ATTR_ISCSI_NAME 32 #define ISNS_ATTR_ISCSI_NODE_TYPE 33 #define ISNS_ATTR_ISCSI_ALIAS 34 #define ISNS_ATTR_ISCSI_SCN_BITMAP 35 #define ISNS_ATTR_ISCSI_NODE_INDEX 36 #define ISNS_ATTR_WWNN_TOKEN 37 #define ISNS_ATTR_ISCSI_NODE_NEXT_INDEX 38 #define ISNS_ATTR_ISCSI_AUTHMETHOD 42 #define ISNS_ATTR_PG_ISCSI_NAME 48 #define ISNS_ATTR_PG_PORTAL_IP_ADDRESS 49 #define ISNS_ATTR_PG_PORTAL_PORT 50 #define ISNS_ATTR_PG_TAG 51 #define ISNS_ATTR_PG_INDEX 52 #define ISNS_ATTR_PG_NEXT_INDEX 53 #define ISNS_ATTR_FC_PORT_NAME_WWPN 64 #define ISNS_ATTR_PORT_ID 65 #define ISNS_ATTR_PORT_TYPE 66 #define ISNS_ATTR_SYMBOLIC_PORT_NAME 67 #define ISNS_ATTR_FABRIC_PORT_NAME 68 #define ISNS_ATTR_HARD_ADDRESS 69 #define ISNS_ATTR_PORT_IP_ADDRESS 70 #define ISNS_ATTR_CLASS_OF_SERVICE 71 #define ISNS_ATTR_FC4_TYPES 72 #define ISNS_ATTR_FC4_DESCRIPOTR 73 #define ISNS_ATTR_FC4_FEATURES 74 #define ISNS_ATTR_IFCP_SCN_BITMAP 75 #define ISNS_ATTR_PORT_ROLE 76 #define ISNS_ATTR_PERMANENT_PORT_NAME 77 #define ISNS_ATTR_FC4_TYPE_CODE 95 #define ISNS_ATTR_FC_NODE_NAME_WWNN 96 #define ISNS_ATTR_SYMBOLIC_NODE_NAME 97 #define ISNS_ATTR_NODE_IP_ADDRESS 98 #define ISNS_ATTR_NODE_IPA 99 #define ISNS_ATTR_PORXY_ISCSI_NAME 101 #define ISNS_ATTR_SWITCH_NAME 128 #define ISNS_ATTR_PREFERRED_ID 129 #define ISNS_ATTR_ASSIGNED_ID 130 #define ISNS_ATTR_VIRTUAL_FABRIC_ID 131 #define ISNS_ATTR_ISNS_SERVER_VENDOR_OUI 256 #define ISNS_ATTR_DD_SET_ID 2049 #define ISNS_ATTR_DD_SET_SYM_NAME 2050 #define ISNS_ATTR_DD_SET_STATUS 2051 #define ISNS_ATTR_DD_SET_NEXT_ID 2052 #define ISNS_ATTR_DD_ID 2065 #define ISNS_ATTR_DD_SYMBOLIC_NAME 2066 #define ISNS_ATTR_DD_MEMBER_ISCSI_INDEX 2067 #define ISNS_ATTR_DD_MEMBER_ISCSI_NAME 2068 #define ISNS_ATTR_DD_MEMBER_FC_PORT_NAME 2069 #define ISNS_ATTR_DD_MEMBER_PORTAL_INDEX 2070 #define ISNS_ATTR_DD_MEMBER_IP_ADDR 2071 #define ISNS_ATTR_DD_MEMBER_TCP_UDP 2072 #define ISNS_ATTR_DD_FEATURES 2078 #define ISNS_ATTR_DD_ID_NEXT_ID 2079 /* SCN flags (6.4.4) */ #define ISNS_SCN_FLAG_INITIATOR (1U << 24) #define ISNS_SCN_FLAG_TARGET (1U << 25) #define ISNS_SCN_FLAG_MANAGEMENT (1U << 26) #define ISNS_SCN_FLAG_OBJECT_REMOVE (1U << 27) #define ISNS_SCN_FLAG_OBJECT_ADDED (1U << 28) #define ISNS_SCN_FLAG_OBJECT_UPDATED (1U << 29) #define ISNS_SCN_FLAG_DD_REMOVED (1U << 30) #define ISNS_SCN_FLAG_DD_ADDED (1U << 31) #endif iscsitarget-1.4.20.3+svn502/usr/log.c000066400000000000000000000051601233224651300170520ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #include "iscsid.h" int log_daemon = 1; int log_level = 0; void log_init(void) { if (log_daemon) openlog("ietd", 0, LOG_DAEMON); } static void dolog(int prio, const char *fmt, va_list ap) { if (log_daemon) vsyslog(prio, fmt, ap); else { struct timeval time; gettimeofday(&time, NULL); fprintf(stderr, "%ld.%06ld: ", time.tv_sec, time.tv_usec); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); fflush(stderr); } } void log_warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); dolog(LOG_WARNING, fmt, ap); va_end(ap); } void log_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); dolog(LOG_ERR, fmt, ap); va_end(ap); } void log_debug(int level, const char *fmt, ...) { if (log_level >= level) { va_list ap; va_start(ap, fmt); dolog(LOG_DEBUG, fmt, ap); va_end(ap); } } /* Definition for log_pdu buffer */ #define BUFFER_SIZE 16 /* * size required for a hex dump of BUFFER_SIZE bytes (' ' + 2 chars = 3 chars * per byte) with a ' |' separator each 4th byte: */ #define LINE_SIZE (BUFFER_SIZE * 3 + BUFFER_SIZE / 4 * 2 + 1) static void __dump_line(int level, unsigned char *buf, int *cp) { char line[LINE_SIZE], *lp = line; int i, cnt; cnt = *cp; if (!cnt) return; for (i = 0; i < BUFFER_SIZE; i++) { if (i < cnt) lp += sprintf(lp, " %02x", buf[i]); else lp += sprintf(lp, " "); if ((i % 4) == 3) lp += sprintf(lp, " |"); if (i >= cnt || !isprint(buf[i])) buf[i] = ' '; } /* buf is not \0-terminated! */ log_debug(level, "%s %.*s |", line, BUFFER_SIZE, buf); *cp = 0; } static void __dump_char(int level, unsigned char *buf, int *cp, int ch) { int cnt = (*cp)++; buf[cnt] = ch; if (cnt == BUFFER_SIZE - 1) __dump_line(level, buf, cp); } #define dump_line() __dump_line(level, char_buf, &char_cnt) #define dump_char(ch) __dump_char(level, char_buf, &char_cnt, ch) void log_pdu(int level, struct PDU *pdu) { unsigned char char_buf[BUFFER_SIZE]; int char_cnt = 0; unsigned char *buf; int i; if (log_level < level) return; buf = (void *)&pdu->bhs; log_debug(level, "BHS: (%p)", buf); for (i = 0; i < BHS_SIZE; i++) dump_char(*buf++); dump_line(); buf = (void *)pdu->ahs; log_debug(level, "AHS: (%p)", buf); for (i = 0; i < pdu->ahssize; i++) dump_char(*buf++); dump_line(); buf = (void *)pdu->data; log_debug(level, "Data: (%p)", buf); for (i = 0; i < pdu->datasize; i++) dump_char(*buf++); dump_line(); } iscsitarget-1.4.20.3+svn502/usr/md5.c000066400000000000000000000144211233224651300167560ustar00rootroot00000000000000/* * MD5 Message Digest Algorithm (RFC1321). * * Derived from cryptoapi implementation, originally based on the * public domain implementation written by Colin Plumb in 1993. * * Copyright (c) Cryptoapi developers. * Copyright (c) 2002 James Morris * * 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. * */ #include "md5.h" /* MD5 Transforms */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* Macro for applying transforms */ #define MD5STEP(f, w, x, y, z, i, k, s) \ (w += f(x, y, z) + i + k, w = (w << s | w >> (32 - s)) + x) static void __md5_transform(u32 hash[MD5_DIGEST_WORDS], const u32 in[MD5_BLOCK_WORDS]) { register u32 a, b, c, d; a = hash[0]; b = hash[1]; c = hash[2]; d = hash[3]; MD5STEP(F1, a, b, c, d, in[0], 0xD76AA478UL, 7); MD5STEP(F1, d, a, b, c, in[1], 0xE8C7B756UL, 12); MD5STEP(F1, c, d, a, b, in[2], 0x242070DBUL, 17); MD5STEP(F1, b, c, d, a, in[3], 0xC1BDCEEEUL, 22); MD5STEP(F1, a, b, c, d, in[4], 0xF57C0FAFUL, 7); MD5STEP(F1, d, a, b, c, in[5], 0x4787C62AUL, 12); MD5STEP(F1, c, d, a, b, in[6], 0xA8304613UL, 17); MD5STEP(F1, b, c, d, a, in[7], 0xFD469501UL, 22); MD5STEP(F1, a, b, c, d, in[8], 0x698098D8UL, 7); MD5STEP(F1, d, a, b, c, in[9], 0x8B44F7AFUL, 12); MD5STEP(F1, c, d, a, b, in[10], 0xFFFF5BB1UL, 17); MD5STEP(F1, b, c, d, a, in[11], 0x895CD7BEUL, 22); MD5STEP(F1, a, b, c, d, in[12], 0x6B901122UL, 7); MD5STEP(F1, d, a, b, c, in[13], 0xFD987193UL, 12); MD5STEP(F1, c, d, a, b, in[14], 0xA679438EUL, 17); MD5STEP(F1, b, c, d, a, in[15], 0x49B40821UL, 22); MD5STEP(F2, a, b, c, d, in[1], 0xF61E2562UL, 5); MD5STEP(F2, d, a, b, c, in[6], 0xC040B340UL, 9); MD5STEP(F2, c, d, a, b, in[11], 0x265E5A51UL, 14); MD5STEP(F2, b, c, d, a, in[0], 0xE9B6C7AAUL, 20); MD5STEP(F2, a, b, c, d, in[5], 0xD62F105DUL, 5); MD5STEP(F2, d, a, b, c, in[10], 0x02441453UL, 9); MD5STEP(F2, c, d, a, b, in[15], 0xD8A1E681UL, 14); MD5STEP(F2, b, c, d, a, in[4], 0xE7D3FBC8UL, 20); MD5STEP(F2, a, b, c, d, in[9], 0x21E1CDE6UL, 5); MD5STEP(F2, d, a, b, c, in[14], 0xC33707D6UL, 9); MD5STEP(F2, c, d, a, b, in[3], 0xF4D50D87UL, 14); MD5STEP(F2, b, c, d, a, in[8], 0x455A14EDUL, 20); MD5STEP(F2, a, b, c, d, in[13], 0xA9E3E905UL, 5); MD5STEP(F2, d, a, b, c, in[2], 0xFCEFA3F8UL, 9); MD5STEP(F2, c, d, a, b, in[7], 0x676F02D9UL, 14); MD5STEP(F2, b, c, d, a, in[12], 0x8D2A4C8AUL, 20); MD5STEP(F3, a, b, c, d, in[5], 0xFFFA3942UL, 4); MD5STEP(F3, d, a, b, c, in[8], 0x8771F681UL, 11); MD5STEP(F3, c, d, a, b, in[11], 0x6D9D6122UL, 16); MD5STEP(F3, b, c, d, a, in[14], 0xFDE5380CUL, 23); MD5STEP(F3, a, b, c, d, in[1], 0xA4BEEA44UL, 4); MD5STEP(F3, d, a, b, c, in[4], 0x4BDECFA9UL, 11); MD5STEP(F3, c, d, a, b, in[7], 0xF6BB4B60UL, 16); MD5STEP(F3, b, c, d, a, in[10], 0xBEBFBC70UL, 23); MD5STEP(F3, a, b, c, d, in[13], 0x289B7EC6UL, 4); MD5STEP(F3, d, a, b, c, in[0], 0xEAA127FAUL, 11); MD5STEP(F3, c, d, a, b, in[3], 0xD4EF3085UL, 16); MD5STEP(F3, b, c, d, a, in[6], 0x04881D05UL, 23); MD5STEP(F3, a, b, c, d, in[9], 0xD9D4D039UL, 4); MD5STEP(F3, d, a, b, c, in[12], 0xE6DB99E5UL, 11); MD5STEP(F3, c, d, a, b, in[15], 0x1FA27CF8UL, 16); MD5STEP(F3, b, c, d, a, in[2], 0xC4AC5665UL, 23); MD5STEP(F4, a, b, c, d, in[0], 0xF4292244UL, 6); MD5STEP(F4, d, a, b, c, in[7], 0x432AFF97UL, 10); MD5STEP(F4, c, d, a, b, in[14], 0xAB9423A7UL, 15); MD5STEP(F4, b, c, d, a, in[5], 0xFC93A039UL, 21); MD5STEP(F4, a, b, c, d, in[12], 0x655B59C3UL, 6); MD5STEP(F4, d, a, b, c, in[3], 0x8F0CCC92UL, 10); MD5STEP(F4, c, d, a, b, in[10], 0xFFEFF47DUL, 15); MD5STEP(F4, b, c, d, a, in[1], 0x85845DD1UL, 21); MD5STEP(F4, a, b, c, d, in[8], 0x6FA87E4FUL, 6); MD5STEP(F4, d, a, b, c, in[15], 0xFE2CE6E0UL, 10); MD5STEP(F4, c, d, a, b, in[6], 0xA3014314UL, 15); MD5STEP(F4, b, c, d, a, in[13], 0x4E0811A1UL, 21); MD5STEP(F4, a, b, c, d, in[4], 0xF7537E82UL, 6); MD5STEP(F4, d, a, b, c, in[11], 0xBD3AF235UL, 10); MD5STEP(F4, c, d, a, b, in[2], 0x2AD7D2BBUL, 15); MD5STEP(F4, b, c, d, a, in[9], 0xEB86D391UL, 21); hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; } static inline void md5_transform(u32 digest[MD5_DIGEST_WORDS], u32 block[MD5_BLOCK_WORDS]) { int i; for (i = 0; i < MD5_BLOCK_WORDS; i++) block[i] = cpu_to_le32(block[i]); __md5_transform(digest, block); } void md5_init(struct md5_ctx *ctx) { ctx->digest[0] = 0x67452301UL; ctx->digest[1] = 0xEFCDAB89UL; ctx->digest[2] = 0x98BADCFEUL; ctx->digest[3] = 0x10325476UL; ctx->count = 0; } void md5_update(struct md5_ctx *ctx, const void *data_in, size_t len) { const size_t offset = ctx->count & 0x3f; const size_t avail = MD5_BLOCK_BYTES - offset; const u8 *data = data_in; ctx->count += len; if (avail > len) { memcpy((u8 *)ctx->block + offset, data, len); return; } memcpy((u8 *)ctx->block + offset, data, avail); md5_transform(ctx->digest, ctx->block); data += avail; len -= avail; while (len >= MD5_BLOCK_BYTES) { memcpy(ctx->block, data, MD5_BLOCK_BYTES); md5_transform(ctx->digest, ctx->block); data += MD5_BLOCK_BYTES; len -= MD5_BLOCK_BYTES; } if (len) memcpy(ctx->block, data, len); } void md5_final(struct md5_ctx *ctx, u8 *out) { const size_t offset = ctx->count & 0x3f; char *p = (char *)ctx->block + offset; int padding = (MD5_BLOCK_BYTES - MD5_COUNTER_BYTES) - (offset + 1); int i; *p++ = 0x80; if (padding < 0) { memset(p, 0, (padding + MD5_COUNTER_BYTES)); md5_transform(ctx->digest, ctx->block); p = (char *)ctx->block; padding = (MD5_BLOCK_BYTES - MD5_COUNTER_BYTES); } memset(p, 0, padding); /* 64-bit bit counter stored in LSW/LSB format */ ctx->block[14] = cpu_to_le32(ctx->count << 3); ctx->block[15] = cpu_to_le32(ctx->count >> 29); md5_transform(ctx->digest, ctx->block); for (i = 0; i < MD5_DIGEST_WORDS; i++) ctx->digest[i] = le32_to_cpu(ctx->digest[i]); memcpy(out, ctx->digest, MD5_DIGEST_BYTES); memset(ctx, 0, sizeof(*ctx)); } iscsitarget-1.4.20.3+svn502/usr/md5.h000066400000000000000000000026241233224651300167650ustar00rootroot00000000000000/* * This is the header file for the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' * header definitions; now uses stuff from dpkg's config.h * - Ian Jackson . * Still in the public domain. */ #ifndef MD5_H #define MD5_H #include "types.h" #include #define MD5_BLOCK_WORDS 16 #define MD5_BLOCK_BYTES (MD5_BLOCK_WORDS * 4) #define MD5_DIGEST_WORDS 4 #define MD5_DIGEST_BYTES (MD5_DIGEST_WORDS * 4) #define MD5_COUNTER_BYTES 8 struct md5_ctx { u32 block[MD5_BLOCK_WORDS]; u32 digest[MD5_DIGEST_WORDS]; u64 count; }; void md5_init(struct md5_ctx *ctx); void md5_update(struct md5_ctx *ctx, const void *data_in, size_t len); void md5_final(struct md5_ctx *ctx, u8 *out); #endif /* !MD5_H */ iscsitarget-1.4.20.3+svn502/usr/message.c000066400000000000000000000127061233224651300177210ustar00rootroot00000000000000/* * message.c - ietd inter-process communication * * Copyright (C) 2004-2005 FUJITA Tomonori * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include "iscsid.h" #include "ietadm.h" int ietadm_request_listen(void) { int fd, err; struct sockaddr_un addr; fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) return fd; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; memcpy((char *) &addr.sun_path + 1, IETADM_NAMESPACE, strlen(IETADM_NAMESPACE)); if ((err = bind(fd, (struct sockaddr *) &addr, sizeof(addr))) < 0) return err; if ((err = listen(fd, 32)) < 0) return err; return fd; } static void ietadm_request_exec(struct ietadm_req *req, struct ietadm_rsp *rsp, void **rsp_data, size_t *rsp_data_sz) { int err = 0; log_debug(1, "%u %u %" PRIu64 " %u %u", req->rcmnd, req->tid, req->sid, req->cid, req->lun); switch (req->rcmnd) { case C_TRGT_NEW: err = cops->target_add(&req->tid, req->u.trgt.name); break; case C_TRGT_DEL: err = cops->target_del(req->tid); break; case C_TRGT_UPDATE: if (req->u.trgt.type & (1 << key_session)) err = cops->param_set(req->tid, req->sid, key_session, req->u.trgt.session_partial, req->u.trgt.session_param); if (err < 0) goto out; if (req->u.trgt.type & (1 << key_target)) err = cops->param_set(req->tid, req->sid, key_target, req->u.trgt.target_partial, req->u.trgt.target_param); break; case C_TRGT_SHOW: err = ki->param_get(req->tid, req->sid, key_target, req->u.trgt.target_param); break; case C_TRGT_REDIRECT: err = cops->target_redirect(req->tid, req->u.redir.dest, ISCSI_STATUS_TGT_MOVED_TEMP); break; case C_SESS_NEW: break; case C_SESS_DEL: err = ki->session_destroy(req->tid, req->sid); break; case C_SESS_UPDATE: break; case C_SESS_SHOW: err = ki->param_get(req->tid, req->sid, key_session, req->u.trgt.session_param); break; case C_LUNIT_NEW: err = cops->lunit_add(req->tid, req->lun, req->u.lunit.args); break; case C_LUNIT_DEL: err = cops->lunit_del(req->tid, req->lun); break; case C_LUNIT_UPDATE: case C_LUNIT_SHOW: break; case C_CONN_NEW: break; case C_CONN_DEL: err = ki->conn_destroy(req->tid, req->sid, req->cid); break; case C_CONN_UPDATE: case C_CONN_SHOW: break; case C_ACCT_NEW: err = cops->account_add(req->tid, req->u.acnt.auth_dir, req->u.acnt.u.user.name, req->u.acnt.u.user.pass); break; case C_ACCT_DEL: err = cops->account_del(req->tid, req->u.acnt.auth_dir, req->u.acnt.u.user.name); break; case C_ACCT_LIST: *rsp_data = malloc(req->u.acnt.u.list.alloc_len); if (!*rsp_data) { err = -ENOMEM; break; } *rsp_data_sz = req->u.acnt.u.list.alloc_len; memset(*rsp_data, 0x0, *rsp_data_sz); err = cops->account_list(req->tid, req->u.acnt.auth_dir, &req->u.acnt.u.list.count, &req->u.acnt.u.list.overflow, *rsp_data, *rsp_data_sz); break; case C_ACCT_UPDATE: break; case C_ACCT_SHOW: err = cops->account_query(req->tid, req->u.acnt.auth_dir, req->u.acnt.u.user.name, req->u.acnt.u.user.pass); break; case C_SYS_NEW: break; case C_SYS_DEL: if (!err) isns_exit(); break; case C_SYS_UPDATE: case C_SYS_SHOW: break; default: break; } out: rsp->err = err; } int ietadm_request_handle(int accept_fd) { struct sockaddr addr; struct ucred cred; int fd, err; socklen_t len; struct ietadm_req req; struct ietadm_rsp rsp; struct iovec iov[3]; void *rsp_data = NULL; size_t rsp_data_sz; memset(&rsp, 0, sizeof(rsp)); len = sizeof(addr); if ((fd = accept(accept_fd, (struct sockaddr *) &addr, &len)) < 0) { if (errno == EINTR) err = -EINTR; else err = -EIO; goto out; } len = sizeof(cred); if ((err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *) &cred, &len)) < 0) { rsp.err = -EPERM; goto send; } if (cred.uid || cred.gid) { rsp.err = -EPERM; goto send; } if ((err = read(fd, &req, sizeof(req))) != sizeof(req)) { if (err >= 0) err = -EIO; goto out; } ietadm_request_exec(&req, &rsp, &rsp_data, &rsp_data_sz); send: iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); iov[1].iov_base = &rsp; iov[1].iov_len = sizeof(rsp); iov[2].iov_base = rsp.err ? NULL : rsp_data; iov[2].iov_len = iov[2].iov_base ? rsp_data_sz : 0; err = writev(fd, iov, 2 + !!iov[2].iov_len); out: if (fd > 0) close(fd); if (rsp_data) free(rsp_data); return err; } iscsitarget-1.4.20.3+svn502/usr/misc.h000066400000000000000000000050171233224651300172320ustar00rootroot00000000000000/* * Released under the terms of the GNU GPL v2.0. */ #ifndef MISC_H #define MISC_H struct __qelem { struct __qelem *q_forw; struct __qelem *q_back; }; /* stolen list stuff from Linux kernel */ #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct __qelem name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->q_forw = (ptr); (ptr)->q_back = (ptr); \ } while (0) static inline int list_empty(const struct __qelem *head) { return head->q_forw == head; } static inline int list_length_is_one(const struct __qelem *head) { return (!list_empty(head) && head->q_forw == head->q_back); } #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->q_forw, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.q_forw, typeof(*pos), member)) #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->q_forw, typeof(*pos), member), \ n = list_entry(pos->member.q_forw, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.q_forw, typeof(*n), member)) #define list_del(elem) remque(elem) #define list_del_init(elem) do { \ remque(elem); \ INIT_LIST_HEAD(elem); \ } while (0) #define list_add(new, head) insque (new, head) #define list_add_tail(new, head) insque(new, (head)->q_back) /* min()/max() that do strict type-checking. Lifted from the kernel. */ #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) #define max(x, y) ({ \ typeof(x) _max1 = (x); \ typeof(y) _max2 = (y); \ (void) (&_max1 == &_max2); \ _max1 > _max2 ? _max1 : _max2; }) /* ... and their non-checking counterparts, also taken from the kernel. */ #define min_t(type, x, y) ({ \ type __min1 = (x); \ type __min2 = (y); \ __min1 < __min2 ? __min1: __min2; }) #define max_t(type, x, y) ({ \ type __max1 = (x); \ type __max2 = (y); \ __max1 > __max2 ? __max1: __max2; }) #ifndef IPV6_V6ONLY #define IPV6_V6ONLY 26 #endif #endif iscsitarget-1.4.20.3+svn502/usr/param.c000066400000000000000000000144331233224651300173740ustar00rootroot00000000000000/* * (C) 2005 FUJITA Tomonori * * This code is licenced under the GPL. */ #include #include #include #include #include #include "iscsid.h" int param_index_by_name(char *name, struct iscsi_key *keys) { int i, err = -ENOENT; for (i = 0; keys[i].name; i++) { if (!strcasecmp(keys[i].name, name)) { err = i; break; } } return err; } void param_set_defaults(struct iscsi_param *params, struct iscsi_key *keys) { int i; for (i = 0; keys[i].name; i++) params[i].val = keys[i].def; } static int range_val_to_str(unsigned int val, char *str) { sprintf(str, "%u", val); return 0; } static int range_str_to_val(char *str, unsigned int *val) { *val = strtol(str, NULL, 0); return 0; } static int bool_val_to_str(unsigned int val, char *str) { int err = 0; switch (val) { case 0: strcpy(str, "No"); break; case 1: strcpy(str, "Yes"); break; default: err = -EINVAL; } return err; } static int bool_str_to_val(char *str, unsigned int *val) { int err = 0; if (!strcmp(str, "Yes")) *val = 1; else if (!strcmp(str, "No")) *val = 0; else err = -EINVAL; return err; } static void or_set_val(struct iscsi_param *param, int idx, unsigned int *val) { *val |= param[idx].val; param[idx].val = *val; } static void and_set_val(struct iscsi_param *param, int idx, unsigned int *val) { *val &= param[idx].val; param[idx].val = *val; } static int minimum_check_val(struct iscsi_key *key, unsigned int *val) { int err = 0; if (*val < key->min || key->max < *val) { *val = key->min; err = -EINVAL; } return 0; } static int maximum_check_val(struct iscsi_key *key, unsigned int *val) { int err = 0; if (*val < key->min || key->max < *val) { *val = key->max; err = -EINVAL; } return 0; } static void minimum_set_val(struct iscsi_param *param, int idx, unsigned int *val) { if (*val > param[idx].val) *val = param[idx].val; else param[idx].val = *val; } static void maximum_set_val(struct iscsi_param *param, int idx, unsigned int *val) { if (param[idx].val > *val) *val = param[idx].val; else param[idx].val = *val; } static int digest_val_to_str(unsigned int val, char *str) { int err = 0; if (val & DIGEST_NONE) strcpy(str, "None"); if (val & DIGEST_CRC32C) { if (strlen(str)) strcat(str, ",CRC32C"); else strcpy(str, "CRC32C"); } if (!strlen(str)) err = -EINVAL; return err; } static int digest_str_to_val(char *str, unsigned int *val) { int err = 0; char *p, *q; p = str; *val = 0; do { q = strsep(&p, ","); if (!strcmp(q, "None")) *val |= DIGEST_NONE; else if (!strcmp(q, "CRC32C")) *val |= DIGEST_CRC32C; else { err = -EINVAL; break; } } while (p); return err; } static void digest_set_val(struct iscsi_param *param, int idx, unsigned int *val) { if (*val & DIGEST_CRC32C && param[idx].val & DIGEST_CRC32C) *val = DIGEST_CRC32C; else *val = DIGEST_NONE; param[idx].val = *val; } static int marker_val_to_str(unsigned int val, char *str) { if (val == 0) strcpy(str, "Irrelevant"); else strcpy(str, "Reject"); return 0; } static void marker_set_val(struct iscsi_param *param, int idx, unsigned int *val) { if ((idx == key_ofmarkint && param[key_ofmarker].state == KEY_STATE_DONE) || (idx == key_ifmarkint && param[key_ifmarker].state == KEY_STATE_DONE)) *val = 0; else *val = 1; param[idx].val = *val; } int param_val_to_str(struct iscsi_key *keys, int idx, unsigned int val, char *str) { if (keys[idx].ops->val_to_str) return keys[idx].ops->val_to_str(val, str); else return 0; } int param_str_to_val(struct iscsi_key *keys, int idx, char *str, unsigned int *val) { if (keys[idx].ops->str_to_val) return keys[idx].ops->str_to_val(str, val); else return 0; } int param_check_val(struct iscsi_key *keys, int idx, unsigned int *val) { if (keys[idx].ops->check_val) return keys[idx].ops->check_val(&keys[idx], val); else return 0; } void param_set_val(struct iscsi_key *keys, struct iscsi_param *param, int idx, unsigned int *val2) { if (keys[idx].ops->set_val) keys[idx].ops->set_val(param, idx, val2); } static struct iscsi_key_ops minimum_ops = { .val_to_str = range_val_to_str, .str_to_val = range_str_to_val, .check_val = minimum_check_val, .set_val = minimum_set_val, }; static struct iscsi_key_ops maximum_ops = { .val_to_str = range_val_to_str, .str_to_val = range_str_to_val, .check_val = maximum_check_val, .set_val = maximum_set_val, }; static struct iscsi_key_ops or_ops = { .val_to_str = bool_val_to_str, .str_to_val = bool_str_to_val, .set_val = or_set_val, }; static struct iscsi_key_ops and_ops = { .val_to_str = bool_val_to_str, .str_to_val = bool_str_to_val, .set_val = and_set_val, }; static struct iscsi_key_ops digest_ops = { .val_to_str = digest_val_to_str, .str_to_val = digest_str_to_val, .set_val = digest_set_val, }; static struct iscsi_key_ops marker_ops = { .val_to_str = marker_val_to_str, .set_val = marker_set_val, }; #define SET_KEY_VALUES(x) DEFAULT_##x,MIN_##x, MAX_##x struct iscsi_key target_keys[] = { {"Wthreads", SET_KEY_VALUES(NR_WTHREADS), &minimum_ops}, {"Type", 0, 0, 16, &minimum_ops}, {"QueuedCommands", SET_KEY_VALUES(NR_QUEUED_CMNDS), &minimum_ops}, {"NOPInterval", SET_KEY_VALUES(NOP_INTERVAL), &minimum_ops}, {"NOPTimeout", SET_KEY_VALUES(NOP_TIMEOUT), &minimum_ops}, {NULL,}, }; struct iscsi_key session_keys[] = { {"InitialR2T", 1, 0, 1, &or_ops}, {"ImmediateData", 1, 0, 1, &and_ops}, {"MaxConnections", 1, 1, 65535, &minimum_ops}, {"MaxRecvDataSegmentLength", 8192, 512, 16777215, &minimum_ops}, {"MaxXmitDataSegmentLength", 8192, 512, 16777215, &minimum_ops}, {"MaxBurstLength", 262144, 512, 16777215, &minimum_ops}, {"FirstBurstLength", 65536, 512, 16777215, &minimum_ops}, {"DefaultTime2Wait", 2, 0, 3600, &maximum_ops}, {"DefaultTime2Retain", 20, 0, 3600, &minimum_ops}, {"MaxOutstandingR2T", 1, 1, 65535, &minimum_ops}, {"DataPDUInOrder", 1, 0, 1, &or_ops}, {"DataSequenceInOrder", 1, 0, 1, &or_ops}, {"ErrorRecoveryLevel", 0, 0, 2, &minimum_ops}, {"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, &digest_ops}, {"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, &digest_ops}, {"OFMarker", 0, 0, 1, &and_ops}, {"IFMarker", 0, 0, 1, &and_ops}, {"OFMarkInt", 2048, 1, 65535, &marker_ops}, {"IFMarkInt", 2048, 1, 65535, &marker_ops}, {NULL,}, }; iscsitarget-1.4.20.3+svn502/usr/param.h000066400000000000000000000021021233224651300173670ustar00rootroot00000000000000/* * (C) 2005 FUJITA Tomonori * * This code is licenced under the GPL. */ #ifndef PARAMS_H #define PARAMS_H struct iscsi_key; struct iscsi_param { int state; unsigned int val; }; struct iscsi_key_ops { int (*val_to_str)(unsigned int, char *); int (*str_to_val)(char *, unsigned int *); int (*check_val)(struct iscsi_key *, unsigned int *); void (*set_val)(struct iscsi_param *, int, unsigned int *); }; struct iscsi_key { char *name; unsigned int def; unsigned int min; unsigned int max; struct iscsi_key_ops *ops; }; extern struct iscsi_key session_keys[]; extern struct iscsi_key target_keys[]; extern void param_set_defaults(struct iscsi_param *, struct iscsi_key *); extern int param_index_by_name(char *, struct iscsi_key *); extern int param_val_to_str(struct iscsi_key *, int, unsigned int, char *); extern int param_str_to_val(struct iscsi_key *, int, char *, unsigned int *); extern int param_check_val(struct iscsi_key *, int, unsigned int *); extern void param_set_val(struct iscsi_key *, struct iscsi_param *, int, unsigned int *); #endif iscsitarget-1.4.20.3+svn502/usr/plain.c000066400000000000000000000437361233224651300174070ustar00rootroot00000000000000/* * plain.c - ietd plain file-based configuration * * Copyright (C) 2004-2005 FUJITA Tomonori * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsid.h" #define BUFSIZE 4096 #define CONFIG_FILE "ietd.conf" #define CONFIG_DIR "/etc/iet/" #define INI_ALLOW_FILE "initiators.allow" #define INI_DENY_FILE "initiators.deny" #define TGT_ALLOW_FILE "targets.allow" /* * Account configuration code */ struct user { struct __qelem ulist; u32 tid; char *name; char *password; }; int is_addr_valid(char *addr) { struct in_addr ia; struct in6_addr ia6; char tmp[NI_MAXHOST + 1], *p = tmp, *q; snprintf(tmp, sizeof(tmp), "%s", addr); if (inet_pton(AF_INET, p, &ia) == 1) return 1; if (*p == '[') { p++; q = p + strlen(p) - 1; if (*q != ']') return 0; *q = '\0'; } if (inet_pton(AF_INET6, p, &ia6) == 1) return 1; return 0; } /* this is the orignal Ardis code. */ static char *target_sep_string(char **pp) { char *p = *pp; char *q; for (p = *pp; isspace(*p); p++) ; for (q = p; *q && !isspace(*q); q++) ; if (*q) *q++ = 0; else p = NULL; *pp = q; return p; } static struct iscsi_key user_keys[] = { {"IncomingUser",}, {"OutgoingUser",}, {NULL,}, }; static struct __qelem discovery_users_in = LIST_HEAD_INIT(discovery_users_in); static struct __qelem discovery_users_out = LIST_HEAD_INIT(discovery_users_out); #define HASH_ORDER 4 #define acct_hash(x) ((x) & ((1 << HASH_ORDER) - 1)) static struct __qelem trgt_acct_in[1 << HASH_ORDER]; static struct __qelem trgt_acct_out[1 << HASH_ORDER]; static struct __qelem *account_list_get(u32 tid, int dir) { struct __qelem *list = NULL; if (tid) { list = (dir == AUTH_DIR_INCOMING) ? &trgt_acct_in[acct_hash(tid)] : &trgt_acct_out[acct_hash(tid)]; } else list = (dir == AUTH_DIR_INCOMING) ? &discovery_users_in : &discovery_users_out; return list; } /* Return the first account if the length of name is zero */ static struct user *account_lookup_by_name(u32 tid, int dir, char *name) { struct __qelem *list = account_list_get(tid, dir); struct user *user = NULL; list_for_each_entry(user, list, ulist) { fprintf(stderr, "%u %s %s\n", user->tid, user->password, user->name); if (user->tid != tid) continue; if (!strlen(name)) return user; if (!strcmp(user->name, name)) return user; } return NULL; } static int plain_account_query(u32 tid, int dir, char *name, char *pass) { struct user *user; if (!(user = account_lookup_by_name(tid, dir, name))) return -ENOENT; if (!strlen(name)) strncpy(name, user->name, ISCSI_NAME_LEN); strncpy(pass, user->password, ISCSI_NAME_LEN); return 0; } static int plain_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow, char *buf, size_t buf_sz) { struct __qelem *list = account_list_get(tid, dir); struct user *user; *cnt = *overflow = 0; if (!list) return -ENOENT; list_for_each_entry(user, list, ulist) { if (user->tid != tid) continue; if (buf_sz >= ISCSI_NAME_LEN) { strncpy(buf, user->name, ISCSI_NAME_LEN); buf_sz -= ISCSI_NAME_LEN; buf += ISCSI_NAME_LEN; *cnt += 1; } else *overflow += 1; } return 0; } static void account_destroy(struct user *user) { if (!user) return; remque(&user->ulist); free(user->name); free(user->password); free(user); } static int plain_account_del(u32 tid, int dir, char *name) { struct user *user; if (!name || !(user = account_lookup_by_name(tid, dir, name))) return -ENOENT; account_destroy(user); /* update the file here. */ return 0; } static struct user *account_create(void) { struct user *user; if (!(user = malloc(sizeof(*user)))) return NULL; memset(user, 0, sizeof(*user)); INIT_LIST_HEAD(&user->ulist); return user; } static int plain_account_add(u32 tid, int dir, char *name, char *pass) { int err = -ENOMEM; struct user *user; struct __qelem *list; if (!name || !pass) return -EINVAL; if (tid) { /* check here */ /* return -ENOENT; */ } if (!(user = account_create()) || !(user->name = strdup(name)) || !(user->password = strdup(pass))) goto out; user->tid = tid; list = account_list_get(tid, dir); if (dir == AUTH_DIR_OUTGOING) { struct user *old, *tmp; list_for_each_entry_safe(old, tmp, list, ulist) { if (tid != old->tid) continue; log_warning("Only one outgoing %s account is supported." " Replacing the old one.\n", tid ? "target" : "discovery"); account_destroy(old); } } insque(user, list); /* update the file here. */ return 0; out: account_destroy(user); return err; } /* * Access control code */ static int netmask_match_v6(struct sockaddr *sa1, struct sockaddr *sa2, uint32_t mbit) { uint16_t mask, a1[8], a2[8]; int i; for (i = 0; i < 8; i++) { a1[i] = ntohs(((struct sockaddr_in6 *) sa1)->sin6_addr.s6_addr16[i]); a2[i] = ntohs(((struct sockaddr_in6 *) sa2)->sin6_addr.s6_addr16[i]); } for (i = 0; i < mbit / 16; i++) if (a1[i] ^ a2[i]) return 0; if (mbit % 16) { mask = ~((1 << (16 - (mbit % 16))) - 1); if ((mask & a1[mbit / 16]) ^ (mask & a2[mbit / 16])) return 0; } return 1; } static int netmask_match_v4(struct sockaddr *sa1, struct sockaddr *sa2, uint32_t mbit) { uint32_t s1, s2, mask = ~((1 << (32 - mbit)) - 1); s1 = htonl(((struct sockaddr_in *) sa1)->sin_addr.s_addr); s2 = htonl(((struct sockaddr_in *) sa2)->sin_addr.s_addr); if (~mask & s1) return 0; if (!((mask & s2) ^ (mask & s1))) return 1; return 0; } static int netmask_match(struct sockaddr *sa1, struct sockaddr *sa2, char *buf) { uint32_t mbit; uint8_t family = sa1->sa_family; errno = 0; mbit = strtoul(buf, NULL, 0); if ((mbit == ULONG_MAX && errno != 0) || (family == AF_INET && mbit > 31) || (family == AF_INET6 && mbit > 127)) return 0; if (family == AF_INET) return netmask_match_v4(sa1, sa2, mbit); return netmask_match_v6(sa1, sa2, mbit); } static int address_match(struct sockaddr *sa1, struct sockaddr *sa2) { if (sa1->sa_family == AF_INET) return ((struct sockaddr_in *) sa1)->sin_addr.s_addr == ((struct sockaddr_in *) sa2)->sin_addr.s_addr; else { struct in6_addr *a1, *a2; a1 = &((struct sockaddr_in6 *) sa1)->sin6_addr; a2 = &((struct sockaddr_in6 *) sa2)->sin6_addr; return (a1->s6_addr32[0] == a2->s6_addr32[0] && a1->s6_addr32[1] == a2->s6_addr32[1] && a1->s6_addr32[2] == a2->s6_addr32[2] && a1->s6_addr32[3] == a2->s6_addr32[3]); } return 0; } static int initiator_match(char *initiator, char *p) { int match = 0; char pattern[strlen(p) + 3]; regex_t re; snprintf(pattern, sizeof(pattern), "^%s$", p); if (regcomp(&re, pattern, REG_NOSUB)) return 0; match = !regexec(&re, initiator, (size_t) 0, NULL, 0); regfree(&re); return match; } static int __match(struct sockaddr *sa, char *initiator, char *str) { struct addrinfo hints, *res; char *p, *q; int match = 0; while ((p = strsep(&str, ","))) { while (isspace(*p)) p++; if (!*p) continue; q = p + (strlen(p) - 1); while (isspace(*q)) *(q--) = '\0'; if (!strcmp(p, "ALL")) return 1; if (initiator) if (initiator_match(initiator, p)) return 1; if (*p == '[') { p++; if (!(q = strchr(p, ']'))) return 0; *(q++) = '\0'; } else q = p; if ((q = strchr(q, '/'))) *(q++) = '\0'; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(p, NULL, &hints, &res) == 0) { if (q) match = netmask_match(res->ai_addr, sa, q); else match = address_match(res->ai_addr, sa); freeaddrinfo(res); } if (match) break; } return match; } static int match(u32 tid, struct sockaddr *sa, char *initiator, char *filename) { int err = -EPERM; FILE *fp; char buf[BUFSIZE], fname[PATH_MAX], *p, *q; snprintf(fname, sizeof(fname), "%s%s", CONFIG_DIR, filename); if (!(fp = fopen(fname, "r"))) { if (errno != ENOENT) return -errno; snprintf(fname, sizeof(fname), "%s%s", "/etc/", filename); fp = fopen(fname, "r"); if (!fp) return -errno; } /* * Every time we are called, we read the file. So we don't need to * implement 'reload feature'. It's slow, however, it doesn't matter. */ while ((p = fgets(buf, sizeof(buf), fp))) { struct target *target; q = &buf[strlen(buf) - 1]; if (*q != '\n') continue; else *q = '\0'; q = p; p = target_sep_string(&q); if (!p || *p == '#') continue; if (strcmp(p, "ALL")) { if (!(target = target_find_by_name(p))) continue; if (target->tid != tid) continue; } if (__match(sa, initiator, q)) err = 0; break; } fclose(fp); return err; } static int plain_initiator_allow(u32 tid, int fd, char *initiator) { struct sockaddr_storage ss; socklen_t slen = sizeof(struct sockaddr_storage); int allow, deny; memset(&ss, 0, sizeof(ss)); getpeername(fd, (struct sockaddr *) &ss, &slen); allow = match(tid, (struct sockaddr *) &ss, initiator, INI_ALLOW_FILE); deny = match(tid, (struct sockaddr *) &ss, initiator, INI_DENY_FILE); if (deny != -ENOENT) { if (!deny && allow) return 0; else return 1; } return (allow == -ENOENT) ? 1 : !allow; } static int plain_target_allow(u32 tid, struct sockaddr *sa) { int allow; allow = match(tid, sa, NULL, TGT_ALLOW_FILE); return (allow == -ENOENT) ? 1 : !allow; } /* * Main configuration code */ static int __plain_target_redirect(u32 tid, char *dest, u8 type, int update) { struct target *target; char *a = dest, *p = dest + strlen(dest); if (!tid || !dest || !type) return -EINVAL; if (type != ISCSI_STATUS_TGT_MOVED_TEMP && type != ISCSI_STATUS_TGT_MOVED_PERM) return -EINVAL; target = target_find_by_id(tid); if (!target) { log_error("can't find target: %d", tid); return -ENOENT; } while(isspace(*a)) a++; if (strlen(a)) { while(isspace(*(--p))) *p = '\0'; if ((p = strrchr(a, ']'))) p = strchrnul(p, ':'); else p = strchrnul(a, ':'); if (*p) { *(p++) = '\0'; if (!atoi(p)) return -EINVAL; } if (!is_addr_valid(a)) return -EINVAL; log_warning("target %s %s redirected to %s:%s", target->name, (type == ISCSI_STATUS_TGT_MOVED_TEMP) ? "temporarily" : "permanently", a, strlen(p) ? p : "3260"); } else log_warning("target redirection for %s cleared", target->name); target->redirect.type = type; snprintf(target->redirect.addr, sizeof(target->redirect.addr), "%s", a); snprintf(target->redirect.port, sizeof(target->redirect.port), "%s", p); return 0; } static int plain_target_redirect(u32 tid, char *dest, u8 type) { return __plain_target_redirect(tid, dest, type, 1); } static int __plain_target_create(u32 *tid, char *name, int update) { int err; if (target_find_by_name(name)) { log_error("duplicated target %s", name); return -EINVAL; } if ((err = target_add(tid, name)) < 0) return err; if (update) ; /* Update the config file here. */ return err; } static int plain_target_create(u32 *tid, char *name) { return __plain_target_create(tid, name, 1); } static int plain_target_destroy(u32 tid) { int err; if ((err = target_del(tid)) < 0) return err; /* Update the config file here. */ return err; } static int __plain_lunit_create(u32 tid, u32 lun, char *args, int update) { int err; err = ki->lunit_create(tid, lun, args); if (err < 0) { log_error("unable to create logical unit %u in target %u: %d", lun, tid, errno); return err; } if (update) ; return err; } static int plain_lunit_create(u32 tid, u32 lun, char *args) { return __plain_lunit_create(tid, lun, args, 1); } static int plain_lunit_destroy(u32 tid, u32 lun) { int err; err = ki->lunit_destroy(tid, lun); if (err < 0) log_error("unable to delete logical unit %u in target %u: %d", lun, tid, errno); return err; } static int __plain_param_set(u32 tid, u64 sid, int type, u32 partial, struct iscsi_param *param, int update) { int err; err = ki->param_set(tid, sid, type, partial, param); if (err < 0) { log_error ("unable to set parameter (%d:%u) of session %#" PRIx64 " in target %u: %d", type, partial, sid, tid, errno); return err; } if (update) ; return err; } static int plain_param_set(u32 tid, u64 sid, int type, u32 partial, struct iscsi_param *param) { return __plain_param_set(tid, sid, type, partial, param, 1); } static int iscsi_param_partial_set(u32 tid, u64 sid, int type, int key, u32 val) { struct iscsi_param *param; struct iscsi_param session_param[session_key_last]; struct iscsi_param target_param[target_key_last]; if (type == key_session) param = session_param; else param = target_param; memset(param, 0x0, (type == key_session) ? sizeof(session_param) : sizeof(target_param)); param[key].val = val; return __plain_param_set(tid, sid, type, 1 << key, param, 0); } static void plain_portal_init(FILE *fp, int *timeout) { char buf[BUFSIZE]; char *p, *q; char *isns = NULL; int isns_ac = 0; while (fgets(buf, BUFSIZE, fp)) { q = buf; p = target_sep_string(&q); if (!p || *p == '#') continue; if (!strcasecmp(p, "iSNSServer")) { isns = strdup(target_sep_string(&q)); } else if (!strcasecmp(p, "iSNSAccessControl")) { char *str = target_sep_string(&q); if (!strcasecmp(str, "Yes")) isns_ac = 1; } } if (isns) *timeout = isns_init(isns, isns_ac); } static void plain_target_init(FILE *fp) { char buf[BUFSIZE]; char *p, *q; int idx; u32 tid, val; tid = 0; while (fgets(buf, BUFSIZE, fp)) { q = buf; p = target_sep_string(&q); if (!p || *p == '#') continue; if (!strcasecmp(p, "Target")) { tid = 0; if (!(p = target_sep_string(&q))) continue; __plain_target_create(&tid, p, 0); } else if (!strcasecmp(p, "Alias") && tid) { ; } else if (!strcasecmp(p, "MaxSessions") && tid) { struct target * const target = target_find_by_id(tid); if (target) target->max_nr_sessions = strtol(q, &q, 0); else log_warning("Target '%s' not found", q); } else if (!strcasecmp(p, "Lun") && tid) { u32 lun = strtol(q, &q, 10); __plain_lunit_create(tid, lun, q, 0); } else if (!((idx = param_index_by_name(p, target_keys)) < 0) && tid) { val = strtol(q, &q, 0); if (param_check_val(target_keys, idx, &val) < 0) log_warning("%s, %u\n", target_keys[idx].name, val); iscsi_param_partial_set(tid, 0, key_target, idx, val); } else if (!((idx = param_index_by_name(p, session_keys)) < 0) && tid) { char *str = target_sep_string(&q); if (param_str_to_val(session_keys, idx, str, &val) < 0) continue; if (param_check_val(session_keys, idx, &val) < 0) log_warning("%s, %u\n", session_keys[idx].name, val); iscsi_param_partial_set(tid, 0, key_session, idx, val); } } return; } static void plain_account_init(FILE *fp) { char buf[BUFSIZE], *p, *q; u32 tid; int idx; tid = 0; while (fgets(buf, sizeof(buf), fp)) { q = buf; p = target_sep_string(&q); if (!p || *p == '#') continue; if (!strcasecmp(p, "Target")) { struct target *target; tid = 0; if (!(p = target_sep_string(&q))) continue; target = target_find_by_name(p); if (target) tid = target->tid; } else if (!((idx = param_index_by_name(p, user_keys)) < 0)) { char *name, *pass; name = target_sep_string(&q); pass = target_sep_string(&q); if (plain_account_add(tid, idx, name, pass) < 0) fprintf(stderr, "%s %s\n", name, pass); } } return; } static void plain_init(char *params, int *timeout) { FILE *fp; struct stat st; char file1[PATH_MAX], file2[PATH_MAX]; int i; for (i = 0; i < 1 << HASH_ORDER; i++) { INIT_LIST_HEAD(&trgt_acct_in[i]); INIT_LIST_HEAD(&trgt_acct_out[i]); } snprintf(file1, sizeof(file1), "%s%s", CONFIG_DIR, INI_DENY_FILE); snprintf(file2, sizeof(file2), "/etc/%s", INI_DENY_FILE); if (!stat(file1, &st) || !stat(file2, &st)) log_warning("%s is depreciated and will be obsoleted in the next release, please see README.initiators for more information", INI_DENY_FILE); snprintf(file1, sizeof(file1), "%s%s", CONFIG_DIR, CONFIG_FILE); snprintf(file2, sizeof(file2), "/etc/%s", CONFIG_FILE); if (!(fp = fopen(params ? params : file1, "r"))) { if ((fp = fopen(file2, "r"))) log_warning("%s's location is depreciated and will be moved in the next release to %s", file2, file1); else { log_warning("%s not found, configure through ietadm", CONFIG_FILE); return; } } plain_portal_init(fp, timeout); rewind(fp); plain_target_init(fp); rewind(fp); plain_account_init(fp); fclose(fp); return; } struct config_operations plain_ops = { .init = plain_init, .target_add = plain_target_create, .target_del = plain_target_destroy, .lunit_add = plain_lunit_create, .lunit_del = plain_lunit_destroy, .param_set = plain_param_set, .account_add = plain_account_add, .account_del = plain_account_del, .account_query = plain_account_query, .account_list = plain_account_list, .initiator_allow = plain_initiator_allow, .target_allow = plain_target_allow, .target_redirect = plain_target_redirect, }; iscsitarget-1.4.20.3+svn502/usr/session.c000066400000000000000000000067711233224651300177650ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #include #include #include #include #include #include #include #include #include "iscsid.h" static struct session *session_alloc(u32 tid) { struct session *session; struct target *target = target_find_by_id(tid); if (!target) return NULL; if (!(session = malloc(sizeof(*session)))) return NULL; memset(session, 0, sizeof(*session)); session->target = target; INIT_LIST_HEAD(&session->slist); insque(&session->slist, &target->sessions_list); return session; } struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid sid) { struct session *session; struct target *target; if (!(target = target_find_by_id(tid))) return NULL; log_debug(1, "session_find_name: %s %#" PRIx64, iname, sid.id64); list_for_each_entry(session, &target->sessions_list, slist) { if (!memcmp(sid.id.isid, session->sid.id.isid, 6) && !strcmp(iname, session->initiator)) return session; } return NULL; } struct session *session_find_id(u32 tid, u64 sid) { struct session *session; struct target *target; if (!(target = target_find_by_id(tid))) return NULL; log_debug(1, "session_find_id: %#" PRIx64, sid); list_for_each_entry(session, &target->sessions_list, slist) { if (session->sid.id64 == sid) return session; } return NULL; } int session_exist(u32 t_tid, u64 t_sid) { struct session_info info; memset(&info, 0x0, sizeof(info)); info.tid = t_tid; info.sid = t_sid; return !ki->session_info(&info); } int session_create(struct connection *conn) { struct session *session; static u16 tsih = 1; int err; if (!conn->session) { session = session_alloc(conn->tid); if (!session) return -ENOMEM; session->sid = conn->sid; session->sid.id.tsih = tsih; while (session_exist(conn->tid, session->sid.id64)) session->sid.id.tsih++; tsih = session->sid.id.tsih + 1; session->initiator = strdup(conn->initiator); conn->session = session; conn->sid = session->sid; } else { session = conn->session; conn->sid = session->sid; if (session_exist(conn->tid, session->sid.id64)) return 0; } log_debug(1, "session_create: %#" PRIx64, session->sid.id64); err = ki->session_create(conn->tid, session->sid.id64, conn->exp_cmd_sn, conn->max_cmd_sn, session->initiator); if (err < 0 && err != -EEXIST) { log_error("unable to create session %#" PRIx64 " in target %u: %d", session->sid.id64, conn->tid, errno); goto out; } err = ki->param_set(conn->tid, session->sid.id64, key_session, 0, conn->session_param); if (err < 0) log_warning("unable to set parameters for session %#" PRIx64 " in target %u: %d", session->sid.id64, conn->tid, errno); return 0; out: conn->session = NULL; if (session->target) { remque(&session->slist); --session->target->nr_sessions; } free(session->initiator); free(session); return err; } int session_remove(struct session *session) { int err; log_debug(1, "session_remove: %#" PRIx64, session->sid.id64); err = ki->session_destroy(session->target->tid, session->sid.id64); if (err < 0 && err != -ENOENT) { log_error("unable to delete session %#" PRIx64 " in target %u: %d", session->sid.id64, session->target->tid, errno); return err; } if (session->target) { remque(&session->slist); --session->target->nr_sessions; } free(session->initiator); free(session); return 0; } iscsitarget-1.4.20.3+svn502/usr/sha1.c000066400000000000000000000077751233224651300171430ustar00rootroot00000000000000/* * SHA1 Secure Hash Algorithm. * * Derived from cryptoapi implementation. * * Copyright (c) Alan Smithee. * Copyright (c) Andrew McDonald * Copyright (c) Jean-Francois Dive * * 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. * */ #include "sha1.h" /* SHA1 transforms */ #define F1(x,y,z) (z ^ (x & (y ^ z))) /* x ? y : z */ #define F2(x,y,z) (x ^ y ^ z) /* XOR */ #define F3(x,y,z) ((x & y) + (z & (x ^ y))) /* majority */ /* SHA1 per-round constants */ #define K1 0x5A827999UL /* Rounds 0-19: sqrt(2) * 2^30 */ #define K2 0x6ED9EBA1UL /* Rounds 20-39: sqrt(3) * 2^30 */ #define K3 0x8F1BBCDCUL /* Rounds 40-59: sqrt(5) * 2^30 */ #define K4 0xCA62C1D6UL /* Rounds 60-79: sqrt(10) * 2^30 */ static inline u32 rol32(u32 word, unsigned int shift) { return (word << shift) | (word >> (32 - shift)); } static void __sha1_transform(u32 hash[SHA1_DIGEST_WORDS], const u32 in[SHA1_BLOCK_WORDS], u32 W[SHA1_WORKSPACE_WORDS]) { register u32 a, b, c, d, e, t; int i; for (i = 0; i < SHA1_BLOCK_WORDS; i++) W[i] = cpu_to_be32(in[i]); for (i = 0; i < 64; i++) W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1); a = hash[0]; b = hash[1]; c = hash[2]; d = hash[3]; e = hash[4]; for (i = 0; i < 20; i++) { t = F1(b, c, d) + K1 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } for (; i < 40; i ++) { t = F2(b, c, d) + K2 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } for (; i < 60; i ++) { t = F3(b, c, d) + K3 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } for (; i < 80; i ++) { t = F2(b, c, d) + K4 + rol32(a, 5) + e + W[i]; e = d; d = c; c = rol32(b, 30); b = a; a = t; } hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; hash[4] += e; } static inline void sha1_transform(u32 digest[SHA1_DIGEST_WORDS], const u32 block[SHA1_BLOCK_WORDS]) { u32 workspace[SHA1_WORKSPACE_WORDS]; __sha1_transform(digest, block, workspace); } void sha1_init(struct sha1_ctx *ctx) { ctx->digest[0] = 0x67452301UL; ctx->digest[1] = 0xEFCDAB89UL; ctx->digest[2] = 0x98BADCFEUL; ctx->digest[3] = 0x10325476UL; ctx->digest[4] = 0xC3D2E1F0UL; ctx->count = 0; } void sha1_update(struct sha1_ctx *ctx, const void *data_in, size_t len) { const size_t offset = ctx->count & 0x3f; const size_t avail = SHA1_BLOCK_BYTES - offset; const u8 *data = data_in; ctx->count += len; if (avail > len) { memcpy((u8 *)ctx->block + offset, data, len); return; } memcpy((u8 *)ctx->block + offset, data, avail); sha1_transform(ctx->digest, ctx->block); data += avail; len -= avail; while (len >= SHA1_BLOCK_BYTES) { memcpy(ctx->block, data, SHA1_BLOCK_BYTES); sha1_transform(ctx->digest, ctx->block); data += SHA1_BLOCK_BYTES; len -= SHA1_BLOCK_BYTES; } if (len) memcpy(ctx->block, data, len); } void sha1_final(struct sha1_ctx *ctx, u8 *out) { const size_t offset = ctx->count & 0x3f; char *p = (char *)ctx->block + offset; int padding = (SHA1_BLOCK_BYTES - SHA1_COUNTER_BYTES) - (offset + 1); int i; *p++ = 0x80; if (padding < 0) { memset(p, 0, (padding + SHA1_COUNTER_BYTES)); sha1_transform(ctx->digest, ctx->block); p = (char *)ctx->block; padding = (SHA1_BLOCK_BYTES - SHA1_COUNTER_BYTES); } memset(p, 0, padding); /* 64-bit bit counter stored in MSW/MSB format */ ctx->block[14] = cpu_to_be32(ctx->count >> 29); ctx->block[15] = cpu_to_be32(ctx->count << 3); sha1_transform(ctx->digest, ctx->block); for (i = 0; i < SHA1_DIGEST_WORDS; i++) ctx->digest[i] = be32_to_cpu(ctx->digest[i]); memcpy(out, ctx->digest, SHA1_DIGEST_BYTES); memset(ctx, 0, sizeof(*ctx)); } iscsitarget-1.4.20.3+svn502/usr/sha1.h000066400000000000000000000011561233224651300171330ustar00rootroot00000000000000/* * Common values for SHA algorithms */ #ifndef SHA1_H #define SHA1_H #include "types.h" #include #define SHA1_DIGEST_WORDS 5 #define SHA1_DIGEST_BYTES (SHA1_DIGEST_WORDS * 4) #define SHA1_BLOCK_WORDS 16 #define SHA1_BLOCK_BYTES (SHA1_BLOCK_WORDS * 4) #define SHA1_WORKSPACE_WORDS 80 #define SHA1_COUNTER_BYTES 8 struct sha1_ctx { u32 digest[SHA1_DIGEST_WORDS]; u32 block[SHA1_BLOCK_WORDS]; u64 count; }; void sha1_init(struct sha1_ctx *ctx); void sha1_update(struct sha1_ctx *ctx, const void *data_in, size_t len); void sha1_final(struct sha1_ctx *ctx, u8 *out); #endif iscsitarget-1.4.20.3+svn502/usr/target.c000066400000000000000000000166111233224651300175620ustar00rootroot00000000000000/* * target.c - ietd target handling * * Copyright (C) 2002-2003 Ardis Technolgies * Copyright (C) 2004-2010 VMware, Inc. All Rights Reserved. * Copyright (C) 2007-2010 Ross Walker * * This file is part of iSCSI Enterprise Target software. * * Released under the terms of the GNU GPL v2.0. * * 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 St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsid.h" struct __qelem targets_list = LIST_HEAD_INIT(targets_list); extern struct pollfd poll_array[POLL_MAX]; static int is_addr_loopback(char *addr) { struct in_addr ia; struct in6_addr ia6; if (inet_pton(AF_INET, addr, &ia) == 1) return !strncmp(addr, "127.", 4); if (inet_pton(AF_INET6, addr, &ia6) == 1) return IN6_IS_ADDR_LOOPBACK(&ia6); return 0; } static int is_addr_unspecified(char *addr) { struct in_addr ia; struct in6_addr ia6; if (inet_pton(AF_INET, addr, &ia) == 1) return (ia.s_addr == 0); if (inet_pton(AF_INET6, addr, &ia6) == 1) return IN6_IS_ADDR_UNSPECIFIED(&ia6); return 0; } static void target_print_addr(struct connection *conn, char *addr, int family) { char taddr[NI_MAXHOST + NI_MAXSERV + 5]; /* strip ipv6 zone id */ addr = strsep(&addr, "%"); snprintf(taddr, sizeof(taddr), (family == AF_INET) ? "%s:%d,1" : "[%s]:%d,1", addr, server_port); text_key_add(conn, "TargetAddress", taddr); } void target_list_build_ifaddrs(struct connection *conn, u32 tid, char *addr, int family) { struct ifaddrs *ifaddr, *ifa; char if_addr[NI_MAXHOST]; getifaddrs(&ifaddr); for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) continue; int sa_family = ifa->ifa_addr->sa_family; if (sa_family == family) { if (getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), if_addr, sizeof(if_addr), NULL, 0, NI_NUMERICHOST)) continue; if (strcmp(addr, if_addr) && !is_addr_loopback(if_addr) && cops->target_allow(tid, ifa->ifa_addr)) target_print_addr(conn, if_addr, family); } } freeifaddrs(ifaddr); } void target_list_build(struct connection *conn, char *name) { struct target *target; struct sockaddr_storage ss1, ss2; socklen_t slen = sizeof(struct sockaddr_storage); char addr1[NI_MAXHOST], addr2[NI_MAXHOST]; int ret, family, i; if (getsockname(conn->fd, (struct sockaddr *) &ss1, &slen)) { log_error("getsockname failed: %m"); return; } ret = getnameinfo((struct sockaddr *) &ss1, slen, addr1, sizeof(addr1), NULL, 0, NI_NUMERICHOST); if (ret) { log_error("getnameinfo failed: %s", (ret == EAI_SYSTEM) ? strerror(errno) : gai_strerror(ret)); return; } family = ss1.ss_family; list_for_each_entry(target, &targets_list, tlist) { if (name && strcmp(target->name, name)) continue; if (!isns_scn_allow(target->tid, conn->initiator) || !cops->initiator_allow(target->tid, conn->fd, conn->initiator) || !cops->target_allow(target->tid, (struct sockaddr *) &ss1)) continue; text_key_add(conn, "TargetName", target->name); target_print_addr(conn, addr1, family); for (i = 0; i < LISTEN_MAX && poll_array[i].fd; i++) { slen = sizeof(struct sockaddr_storage); if (getsockname(poll_array[i].fd, (struct sockaddr *) &ss2, &slen)) continue; if (getnameinfo((struct sockaddr *) &ss2, slen, addr2, sizeof(addr2), NULL, 0, NI_NUMERICHOST)) continue; if (ss2.ss_family != family) continue; if (is_addr_unspecified(addr2)) target_list_build_ifaddrs(conn, target->tid, addr1, family); else if (strcmp(addr1, addr2) && !is_addr_loopback(addr2) && cops->target_allow(target->tid, (struct sockaddr *) &ss2)) target_print_addr(conn, addr2, family); } } } struct target* target_find_by_name(const char *name) { struct target *target; list_for_each_entry(target, &targets_list, tlist) { if (!strcasecmp(target->name, name)) return target; } return NULL; } struct target* target_find_by_id(u32 tid) { struct target *target; list_for_each_entry(target, &targets_list, tlist) { if (target->tid == tid) return target; } return NULL; } static void all_accounts_del(u32 tid, int dir) { char name[ISCSI_NAME_LEN], pass[ISCSI_NAME_LEN]; memset(name, 0, sizeof(name)); for (;cops->account_query(tid, dir, name, pass) != -ENOENT; memset(name, 0, sizeof(name))) { cops->account_del(tid, dir, name); } } int target_del(u32 tid) { struct target *target = target_find_by_id(tid); int err; if (!target) return -ENOENT; if (!list_empty(&target->sessions_list)) { log_warning("%s: target %u still has sessions\n", __FUNCTION__, tid); return -EBUSY; } err = ki->target_destroy(tid); if (err < 0) { log_error("unable to delete target %u: %d", tid, errno); return err; } remque(&target->tlist); all_accounts_del(tid, AUTH_DIR_INCOMING); all_accounts_del(tid, AUTH_DIR_OUTGOING); isns_target_deregister(target->name); free(target); return 0; } int target_add(u32 *tid, char *name) { struct target *target; int err; if (!name) return -EINVAL; if (!(target = malloc(sizeof(*target)))) return -ENOMEM; memset(target, 0, sizeof(*target)); memcpy(target->name, name, sizeof(target->name) - 1); err = ki->target_create(tid, name); if (err < 0) { log_warning("unable create target %u: %d\n", *tid, errno); goto out; } INIT_LIST_HEAD(&target->tlist); INIT_LIST_HEAD(&target->sessions_list); INIT_LIST_HEAD(&target->isns_head); target->tid = *tid; insque(&target->tlist, &targets_list); isns_target_register(name); log_debug(1, "created target %s", name); return 0; out: free(target); return err; } int target_redirected(struct target *target, struct connection *conn, struct sockaddr *sa) { char tmp[NI_MAXHOST + 1]; char addr[NI_MAXHOST + 3]; char redirect[NI_MAXHOST + NI_MAXSERV + 4]; char *p; if (!strlen(target->redirect.addr)) return 0; if (getnameinfo(sa, (sa->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST)) return 0; if ((p = strrchr(tmp, '%'))) *p = '\0'; if (sa->sa_family == AF_INET6) snprintf(addr, sizeof(addr), "[%s]", tmp); else snprintf(addr, sizeof(addr), "%s", tmp); snprintf(redirect, sizeof(redirect), "%s:%s", target->redirect.addr, strlen(target->redirect.port) ? target->redirect.port : "3260"); if (strcmp(target->redirect.addr, addr)) { text_key_add(conn, "TargetAddress", redirect); return 1; } return 0; } iscsitarget-1.4.20.3+svn502/usr/types.h000066400000000000000000000021601233224651300174370ustar00rootroot00000000000000/* * Copyright (C) 2002-2003 Ardis Technolgies * * Released under the terms of the GNU GPL v2.0. */ #ifndef TYPES_H #define TYPES_H #include #include #include #include #include #if __BYTE_ORDER == __BIG_ENDIAN #define cpu_to_le16(x) bswap_16(x) #define le16_to_cpu(x) bswap_16(x) #define cpu_to_le32(x) bswap_32(x) #define le32_to_cpu(x) bswap_32(x) #define cpu_to_be16(x) (x) #define be16_to_cpu(x) (x) #define cpu_to_be32(x) (x) #define be32_to_cpu(x) (x) #define cpu_to_be64(x) (x) #define be64_to_cpu(x) (x) #elif __BYTE_ORDER == __LITTLE_ENDIAN #define cpu_to_le16(x) (x) #define le16_to_cpu(x) (x) #define cpu_to_le32(x) (x) #define le32_to_cpu(x) (x) #define cpu_to_be16(x) bswap_16(x) #define be16_to_cpu(x) bswap_16(x) #define cpu_to_be32(x) bswap_32(x) #define be32_to_cpu(x) bswap_32(x) #define cpu_to_be64(x) bswap_64(x) #define be64_to_cpu(x) bswap_64(x) #else #error "unknown endianess!" #endif typedef u_int8_t u8; typedef u_int16_t u16; typedef u_int32_t u32; typedef u_int64_t u64; #endif /* TYPES_H */