The initial commit of iSCSI-SCST

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@162 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2007-08-15 09:28:54 +00:00
parent 1d3da41d62
commit e6a57baa14
60 changed files with 16637 additions and 0 deletions

View File

@@ -24,22 +24,28 @@ QLA_DIR=qla2x00t/qla2x00-target
LSI_DIR=mpt
USR_DIR=usr/fileio
ISCSI_DIR=iscsi-scst
#ISCSI_DISTDIR=../../../../iscsi_scst_inst
all:
cd $(SCST_DIR) && $(MAKE) $@
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
# @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
@if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
@if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
install:
cd $(SCST_DIR) && $(MAKE) $@
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
# @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
@if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) DISTDIR=$(ISCSI_DISTDIR) $@; fi
@if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
uninstall:
cd $(SCST_DIR) && $(MAKE) $@
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
@if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
@if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
@if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
clean:
@@ -47,6 +53,7 @@ clean:
@if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
@if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
@if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
@if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
extraclean:
@@ -54,6 +61,7 @@ extraclean:
@if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
@if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
@if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
@if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
scst:
@@ -88,6 +96,21 @@ qla_extraclean:
cd $(QLA_INI_DIR)/.. && $(MAKE) extraclean
cd $(QLA_DIR) && $(MAKE) extraclean
iscsi:
cd $(ISCSI_DIR) && $(MAKE)
iscsi_install:
cd $(ISCSI_DIR) && $(MAKE) install
iscsi_uninstall:
cd $(ISCSI_DIR) && $(MAKE) uninstall
iscsi_clean:
cd $(ISCSI_DIR) && $(MAKE) clean
iscsi_extraclean:
cd $(ISCSI_DIR) && $(MAKE) extraclean
lsi:
cd $(LSI_DIR) && $(MAKE)
@@ -137,6 +160,12 @@ help:
@echo " qla_install : 2.6 qla target: install"
@echo " qla_uninstall : 2.6 qla target: uninstall"
@echo ""
@echo " iscsi : make new ISCSI target"
@echo " iscsi_clean : ISCSI target: clean "
@echo " iscsi_extraclean : ISCSI target: clean + clean dependencies"
@echo " iscsi_install : ISCSI target: install"
@echo " iscsi_uninstall : ISCSI target: uninstall"
@echo ""
@echo " lsi : make lsi target"
@echo " lsi_clean : lsi target: clean "
@echo " lsi_extraclean : lsi target: clean + clean dependencies"
@@ -154,5 +183,6 @@ help:
.PHONY: all install uninstall clean extraclean help \
qla qla_install qla_uninstall qla_clean qla_extraclean \
lsi lsi_install lsi_uninstall lsi_clean lsi_extraclean \
iscsi iscsi_install iscsi_uninstall iscsi_clean iscsi_extraclean \
scst scst_install scst_uninstall scst_clean scst_extraclean \
usr usr_install usr_uninstall usr_clean usr_extraclean

341
iscsi-scst/COPYING Normal file
View File

@@ -0,0 +1,341 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

38
iscsi-scst/ChangeLog Normal file
View File

@@ -0,0 +1,38 @@
Summary of changes in iSCSI-SCST since it was IET
-------------------------------------------------
- Commands processing flow changed to work with SCST. Significant
cleanup was done. Modules and config files were renamed to allow to
run with IET on the same host.
- In IET the iSCSI negotiation isn't fully iSCSI RFC confirmed: it doesn't
support ranges of values and, more important, violates RFC in case
when in the IET config file some value for some parameter is set and
a remote initiator doesn't initiate the negotiation for this
parameter or declare its value. According to RFC, in this case IET
shall use the RFC-specified default value, but it uses config file
specified one instead. Looks like the implementation confuses IET
config file default values and iSCSI RFC ones. The default values
handling was fixed. But support for ranges remains unfixed (see ToDo
file).
- All shutdown/restart problems (resource leaks, hangups, etc.), especially
under load, were fixed.
- Full duplex network IO implemented.
- Threading model reimplemented.
- Digests calculation made multi-threaded.
- TX data digest errors handling iSCSI RFC violation was fixed. Another one
(SNACK handling) remains unfixed.
- Ability to send data with sense in a single response added.
- Ability to handle initiators misbehavior a bit improved: several BUG()'s
were relaced by the proper handling. Hovewer, there is a plenty of work in
this area left, IET is known to have a lot of weaknesses here.
- A lot of other bugfixes and code cleanups.

435
iscsi-scst/ChangeLog-IET Normal file
View File

@@ -0,0 +1,435 @@
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

76
iscsi-scst/Makefile Normal file
View File

@@ -0,0 +1,76 @@
#
# 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.
SCST_DIR := $(shell pwd)/../scst/src
SUBDIRS := $(shell pwd)
ifeq ($(KVER),)
ifeq ($(KDIR),)
KVER = $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
else
KVER = $$KERNELRELEASE
endif
else
KDIR ?= /lib/modules/$(KVER)/build
endif
all: progs mods
mods: Modules.symvers Module.symvers
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/kernel modules
progs:
$(MAKE) -C usr
install: all kernel/iscsi-scst.ko usr/iscsi-scstd usr/iscsi-scst-adm
@install -vD usr/iscsi-scstd $(DISTDIR)/usr/local/sbin/iscsi-scstd
@install -vD usr/iscsi-scst-adm $(DISTDIR)/usr/local/sbin/iscsi-scst-adm
if [ -f /etc/debian_version ]; then \
install -vD -m 755 etc/initd/initd.debian $(DISTDIR)/etc/init.d/iscsi-scst; \
elif [ -f /etc/redhat-release ]; then \
install -vD -m 755 etc/initd/initd.redhat $(DISTDIR)/etc/init.d/iscsi-scst; \
elif [ -f /etc/gentoo-release ]; then \
install -vD -m 755 etc/initd/initd.gentoo $(DISTDIR)/etc/init.d/iscsi-scst; \
elif [ -f /etc/slackware-version ]; then \
install -vD -m 755 etc/initd/initd $(DISTDIR)/etc/rc.d/iscsi-scst; \
else \
install -vD -m 755 etc/initd/initd $(DISTDIR)/etc/init.d/iscsi-scst; \
fi
@eval `sed -n 's/#define UTS_RELEASE /KERNELRELEASE=/p' $(KDIR)/include/linux/version.h $(KDIR)/include/linux/utsrelease.h 2>/dev/null`; \
install -vD -m 644 kernel/iscsi-scst.ko \
$(DISTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/iscsi-scst.ko
-/sbin/depmod -aq $(KVER)
SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null)
ifneq ($(SCST_MOD_VERS),)
Modules.symvers: $(SCST_DIR)/Modules.symvers
echo $(SCST_MOD_VERS)
cp $(SCST_DIR)/Modules.symvers kernel/
else
.PHONY: Modules.symvers
endif
# It's renamed in 2.6.18
SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Module.symvers 2>/dev/null)
ifneq ($(SCST_MOD_VERS),)
Module.symvers: $(SCST_DIR)/Module.symvers
cp $(SCST_DIR)/Module.symvers kernel/
else
.PHONY: Module.symvers
endif
clean:
$(MAKE) -C usr clean
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/kernel clean
rm -f kernel/Modules.symvers kernel/Module.symvers
extraclean: clean
.PHONY: all mods progs install clean extraclean

115
iscsi-scst/README Normal file
View File

@@ -0,0 +1,115 @@
iSCSI SCST target driver
========================
Version 0.9.6/XXXX, XX XXX 200X
-------------------------------
This driver is a forked with all respects version of iSCSI Enterprise
Target (IET) (http://iscsitarget.sourceforge.net/) with updates to work
over SCST as well as with many improvements and bugfixes (see ChangeLog
file). The reason of fork is that the necessary changes are intrusive
and with the current IET merge policy, where only simple bugfix-like
patches, which doesn't touch the core code, could be merged, it is very
unlikely that they will be merged in the main IET trunk.
To let it be installed and work at the same host together with IET
simultaneously all the driver's modules and files were renamed:
* ietd.conf -> iscsi-scstd.conf
* ietadm -> iscsi-scst-adm
* ietd -> iscsi-scstd
* iscsi-target -> iscsi-scst
* iscsi-target.ko -> iscsi-scst.ko
This version is compatible with SCST version 0.9.6 and higher.
Tested on 2.6.21.1 kernel, but it should also work on other versions,
starting from 2.6.16.x.
Installation
------------
Basically as in README-IET, where file names are changed as specified
above.
To use full power of TCP zero-copy transmit functions, especially
dealing with user space supplied via scst_user module memory, iSCSI-SCST
needs to be notified when Linux networking finished data transmission.
Patch put_page_callback.patch provides such functionality. The
corresponding version of it should be applied on your kernel. This is
highly recommended, but not required. If it isn't applied, iSCSI-SCST
will work in the performance degraded mode, when for data transmission:
- For in-kernel allocated memory (scst_vdisk and pass-through
handlers) usage of SGV cache on transmit path (READ-type commands)
will be disabled. The performance hit will be not big, performance
will still remain better, than for IET, because SGV cache will remain
used on receive path and IET doesn't have such feature.
- For user space allocated memory (scst_user handler) all transmitted
data will be additionally copied into temporary TCP buffers. The
performance hit will be quite noticiable.
If you need your own version of put_page_callback.patch for your custom
kernel, for which there is no prepared version, you can create it
yourself. For that it is only necessary:
1. Apply the closest version of put_page_callback.patch on your kernel.
Resolve only failed hanks from include/ and net/core/utils.c, ignore
other failures.
2. Grep net/ and replace everywhere, except in net/sunrpc/svc.c and
net/core/pktgen.c, put_page() by net_put_page() and get_page() by
net_get_page().
That's all.
Usage
-----
ISCSI parameters like iSNS, CHAP and target parameters are configured in
iscsi-scstd.conf. All LUN information is configured using the regular
SCST interface. The LUN information in iscsi-scstd.conf will be ignored.
This is because now responsibilities are divided (as it should be)
between the target driver (iSCSI-SCST) and the SCST core. The target
driver is responsible for handling targets and their parameters, SCST
core is responsible for handling backstorage.
If you need to configure different LUs for different targets you should
create for each target group "Default_target_name", where target name
means name of the target, for example:
"Default_iqn.2007-05.com.example:storage.disk1.sys1.xyz", and add there
all necessary LUNs. Check SCST README file for details.
Compilation options
-------------------
There are the following compilation options, that could be commented
in/out in the kernel's module Makefile:
- DEBUG - turns on some debugging code, including some logging. Makes
the driver considerably bigger and slower, producing large amount of
log data.
- TRACING - turns on ability to log events. Makes the driver considerably
bigger and lead to some performance loss.
- EXTRACHECKS - adds extra validity checks in the various places.
- DEBUG_DIGEST_FAILURES - simulates digest failures in random places.
Credits
-------
Thanks to:
* IET developers for IET
* Ming Zhang <blackmagic02881@gmail.com> for fixes
* Krzysztof Blaszkowski <kb@sysmikro.com.pl> for many fixes
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> for comments and help in
debugging
Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net

105
iscsi-scst/README-IET Normal file
View File

@@ -0,0 +1,105 @@
Introduction
-------------
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
-------------
The iSCSI target requires a host running the Linux operating system
with a kernel version of 2.6.19 (2.6.14 - 2.6.18 kernels using
backward compatibility patches, see below) or newer. You need to
enable "Cryptographic API" under "Cryptographic options" in the kernel
config. You 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 user-space code requires
OpenSSL library (http://www.openssl.org/).
The iSCSI target consists of kernel modules and a daemon. Since IET
is generally targetted at the latest stable mainline kernel, you might
need to apply a backward compatibility patch to compile it against
older kernel versions:
patch -p0 < patches/compat-2.6.14-2.6.18.patch
Compilation of the kernel modules require the path to the kernel
sources:
make KSRC=<kernel-src>
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
current running kernel. Be sure to check whether it finds the right
kernel sources.
This will build the modules, the daemon, and the control tool. To
install both, use:
make KSRC=<kernel-src> install
The kernel modules will be install in the module directory of the
kernel. The daemon and the control tool will be installed as ietd and
ietadm under /usr/sbin. The boot script will be installed as
iscsi-targt under /etc/init.d.
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.
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 <iscsitarget-devel@lists.sourceforge.net>.
Developer Notes
----------------
The central resource for IET development is the
<iscsitarget-devel@lists.sourceforge.net> mailing list.
Our subversion repository can be found at: svn://svn.berlios.de/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.

32
iscsi-scst/ToDo Normal file
View File

@@ -0,0 +1,32 @@
- Reimplement sessions parameters negotiation and storage. In IET
the negotiation isn't iSCSI RFC confirmed: it doesn't support ranges
of values and, more important, violates RFC in case when in the IET
config file some value for some parameter is set and a remote
initiator doesn't initiate the negotiation for this parameter or
declare its value. According to RFC, in this case IET shall use the
RFC-specified default value, but it will use config file specified
one instead. Looks like the implementation confuses IET config file
default values and iSCSI ones. The default values handling was fixed
in very dirty and hackish way, but ranges support remains unfixed.
Storage of set by user parameters should be reimplemented, so they
will be kept in iscsi-scstd, not in the kernel, as it is currently
done in IET's code. Using kernel to *only* store parameters is quite
questionable decision, especially considering that it leads to some
code duplication between kernel and user space, so remove all
parameters storage code from kernel. Remove target_param, leave only
one type of parameters, but separate processing of iSCSI RFC
parameters from local ones (currently they are mixed up). Also mixing
up conceptions of "key" and "param" in param_*() and struct
iscsi_key_ops functions makes the code hardly manageable, particularly
for adding support for ranges in the negotiation keys, so it needs to
be fixed as well.
- Fix SNACK command handling. Currently it violates iSCSI RFC.
- Consider better integration with TCP internals on receive path to
improve performance.
- The target shouldn't crash/hang/etc. on initiators' misbehavior as
IET likes to do.
- Minor "ToDo"'s spread in the code.

View File

@@ -0,0 +1,240 @@
.\" 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 "ISCSI_SCST_ADM 1"
.TH ISCSI_SCST_ADM 8 "2007-05" "iSCSI SCST Target admin" "User Manuals"
.SH "NAME"
iscsi-scst-adm \- iSCSI SCST Target Administration Utility.
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
\&\fBiscsi-scst-adm \-\-op [operation] \-\-tid=[id] [\-\-sid [id]] [\-\-params [key=value,...]]\fR
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
iscsi-scst-adm is used to monitor and modify in real-time the iSCSI SCST Target
targets.
.SH "USAGE"
.IX Header "USAGE"
You'll have to get target and sessions ids from /proc/scsi_tgt/iscsi.
Some usage examples can be :
.PP
\&\fBiscsi-scst-adm \-\-op show \-\-tid=1\fR
.PP
display status of target 1 (see /proc/scsi_tgt/iscsi to get the matching target name)
.PP
\&\fBiscsi-scst-adm \-\-op new \-\-tid=2\fR
.PP
create dynamically a new target, numbered 2. \s-1CAUTION\s0 : the target will disappear if you restart iscsi-scstd, you'll have to edit /etc/iscsi-scstd.conf to make it permanent!
.SH "ERROR MESSAGES"
.IX Header "ERROR MESSAGES"
iscsi-scst-adm misses error messages. Look carefully the \s-1STDERR\s0 output : in case of error
it will send a 3 number error code, ending with \-1, for instance :
.PP
iscsi-scstd_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]. [id] must not be zero.
.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 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-1SCST\s0 target parameters of specific
target with [id]. You can use parameters in iscsi-scstd.conf
as a key.
.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 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
iscsi-scst-adm doesn't return any human-readable error message, only error codes.
.IP "\(bu" 4
iscsi-scst-adm doesn't modify or read the /etc/iscsi-scstd.conf iscsi-scstd configuration file.
.IP "\(bu" 4
iscsi-scst-adm can't use target names or aliases, only the tid found in /proc/scsi_tgt/iscsi.
.IP "\(bu" 4
/proc/scsi_tgt/iscsi may list inactive sessions if the initiator doesn't logout properly.
.PP
Report bugs to <iscsitarget\-devel@sourceforge.net>.
.SH "FILES"
.IX Header "FILES"
/proc/scsi_tgt/iscsi
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\fIiscsi-scstd\fR\|(8),\fIiscsi-scstd.conf\fR\|(5)
.\"man page written by Emmanuel Florac <eflorac@intellique.com"
.\"distributed under GPL v2 licence"

View File

@@ -0,0 +1,308 @@
.\" Process this file with
.\" groff -man -Tascii iscsi-scstd.8
.\"
.TH "iSCSI SCST Target Daemon" 8 "May 2007" Linux "User Manuals"
.SH NAME
iscsi-scstd \- iSCSI SCST Target Daemon
.SH SYNOPSIS
.B iscsi-scstd
.RB [\| \-c
.IR configfile \|]
.RB [\| \-d
.IR debuglevel \|]
.RB [\| \-f \|]
.RB [\| \-g
.IR GID \|]
.RB [\| \-h \|]
.RB [\| \-a
.IR address \|]
.RB [\| \-p
.IR port \|]
.RB [\| \-s
.IR IP \|]
.RB [\| \-u
.IR UID \|]
.SH DESCRIPTION
The
.B iscsi-scstd
program implements the user level part of iSCSI SCST Target software for building an iSCSI storage system on Linux.
.SH OPTIONS
.TP
.BI \-c\ filename ,\ \-\-config= filename
Specify configuration file, default is
.I /etc/iscsi-scstd.conf
.TP
.BI \-d\ debuglevel ,\ \-\-debug= debuglevel
Turns on debugging. Logging goes to
.I /var/log/syslog
via
.BR syslog (1).
.TP
.BI \-f,\ \-\-foreground
Act as a normal application which uses a console.
.TP
.BI \-g\ GID ,\ \-\-gid= GID
Specify running group id, default is current gid.
.TP
.BI \-a\ address ,\ \-\-address= address
Specify on which local address the server should listen, default is any.
.TP
.BI \-p\ port ,\ \-\-port= port
Specify on which port the server should listen, default is 3260.
.TP
.BI \-h,\ \-\-help
Display help message on command line options.
.TP
.BI \-s\ IP ,\ \-\-isns= IP
isns server's IP address
.TP
.BI \-u\ UID ,\ \-\-uid= UID
Specify running user id, default is current uid.
.SH FILES
.I /etc/iscsi-scstd.conf
.RS
The system wide configuration file. See
.BR iscsi-scstd.conf (5)
for further details.
.RE
.SH DIAGNOSTICS
This may be logged to stderr:
.B Warnings
"Dropping key (%s=%s)"
.RS
Value(s): key, value
.RE
"unable to set fd flags (%s)!"
.RS
Value(s): strerror(errno)
.RE
"unable to get fd flags (%s)!"
.RS
Value(s): strerror(errno)
.RE
"unable to set SO_REUSEADDR on server socket (%s)!"
.RS
Value(s): strerror(errno)
.RE
"iscsi_ctrl: failed to scan '%s'"
.RS
Value(s): p
.RE
"target %u not found?"
.RS
Value(s): target_id
.RE
"session %#Lx not found?"
.RS
Value(s): session_id
.RE
"session_remove: session %#Lx not found?"
.RS
Value(s): session->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"
.RS
Value(s): target->id
.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
"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 iscsi-scstd.conf (5)

View File

@@ -0,0 +1,210 @@
.\" Process this file with
.\" groff -man -Tascii iscsi-scstd.conf.5
.\"
.TH "ISCSI_SCSTD.CONF" "5" "May 2007" "A. Lehmann, M. Zhang and A. Redlich" "File formats"
.SH "NAME"
/etc/iscsi-scstd.conf \- configuration for iSCSI SCST Target Daemon
.SH "SYNOPSIS"
/etc/iscsi-scstd.conf
.SH "DESCRIPTION"
/etc/iscsi-scstd.conf contains configuration information for the
.B iscsi-scstd (8)
command. This is the place, where you configure your iSCSI targets and daemon parameters.
.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.2007\-05.com.example:storage.disk2.sys1.xyz
IncomingUser jim othersecret
OutgoingUser james yetanothersecret
Alias Test
HeaderDigest None
DataDigest None
MaxConnections 1
InitialR2T No
ImmediateData Yes
MaxRecvDataSegmentLength 1048576
MaxXmitDataSegmentLength 1048576
MaxBurstLength 1048576
FirstBurstLength 1048576
DefaultTime2Wait 2
DefaultTime2Retain 20
MaxOutstandingR2T 20
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 <username> <password>]
The
.I <username>
and
.I <password>
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 <password>
to be 12 characters long. This is enforced e.g. by MS Initiator.
.RE
.TP
.B [OutgoingUser <username> <password>]
The
.I <username>
and
.I <password>
used during discovery sessions to authenticate the target to initiators. Only one outgoing
.I <username>/<password>
combination may be specified.
.RS
HINT: RFC 3720 requires
.I <password>
to be 12 characters long. This is enforced e.g. by MS Initiator.
.RE
.TP
.B Target iqn.<yyyy\-mm>.<tld.domain.some.host>[:<identifier>]
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 <yyyy\-mm>
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 <identifier>
\- 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.2007\-05.com.example.host:storage.disk2.sys1.xyz
.SH "TARGET OPTIONS"
Target options are also case sensitive.
.TP
.B [IncomingUser <username> <password>]
The
.I <username>
and
.I <password>
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 <password>
has to be provided, if there is a
.I <username>
given. Specifying several different
.B IncomingUser
accounts is supported.
.TP
.B [OutgoingUser <username> <password>]
The
.I <username>
and
.I <password>
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 <password>
has to be provided, if there is a
.I <username>
given.
.TP
.B [Alias <aliasname>]
This assigns an optional
.I <aliasname>
to the target.
.TP
.B [HeaderDigest <CRC32C|None>]
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 <CRC32C|None>]
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 <value>]
Optional. Has to be set to "1" (in words: one), which is also the default.
.TP
.B [InitialR2T <Yes|No>]
Optional. If set to "Yes", the initiator has to wait for the target to solicit SCSI data before sending it. Setting it to "No" (default) 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 <Yes|No>]
Optional. This allows the initiator to append unsolicited data to a command. To achieve better performance, this should be set to "Yes". Which is the default.
.TP
.B [MaxRecvDataSegmentLength <value>]
Optional. Sets the maximum data segment length that can be received. The
.I <value>
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 the highes possible for current platform (1 or 2 MB).
.TP
.B [MaxXmitDataSegmentLength <value>]
Optional. Sets the maximum data segment length that can be sent. The
.I <value>
actually used is the minimum of
.B MaxXmitDataSegmentLength
and the
.B MaxRecvDataSegmentLength
announced by the initiator. The
.I <value>
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 the highes possible for current platform (1 or 2 MB).
.TP
.B [MaxBurstLength <value>]
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 <value>
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 the highes possible for current platform (1 or 2 MB).
.TP
.B [FirstBurstLength <value>]
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 <value>
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 the highes possible for current platform (1 or 2 MB).
.TP
.B [DefaultTime2Wait <value>]
Currently not supported.
.TP
.B [DefaultTime2Retain <value>]
Currently not supported.
.TP
.B [MaxOutstandingR2T <value>]
Optional. Controls the maximum number of data transfers the target may request at once, each of up to
.B MaxBurstLength
bytes. The default is 20.
.TP
.B [DataPDUInOrder <Yes|No>]
Optional. Has to be set to "Yes" \- which is also the default.
.TP
.B [DataSequenceInOrder <Yes|No>]
Optional. Has to be set to "Yes" \- which is also the default.
.TP
.B [ErrorRecoveryLevel <value>]
Optional. Has to be set to "0" (in words: zero), which is also the default.
.TP
.B [QueuedCommands <value>]
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 iscsi-scstd (8)
.TP
You should have a look at
.B RFC 3720
for all the glory details.

View File

@@ -0,0 +1,67 @@
#!/bin/bash
#
# Start the iSCSI Enterprise Target.
#
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
MEM_SIZE=1048576
configure_memsize()
{
if [ -e /proc/sys/net/core/wmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
fi
if [ -e /proc/sys/net/core/rmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
fi
if [ -e /proc/sys/net/core/wmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
fi
if [ -e /proc/sys/net/core/rmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
fi
if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
fi
if [ -e /proc/sys/net/ipv4/tcp_rmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
fi
if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
fi
}
start_server()
{
# configure_memsize
modprobe -q crc32c
modprobe iscsi-scst
/usr/local/sbin/iscsi-scstd
}
stop_server()
{
killall iscsi-scstd
rmmod -w iscsi-scst
}
case "$1" in
start)
start_server
;;
stop)
stop_server
;;
*)
echo "Usage: {start|stop}" >&2
exit 1
;;
esac
exit 0

View File

@@ -0,0 +1,133 @@
#!/bin/sh
#
# chkconfig: - 39 35
# description: Starts and stops the iSCSI target
# debianized start-stop script
PID_FILE=/var/run/iscsi-scst.pid
CONFIG_FILE=/etc/iscsi-scstd.conf
DAEMON=/usr/local/sbin/iscsi-scstd
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
# Don't touch this "memsize thingy" unless you are blessed
# with knowledge about it.
MEM_SIZE=1048576
configure_memsize()
{
if [ -e /proc/sys/net/core/wmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
fi
if [ -e /proc/sys/net/core/rmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
fi
if [ -e /proc/sys/net/core/wmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
fi
if [ -e /proc/sys/net/core/rmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
fi
if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
fi
if [ -e /proc/sys/net/ipv4/tcp_rmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
fi
if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
fi
}
RETVAL=0
iscsi_scstd_start()
{
echo -n "Starting iSCSI enterprise target service: "
# configure_memsize
modprobe -q crc32c
modprobe iscsi-scst
start-stop-daemon --start --exec $DAEMON --quiet
RETVAL=$?
if [ $RETVAL == "0" ]; then
echo "succeeded."
else
echo "failed."
fi
}
iscsi_scstd_stop()
{
echo -n "Stopping iSCSI enterprise target service: "
start-stop-daemon --stop --quiet --exec $DAEMON --pidfile $PID_FILE
RETVAL=$?
if [ $RETVAL == "0" ]; then
echo "succeeded."
else
echo "failed."
fi
# ugly, but pid file is not removed ba iscsi-scstd
rm -f $PID_FILE
echo -n "Removing iSCSI enterprise target modules: "
rmmod -w iscsi-scst
RETVAL=$?
modprobe -r crc32c 2>/dev/null
if [ $RETVAL == "0" ]; then
echo "succeeded."
else
echo "failed."
exit 1
fi
}
case "$1" in
start)
iscsi_scstd_start
;;
stop)
iscsi_scstd_stop
;;
restart)
iscsi_scstd_stop
sleep 1
iscsi_scstd_start
;;
status)
PID=`pidof iscsi-scstd`
if [ $PID ]; then
echo "iSCSI enterprise target is running at pid $PID"
else
echo "no iSCSI enterprise target found!"
exit 1
fi
;;
dump)
DUMP=`tempfile -p iscsi-scstd`
RETVAL=$?
if [ $RETVAL != "0" ]; then
echo "Failed to create dump file $DUMP"
exit 1
fi
iscsi-scst-adm --mode dump --all >$DUMP
RETVAL=$?
if [ $RETVAL != "0" ]; then
echo "Error dumping config from daemon"
rm $DUMP
exit 1
fi
mv -u $DUMP $CONFIG_FILE
echo "Config dumped to $CONFIG_FILE"
;;
*)
echo $"Usage: $0 {start|stop|restart|status|dump}"
exit 1
esac
exit 0

View File

@@ -0,0 +1,71 @@
#!/sbin/runscript
#
# Start the iSCSI Enterprise Target.
#
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
MEM_SIZE=1048576
depend()
{
use net
}
configure_memsize()
{
if [ -e /proc/sys/net/core/wmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
fi
if [ -e /proc/sys/net/core/rmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
fi
if [ -e /proc/sys/net/core/wmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
fi
if [ -e /proc/sys/net/core/rmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
fi
if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
fi
if [ -e /proc/sys/net/ipv4/tcp_rmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
fi
if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
fi
}
start_server()
{
# configure_memsize
modprobe -q crc32c
modprobe iscsi-scst
/usr/local/sbin/iscsi-scstd
}
stop_server()
{
killall iscsi-scstd
rmmod -w iscsi-scst
}
start()
{
ebegin "Starting iscsi"
start_server
eend 0
}
stop()
{
ebegin "Stopping iscsi"
stop_server
eend 0
}

View File

@@ -0,0 +1,130 @@
#!/bin/sh
#
# chkconfig: - 39 35
# description: Starts and stops the iSCSI target
#
# pidfile: /var/run/iscsi-scstd.pid
# config: /etc/iscsi-scstd.conf
# 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
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
MEM_SIZE=1048576
configure_memsize()
{
if [ -e /proc/sys/net/core/wmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
fi
if [ -e /proc/sys/net/core/rmem_max ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
fi
if [ -e /proc/sys/net/core/wmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
fi
if [ -e /proc/sys/net/core/rmem_default ]; then
echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
fi
if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
fi
if [ -e /proc/sys/net/ipv4/tcp_rmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
fi
if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
fi
}
RETVAL=0
start()
{
echo -n "Starting iSCSI target service: "
# configure_memsize
modprobe -q crc32c
modprobe iscsi-scst
daemon /usr/local/sbin/iscsi-scstd
# -d 0xFFFF
RETVAL=$?
echo
return $RETVAL
}
stop()
{
echo -n "Stopping iSCSI target service: "
# iscsi-scst-adm --op delete
killall iscsi-scstd
rmmod -w iscsi-scst
RETVAL=$?
modprobe -r crc32c 2>/dev/null
if [ $RETVAL == "0" ]; then
echo_success
else
echo_failure
fi
echo
return $RETVAL
}
restart()
{
stop
start
}
condrestart()
{
PID=`pidofproc iscsi-scstd`
if [ $PID ]; then
restart
fi
}
status()
{
PID=`pidofproc iscsi-scstd`
if [ ! $PID ]; then
echo "iSCSI target stopped"
exit 1
else
echo "iscsi-scstd (pid $PID) is running..."
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
condrestart)
condrestart
;;
status)
status
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
exit 1
esac
exit 0

View File

@@ -0,0 +1,7 @@
# Some examples
#
#iqn.2001-04.com.example:storage.disk1.sys1.xyz 192.168.22.2, 192.168.3.8
#iqn.2001-04.com.example:storage.disk1.sys2.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], [3ffe:505:2:1::]/64
#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.22.4

View File

@@ -0,0 +1,14 @@
# 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

View File

@@ -0,0 +1,55 @@
# Example iscsi target configuration
#
# Everything until the first target definition belongs
# to the global configuration.
# Right now this is only the user configuration used
# during discovery sessions. "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.<reversed domain name>[: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.2007-05.com.example:storage.disk2.sys1.xyz
# Users, who can access this target. The same rules as for discovery
# users apply here.
# Leave them alone if you don't want to use authentication.
#IncomingUser joe secret
#OutgoingUser jim 12charpasswd
# Alias name for this target
# Alias Test
# various iSCSI parameters
# (not all are used right now, see also iSCSI spec for details)
#MaxConnections 1
#InitialR2T No
#ImmediateData Yes
#MaxRecvDataSegmentLength 1048576
#MaxXmitDataSegmentLength 1048576
#MaxBurstLength 1048576
#FirstBurstLength 1048576
#DefaultTime2Wait 2
#DefaultTime2Retain 20
#MaxOutstandingR2T 20
#DataPDUInOrder Yes
#DataSequenceInOrder Yes
#ErrorRecoveryLevel 0
#HeaderDigest CRC32C,None
#DataDigest CRC32C,None
# various target parameters
#QueuedCommands 32

View File

@@ -0,0 +1,137 @@
#ifndef _ISCSI_U_H
#define _ISCSI_U_H
#ifndef __KERNEL__
#include <sys/uio.h>
#endif
#define ISCSI_VERSION_STRING "0.9.6/0.4.15r133"
/* The maximum length of 223 bytes in the RFC. */
#define ISCSI_NAME_LEN 256
#define ISCSI_LISTEN_PORT 3260
#define SCSI_ID_LEN 24
#ifndef aligned_u64
#define aligned_u64 unsigned long long __attribute__((aligned(8)))
#endif
struct target_info {
u32 tid;
char name[ISCSI_NAME_LEN];
};
struct session_info {
u32 tid;
aligned_u64 sid;
char initiator_name[ISCSI_NAME_LEN];
char user_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_queued_cmnds,
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 iscsi_event_state {
E_CONN_CLOSE,
};
struct iscsi_event {
u32 tid;
aligned_u64 sid;
u32 cid;
u32 state;
};
#define DEFAULT_NR_QUEUED_CMNDS 32
#define MIN_NR_QUEUED_CMNDS 1
#define MAX_NR_QUEUED_CMNDS 256
#define MAX_DATA_SEG_LEN (4096/sizeof(struct iovec)*4096)
#define NETLINK_ISCSI_SCST 25
#define ADD_TARGET _IOW('i', 0, struct target_info)
#define DEL_TARGET _IOW('i', 1, struct target_info)
#define ADD_SESSION _IOW('i', 2, struct session_info)
#define DEL_SESSION _IOW('i', 3, struct session_info)
#define GET_SESSION_INFO _IOWR('i', 4, struct session_info)
#define ADD_CONN _IOW('i', 5, struct conn_info)
#define DEL_CONN _IOW('i', 6, struct conn_info)
#define GET_CONN_INFO _IOWR('i', 7, struct conn_info)
#define ISCSI_PARAM_SET _IOW('i', 8, struct iscsi_param_info)
#define ISCSI_PARAM_GET _IOWR('i', 9, struct iscsi_param_info)
static inline int iscsi_is_key_declarative(int key)
{
switch(key)
{
case key_max_xmit_data_length:
return 1;
default:
return 0;
}
}
#endif

View File

@@ -0,0 +1,25 @@
#
# 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.
#SCST_INC_DIR := /usr/local/include/scst
SCST_INC_DIR := $(SUBDIRS)/../../scst/include
EXTRA_CFLAGS += -I$(src)/../include -I$(SCST_INC_DIR)
# -Wextra -Wno-unused-parameter
EXTRA_CFLAGS += -DEXTRACHECKS
#EXTRA_CFLAGS += -DTRACING
EXTRA_CFLAGS += -DDEBUG -g
#EXTRA_CFLAGS += -DDEBUG_DIGEST_FAILURES
obj-m += iscsi-scst.o
iscsi-scst-objs := iscsi.o nthread.o config.o digest.o \
conn.o session.o target.o event.o param.o

526
iscsi-scst/kernel/config.c Normal file
View File

@@ -0,0 +1,526 @@
/*
* (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#include <linux/proc_fs.h>
#include "iscsi.h"
#define ISCSI_PROC_VERSION_NAME "version"
#if defined(DEBUG) || defined(TRACING)
#define ISCSI_PROC_LOG_ENTRY_NAME "trace_level"
#include <linux/proc_fs.h>
static struct scst_proc_log iscsi_proc_local_trace_tbl[] =
{
{ TRACE_D_READ, "d_read" },
{ TRACE_D_WRITE, "d_write" },
{ TRACE_CONN_OC, "conn" },
{ TRACE_D_IOV, "iov" },
{ TRACE_D_DUMP_PDU, "pdu" },
{ 0, NULL }
};
static int iscsi_log_info_show(struct seq_file *seq, void *v)
{
int res = 0;
TRACE_ENTRY();
res = scst_proc_log_entry_read(seq, trace_flag,
iscsi_proc_local_trace_tbl);
TRACE_EXIT_RES(res);
return res;
}
static int iscsi_proc_log_entry_write(struct file *file, const char __user *buf,
size_t length, loff_t *off)
{
int res = 0;
TRACE_ENTRY();
res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
ISCSI_DEFAULT_LOG_FLAGS, iscsi_proc_local_trace_tbl);
TRACE_EXIT_RES(res);
return res;
}
#endif
static int iscsi_version_info_show(struct seq_file *seq, void *v)
{
TRACE_ENTRY();
seq_printf(seq, "%s\n", ISCSI_VERSION_STRING);
#ifdef EXTRACHECKS
seq_printf(seq, "EXTRACHECKS\n");
#endif
#ifdef TRACING
seq_printf(seq, "TRACING\n");
#endif
#ifdef DEBUG
seq_printf(seq, "DEBUG\n");
#endif
#ifdef DEBUG_DIGEST_FAILURES
seq_printf(seq, "DEBUG_DIGEST_FAILURES\n");
#endif
TRACE_EXIT();
return 0;
}
static struct scst_proc_data iscsi_version_proc_data = {
SCST_DEF_RW_SEQ_OP(NULL)
.show = iscsi_version_info_show,
};
#if defined(DEBUG) || defined(TRACING)
static struct scst_proc_data iscsi_log_proc_data = {
SCST_DEF_RW_SEQ_OP(iscsi_proc_log_entry_write)
.show = iscsi_log_info_show,
};
#endif
static __init int iscsi_proc_log_entry_build(struct scst_tgt_template *templ)
{
int res = 0;
struct proc_dir_entry *p, *root;
TRACE_ENTRY();
root = scst_proc_get_tgt_root(templ);
if (root) {
p = scst_create_proc_entry(root, ISCSI_PROC_VERSION_NAME,
&iscsi_version_proc_data);
if (p == NULL) {
PRINT_ERROR_PR("Not enough memory to register "
"target driver %s entry %s in /proc",
templ->name, ISCSI_PROC_VERSION_NAME);
res = -ENOMEM;
goto out;
}
#if defined(DEBUG) || defined(TRACING)
/* create the proc file entry for the device */
iscsi_log_proc_data.data = (void *)templ->name;
p = scst_create_proc_entry(root, ISCSI_PROC_LOG_ENTRY_NAME,
&iscsi_log_proc_data);
if (p == NULL) {
PRINT_ERROR_PR("Not enough memory to register "
"target driver %s entry %s in /proc",
templ->name, ISCSI_PROC_LOG_ENTRY_NAME);
res = -ENOMEM;
goto out_remove_ver;
}
#endif
}
out:
TRACE_EXIT_RES(res);
return res;
#if defined(DEBUG) || defined(TRACING)
out_remove_ver:
remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
goto out;
#endif
}
static void iscsi_proc_log_entry_clean(struct scst_tgt_template *templ)
{
struct proc_dir_entry *root;
TRACE_ENTRY();
root = scst_proc_get_tgt_root(templ);
if (root) {
#if defined(DEBUG) || defined(TRACING)
remove_proc_entry(ISCSI_PROC_LOG_ENTRY_NAME, root);
#endif
remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
}
TRACE_EXIT();
return;
}
struct proc_entries {
const char *name;
struct file_operations *fops;
};
static struct proc_entries iscsi_proc_entries[] =
{
{"session", &session_seq_fops},
};
static struct proc_dir_entry *proc_iscsi_dir;
void iscsi_procfs_exit(void)
{
int i;
if (!proc_iscsi_dir)
return;
for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++)
remove_proc_entry(iscsi_proc_entries[i].name, proc_iscsi_dir);
iscsi_proc_log_entry_clean(&iscsi_template);
}
int __init iscsi_procfs_init(void)
{
int i, err = 0;
struct proc_dir_entry *ent;
proc_iscsi_dir = scst_proc_get_tgt_root(&iscsi_template);
if (proc_iscsi_dir == NULL) {
err = -ESRCH;
goto out;
}
proc_iscsi_dir->owner = THIS_MODULE;
err = iscsi_proc_log_entry_build(&iscsi_template);
if (err < 0)
goto out;
for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++) {
ent = create_proc_entry(iscsi_proc_entries[i].name, 0, proc_iscsi_dir);
if (ent)
ent->proc_fops = iscsi_proc_entries[i].fops;
else {
err = -ENOMEM;
goto err;
}
}
out:
return err;
err:
if (proc_iscsi_dir)
iscsi_procfs_exit();
goto out;
}
/* target_mutex supposed to be locked */
static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
{
int err;
struct iscsi_session *session;
struct conn_info info;
struct iscsi_conn *conn;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
session = session_lookup(target, info.sid);
if (!session)
return -ENOENT;
conn = conn_lookup(session, info.cid);
info.cid = conn->cid;
info.stat_sn = conn->stat_sn;
info.exp_stat_sn = conn->exp_stat_sn;
if (copy_to_user((void *) ptr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
/* target_mutex supposed to be locked */
static int add_conn(struct iscsi_target *target, unsigned long ptr)
{
int err;
struct iscsi_session *session;
struct conn_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
if (!(session = session_lookup(target, info.sid)))
return -ENOENT;
return conn_add(session, &info);
}
/* target_mutex supposed to be locked */
static int del_conn(struct iscsi_target *target, unsigned long ptr)
{
int err;
struct iscsi_session *session;
struct conn_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
if (!(session = session_lookup(target, info.sid)))
return -ENOENT;
return conn_del(session, &info);
}
/* target_mutex supposed to be locked */
static int get_session_info(struct iscsi_target *target, unsigned long ptr)
{
int err;
struct iscsi_session *session;
struct session_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
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;
if (copy_to_user((void *) ptr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
/* target_mutex supposed to be locked */
static int add_session(struct iscsi_target *target, unsigned long ptr)
{
int err;
struct session_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
info.initiator_name[ISCSI_NAME_LEN-1] = '\0';
info.user_name[ISCSI_NAME_LEN-1] = '\0';
return session_add(target, &info);
}
/* target_mutex supposed to be locked */
static int del_session(struct iscsi_target *target, unsigned long ptr)
{
int err;
struct session_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
return session_del(target, info.sid);
}
/* target_mutex supposed to be locked */
static int iscsi_param_config(struct iscsi_target *target, unsigned long ptr, int set)
{
int err;
struct iscsi_param_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
goto out;
if ((err = iscsi_param_set(target, &info, set)) < 0)
goto out;
if (!set)
err = copy_to_user((void *) ptr, &info, sizeof(info));
out:
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int add_target(unsigned long ptr)
{
int err;
struct target_info info;
if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
return err;
if (!(err = target_add(&info)))
err = copy_to_user((void *) ptr, &info, sizeof(info));
return err;
}
static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct iscsi_target *target = NULL;
long err;
u32 id;
if ((err = get_user(id, (u32 *) arg)) != 0)
goto out;
if ((err = mutex_lock_interruptible(&target_mgmt_mutex)) < 0)
goto out;
if (cmd == DEL_TARGET) {
err = target_del(id);
goto out_unlock;
}
target = target_lookup_by_id(id);
if (cmd == ADD_TARGET)
if (target) {
err = -EEXIST;
PRINT_ERROR_PR("Target %u already exist!", id);
goto out_unlock;
}
switch (cmd) {
case ADD_TARGET:
err = add_target(arg);
goto out_unlock;
}
if (!target) {
PRINT_ERROR_PR("can't find the target %u", id);
err = -EINVAL;
goto out_unlock;
}
mutex_lock(&target->target_mutex);
switch (cmd) {
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:
PRINT_ERROR_PR("invalid ioctl cmd %x", cmd);
err = -EINVAL;
}
mutex_unlock(&target->target_mutex);
out_unlock:
mutex_unlock(&target_mgmt_mutex);
out:
return err;
}
static int release(struct inode *inode, struct file *filp)
{
target_del_all();
return 0;
}
struct file_operations ctr_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ioctl,
.compat_ioctl = ioctl,
.release = release,
};
#ifdef DEBUG
void iscsi_dump_iov(struct msghdr *msg)
{
if (trace_flag & TRACE_D_IOV) {
int i;
printk("%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("%d: %p,%d\n", i, msg->msg_iov[i].iov_base,
msg->msg_iov[i].iov_len);
}
}
}
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(" |");
}
void iscsi_dump_pdu(struct iscsi_pdu *pdu)
{
if (trace_flag & TRACE_D_DUMP_PDU) {
unsigned char *buf;
int i;
buf = (void *)&pdu->bhs;
printk("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("AHS: (%p,%d)\n", buf, pdu->ahssize);
for (i = 0; i < pdu->ahssize; i++)
iscsi_dump_char(*buf++);
iscsi_dump_char(-1);
printk("Data: (%d)\n", pdu->datasize);
}
}
#endif /* DEBUG */

384
iscsi-scst/kernel/conn.c Normal file
View File

@@ -0,0 +1,384 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <linux/file.h>
#include <linux/ip.h>
#include <net/tcp.h>
#include "iscsi.h"
#include "digest.h"
static void print_conn_state(char *p, size_t size, struct iscsi_conn *conn)
{
int printed = 0;
if (conn->closing) {
snprintf(p, size, "%s", "closing");
return;
}
switch(conn->rd_state) {
case ISCSI_CONN_RD_STATE_PROCESSING:
snprintf(p, size, "%s", "read_processing ");
printed = 1;
break;
case ISCSI_CONN_RD_STATE_IN_LIST:
snprintf(p, size, "%s", "in_read_list ");
printed = 1;
break;
}
switch(conn->wr_state) {
case ISCSI_CONN_WR_STATE_PROCESSING:
snprintf(p, size, "%s", "write_processing ");
printed = 1;
break;
case ISCSI_CONN_WR_STATE_IN_LIST:
snprintf(p, size, "%s", "in_write_list ");
printed = 1;
break;
case ISCSI_CONN_WR_STATE_SPACE_WAIT:
snprintf(p, size, "%s", "space_waiting ");
printed = 1;
break;
}
if (!printed)
snprintf(p, size, "%s", "established idle ");
}
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");
}
/* target_mutex supposed to be locked */
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, conn_list_entry) {
sk = conn->sock->sk;
switch (sk->sk_family) {
case AF_INET:
snprintf(buf, sizeof(buf),
"%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr));
break;
case AF_INET6:
snprintf(buf, sizeof(buf),
"[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
NIP6(inet6_sk(sk)->daddr));
break;
default:
break;
}
seq_printf(seq, "\t\tcid:%u ip:%s ", conn->cid, buf);
print_conn_state(buf, sizeof(buf), conn);
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);
}
}
/* target_mutex supposed to be locked */
struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid)
{
struct iscsi_conn *conn;
list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
if (conn->cid == cid)
return conn;
}
return NULL;
}
static void iscsi_make_conn_rd_active(struct iscsi_conn *conn)
{
TRACE_ENTRY();
spin_lock_bh(&iscsi_rd_lock);
TRACE_DBG("conn %p, rd_state %x, rd_data_ready %d", conn,
conn->rd_state, conn->rd_data_ready);
conn->rd_data_ready = 1;
if (conn->rd_state == ISCSI_CONN_RD_STATE_IDLE) {
list_add_tail(&conn->rd_list_entry, &iscsi_rd_list);
conn->rd_state = ISCSI_CONN_RD_STATE_IN_LIST;
wake_up(&iscsi_rd_waitQ);
}
spin_unlock_bh(&iscsi_rd_lock);
TRACE_EXIT();
return;
}
void iscsi_make_conn_wr_active(struct iscsi_conn *conn)
{
TRACE_ENTRY();
spin_lock_bh(&iscsi_wr_lock);
TRACE_DBG("conn %p, wr_state %x, wr_space_ready %d", conn,
conn->wr_state, conn->wr_space_ready);
if (conn->wr_state == ISCSI_CONN_WR_STATE_IDLE) {
list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
wake_up(&iscsi_wr_waitQ);
}
spin_unlock_bh(&iscsi_wr_lock);
TRACE_EXIT();
return;
}
void mark_conn_closed(struct iscsi_conn *conn)
{
spin_lock_bh(&iscsi_rd_lock);
conn->closing = 1;
spin_unlock_bh(&iscsi_rd_lock);
iscsi_make_conn_rd_active(conn);
}
static void iscsi_state_change(struct sock *sk)
{
struct iscsi_conn *conn = sk->sk_user_data;
TRACE_ENTRY();
if (sk->sk_state != TCP_ESTABLISHED) {
if (!conn->closing) {
PRINT_ERROR_PR("Connection with initiator %s (%p) "
"unexpectedly closed!",
conn->session->initiator_name, conn);
mark_conn_closed(conn);
}
} else
iscsi_make_conn_rd_active(conn);
conn->old_state_change(sk);
TRACE_EXIT();
return;
}
static void iscsi_data_ready(struct sock *sk, int len)
{
struct iscsi_conn *conn = sk->sk_user_data;
TRACE_ENTRY();
iscsi_make_conn_rd_active(conn);
conn->old_data_ready(sk, len);
TRACE_EXIT();
return;
}
static void iscsi_write_space_ready(struct sock *sk)
{
struct iscsi_conn *conn = sk->sk_user_data;
TRACE_ENTRY();
TRACE_DBG("Write space ready for conn %p", conn);
spin_lock_bh(&iscsi_wr_lock);
conn->wr_space_ready = 1;
if ((conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT)) {
list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
wake_up(&iscsi_wr_waitQ);
}
spin_unlock_bh(&iscsi_wr_lock);
conn->old_write_space(sk);
TRACE_EXIT();
return;
}
static int iscsi_socket_bind(struct iscsi_conn *conn)
{
int res = 0;
int opt = 1;
mm_segment_t oldfs;
struct iscsi_session *session = conn->session;
TRACE_DBG("%llu", (unsigned long long) session->sid);
conn->sock = SOCKET_I(conn->file->f_dentry->d_inode);
if (conn->sock->ops->sendpage == NULL) {
PRINT_ERROR_PR("Socket for sid %llu doesn't support sendpage()",
session->sid);
res = -EINVAL;
goto out;
}
#if 0
conn->sock->sk->sk_allocation = GFP_NOIO;
#endif
conn->sock->sk->sk_user_data = conn;
write_lock_bh(&conn->sock->sk->sk_callback_lock);
conn->old_state_change = conn->sock->sk->sk_state_change;
conn->sock->sk->sk_state_change = iscsi_state_change;
conn->old_data_ready = conn->sock->sk->sk_data_ready;
conn->sock->sk->sk_data_ready = iscsi_data_ready;
conn->old_write_space = conn->sock->sk->sk_write_space;
conn->sock->sk->sk_write_space = iscsi_write_space_ready;
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);
out:
return res;
}
/* target_mutex supposed to be locked */
int conn_free(struct iscsi_conn *conn)
{
TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn,
conn->session, (unsigned long long)conn->session->sid,
conn->cid);
sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0);
sBUG_ON(!list_empty(&conn->cmd_list));
sBUG_ON(!list_empty(&conn->write_list));
list_del(&conn->conn_list_entry);
fput(conn->file);
conn->file = NULL;
conn->sock = NULL;
free_page((unsigned long)conn->read_iov);
kfree(conn);
return 0;
}
/* target_mutex supposed to be locked */
static int iscsi_conn_alloc(struct iscsi_session *session, struct conn_info *info)
{
struct iscsi_conn *conn;
int res = 0;
TRACE_MGMT_DBG("Creating connection for sid %#Lx, cid %u",
(unsigned long long)session->sid, info->cid);
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn) {
res = -ENOMEM;
goto out_err;
}
/* Changing it, change ISCSI_CONN_IOV_MAX as well !! */
conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL);
if (conn->read_iov == NULL) {
res = -ENOMEM;
goto out_err_free_conn;
}
atomic_set(&conn->conn_ref_cnt, 0);
conn->session = session;
conn->cid = info->cid;
conn->stat_sn = info->stat_sn;
conn->exp_stat_sn = info->exp_stat_sn;
conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
conn->hdigest_type = info->header_digest;
conn->ddigest_type = info->data_digest;
res = digest_init(conn);
if (res != 0)
goto out_err_free1;
conn->target = session->target;
spin_lock_init(&conn->cmd_list_lock);
INIT_LIST_HEAD(&conn->cmd_list);
spin_lock_init(&conn->write_list_lock);
INIT_LIST_HEAD(&conn->write_list);
conn->file = fget(info->fd);
res = iscsi_socket_bind(conn);
if (res != 0)
goto out_err_free2;
list_add(&conn->conn_list_entry, &session->conn_list);
out:
return res;
out_err_free2:
fput(conn->file);
out_err_free1:
free_page((unsigned long)conn->read_iov);
out_err_free_conn:
kfree(conn);
out_err:
goto out;
}
/* target_mutex supposed to be locked */
int conn_add(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;
return iscsi_conn_alloc(session, info);
}
/* target_mutex supposed to be locked */
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;
PRINT_INFO_PR("Deleting connection with initiator %s (%p)",
conn->session->initiator_name, conn);
mark_conn_closed(conn);
return 0;
}

212
iscsi-scst/kernel/digest.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* iSCSI digest handling.
* (C) 2004 - 2006 Xiranet Communications GmbH <arne.redlich@xiranet.com>
* This code is licensed under the GPL.
*/
#include <asm/types.h>
#include <asm/scatterlist.h>
#include "iscsi.h"
#include "digest.h"
#include <linux/crc32c.h>
void digest_alg_available(unsigned int *val)
{
#if CONFIG_LIBCRC32C_MODULE || CONFIG_LIBCRC32C
int crc32c = 1;
#else
int crc32c = 0;
#endif
if ((*val & DIGEST_CRC32C) && !crc32c) {
PRINT_ERROR_PR("%s", "CRC32C digest algorithm not available "
"in kernel");
*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)
{
if (!(conn->hdigest_type & DIGEST_ALL))
conn->hdigest_type = DIGEST_NONE;
if (!(conn->ddigest_type & DIGEST_ALL))
conn->ddigest_type = DIGEST_NONE;
return 0;
}
/* 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_MASK); \
(sg).length = (l); \
} while (0)
static u32 evaluate_crc32_from_sg(struct scatterlist *sg, int total,
int pad_bytes)
{
u32 crc = ~0;
#ifdef DEBUG_DIGEST_FAILURES
if (((scst_random() % 100000) == 752)) {
PRINT_INFO_PR("%s", "Simulating digest failure");
return 0;
}
#endif
#if CONFIG_LIBCRC32C_MODULE || CONFIG_LIBCRC32C
while (total > 0) {
int d = min(min(total, (int)(sg->length)),
(int)(PAGE_SIZE - sg->offset));
crc = crc32c(crc, page_address(sg->page) + sg->offset, d);
total -= d;
sg++;
}
if (pad_bytes) {
u32 padding = 0;
/*
* Digest includes also padding for aligned pdu length, hopefully
* it is always filled with 0s in pdu (according to crypto/crc32c.c
*/
crc = crc32c(crc, (u8 *)&padding, pad_bytes);
}
#endif
return ~cpu_to_le32(crc);
}
static u32 digest_header(struct iscsi_pdu *pdu)
{
struct scatterlist sg[2];
unsigned int nbytes = sizeof(struct iscsi_hdr);
SETSG(sg[0], &pdu->bhs, nbytes);
if (pdu->ahssize) {
SETSG(sg[1], pdu->ahs, pdu->ahssize);
nbytes += pdu->ahssize;
}
return evaluate_crc32_from_sg(sg, nbytes, 0);
}
static u32 digest_data(struct iscsi_cmnd *req, u32 osize, u32 offset)
{
struct scatterlist *sg = req->sg;
int idx, count;
struct scatterlist saved_sg;
u32 size = (osize + 3) & ~3;
u32 crc;
offset += sg[0].offset;
idx = offset >> PAGE_SHIFT;
offset &= ~PAGE_MASK;
count = get_pgcnt(size, offset);
sBUG_ON(idx + count > get_pgcnt(req->bufflen, 0));
sBUG_ON(count > ISCSI_CONN_IOV_MAX);
saved_sg = sg[idx];
sg[idx].offset = offset;
sg[idx].length -= offset - saved_sg.offset;
crc = evaluate_crc32_from_sg(sg + idx, osize, size - osize);
sg[idx] = saved_sg;
return crc;
}
int digest_rx_header(struct iscsi_cmnd *cmnd)
{
u32 crc;
crc = digest_header(&cmnd->pdu);
if (crc != cmnd->hdigest) {
PRINT_ERROR_PR("%s", "RX header digest failed");
return -EIO;
} else
TRACE_DBG("RX header digest OK for cmd %p", cmnd);
return 0;
}
void digest_tx_header(struct iscsi_cmnd *cmnd)
{
cmnd->hdigest = digest_header(&cmnd->pdu);
TRACE_DBG("TX header digest for cmd %p: %x", cmnd, cmnd->hdigest);
}
int digest_rx_data(struct iscsi_cmnd *cmnd)
{
struct iscsi_cmnd *req;
struct iscsi_data_out_hdr *req_hdr;
u32 offset, crc;
int res = 0;
switch (cmnd_opcode(cmnd)) {
case ISCSI_OP_SCSI_DATA_OUT:
req = cmnd->cmd_req;
req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
offset = be32_to_cpu(req_hdr->buffer_offset);
break;
case ISCSI_OP_SCSI_REJECT:
case ISCSI_OP_PDU_REJECT:
case ISCSI_OP_DATA_REJECT:
goto out;
default:
req = cmnd;
offset = 0;
}
crc = digest_data(req, cmnd->pdu.datasize, offset);
if (crc != cmnd->ddigest) {
PRINT_ERROR_PR("%s", "RX data digest failed");
res = -EIO;
} else
TRACE_DBG("RX data digest OK for cmd %p", cmnd);
out:
return res;
}
void digest_tx_data(struct iscsi_cmnd *cmnd)
{
struct iscsi_data_in_hdr *hdr;
u32 offset;
TRACE_DBG("%s:%d req %p, own_sg %d, sg %p, sgcnt %d cmnd %p, "
"own_sg %d, sg %p, sgcnt %d", __FUNCTION__, __LINE__,
cmnd->parent_req, cmnd->parent_req->own_sg,
cmnd->parent_req->sg, cmnd->parent_req->sg_cnt,
cmnd, cmnd->own_sg, cmnd->sg, cmnd->sg_cnt);
switch (cmnd_opcode(cmnd)) {
case ISCSI_OP_SCSI_DATA_IN:
hdr = (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs;
offset = be32_to_cpu(hdr->buffer_offset);
break;
default:
offset = 0;
}
/*
* cmnd is used here regardless of its sg comes from parent or was
* allocated for this cmnd only, see cmnd_send_pdu()
*/
cmnd->ddigest = digest_data(cmnd, cmnd->pdu.datasize, offset);
TRACE_DBG("TX data digest for cmd %p: %x (offset %d, opcode %x)", cmnd,
cmnd->ddigest, offset, cmnd_opcode(cmnd));
}

View File

@@ -0,0 +1,19 @@
/*
* iSCSI digest handling.
* (C) 2004 Xiranet Communications GmbH <arne.redlich@xiranet.com>
* This code is licensed under the GPL.
*/
#ifndef __ISCSI_DIGEST_H__
#define __ISCSI_DIGEST_H__
extern void digest_alg_available(unsigned int *val);
extern int digest_init(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 /* __ISCSI_DIGEST_H__ */

115
iscsi-scst/kernel/event.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* Event notification code.
* (C) 2005 FUJITA Tomonori <tomof@acm.org>
* This code is licenced under the GPL.
*
* Some functions are based on audit code.
*/
#include <net/tcp.h>
#include "iscsi_u.h"
#include "iscsi.h"
static struct sock *nl;
static u32 iscsid_pid;
static int event_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
u32 uid, pid, seq;
char *data;
pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid;
seq = nlh->nlmsg_seq;
data = NLMSG_DATA(nlh);
iscsid_pid = pid;
return 0;
}
static int event_recv_skb(struct sk_buff *skb)
{
int err;
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)
return 0;
rlen = NLMSG_ALIGN(nlh->nlmsg_len);
if (rlen > skb->len)
rlen = skb->len;
if ((err = event_recv_msg(skb, nlh))) {
netlink_ack(skb, nlh, -err);
} else if (nlh->nlmsg_flags & NLM_F_ACK)
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)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
static u32 seq = 0;
if (!(skb = alloc_skb(NLMSG_SPACE(len), gfp_mask)))
return -ENOMEM;
nlh = __nlmsg_put(skb, iscsid_pid, seq++, NLMSG_DONE, len - sizeof(*nlh), 0);
memcpy(NLMSG_DATA(nlh), data, len);
return netlink_unicast(nl, skb, iscsid_pid, 0);
}
int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic)
{
int err;
struct iscsi_event event;
event.tid = tid;
event.sid = sid;
event.cid = cid;
event.state = state;
err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_event)), 0);
return err;
}
int __init event_init(void)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv, THIS_MODULE);
#else
nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv, NULL,
THIS_MODULE);
#endif
if (!nl) {
PRINT_ERROR_PR("%s", "netlink_kernel_create() failed");
return -ENOMEM;
} else
return 0;
}
void event_exit(void)
{
if (nl)
sock_release(nl->sk_socket);
}

2425
iscsi-scst/kernel/iscsi.c Normal file

File diff suppressed because it is too large Load Diff

464
iscsi-scst/kernel/iscsi.h Normal file
View File

@@ -0,0 +1,464 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#ifndef __ISCSI_H__
#define __ISCSI_H__
#include <linux/pagemap.h>
#include <linux/seq_file.h>
#include <linux/mm.h>
#include <linux/net.h>
#include <net/sock.h>
#include <scsi_tgt.h>
#include "iscsi_hdr.h"
#include "iscsi_u.h"
#include "iscsi_dbg.h"
#define iscsi_sense_crc_error ABORTED_COMMAND, 0x47, 0x5
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 queued_cmnds;
};
struct network_thread_info {
struct task_struct *task;
unsigned int ready;
};
struct iscsi_cmnd;
struct iscsi_target {
struct scst_tgt *scst_tgt;
struct mutex target_mutex;
struct list_head session_list; /* protected by target_mutex */
/* Both protected by target_mutex */
struct iscsi_sess_param trgt_sess_param;
struct iscsi_trgt_param trgt_param;
struct list_head target_list_entry;
u32 tid;
char name[ISCSI_NAME_LEN];
};
#define ISCSI_HASH_ORDER 8
#define cmnd_hashfn(itt) hash_long((itt), ISCSI_HASH_ORDER)
struct iscsi_session {
struct iscsi_target *target;
struct scst_session *scst_sess;
/* All 2 unprotected, since accessed only from a single read thread */
struct list_head pending_list;
u32 next_ttt;
/* Both unprotected, since read-only */
u32 max_queued_cmnds;
u32 max_cmd_sn; /* Not used ??, ToDo */
spinlock_t sn_lock;
u32 exp_cmd_sn; /* protected by sn_lock */
/* read only, if there are connection(s) */
struct iscsi_sess_param sess_param;
spinlock_t cmnd_hash_lock;
struct list_head cmnd_hash[1 << ISCSI_HASH_ORDER];
struct list_head conn_list; /* protected by target_mutex */
struct list_head session_list_entry;
/* Bot don't need any protection */
char *initiator_name;
u64 sid;
};
#define ISCSI_CONN_IOV_MAX (PAGE_SIZE/sizeof(struct iovec))
#define ISCSI_CONN_RD_STATE_IDLE 0
#define ISCSI_CONN_RD_STATE_IN_LIST 1
#define ISCSI_CONN_RD_STATE_PROCESSING 2
#define ISCSI_CONN_WR_STATE_IDLE 0
#define ISCSI_CONN_WR_STATE_IN_LIST 1
#define ISCSI_CONN_WR_STATE_SPACE_WAIT 2
#define ISCSI_CONN_WR_STATE_PROCESSING 3
struct iscsi_conn {
struct iscsi_session *session; /* owning session */
/* Both protected by session->sn_lock */
u32 stat_sn;
u32 exp_stat_sn;
spinlock_t cmd_list_lock; /* BH lock */
/*
* IMPORTANT! If you find a cmd in cmd_list and immediately get_cmnd()
* it, it still can be destroyed immediately after you drop
* cmd_list_lock no matter how big is its ref_cnt!
*/
/* Protected by cmd_list_lock */
struct list_head cmd_list; /* in/outcoming pdus */
atomic_t conn_ref_cnt;
spinlock_t write_list_lock;
/* List of data pdus to be sent, protected by write_list_lock */
struct list_head write_list;
/* All 2 protected by iscsi_wr_lock */
unsigned short wr_state;
unsigned short wr_space_ready:1;
struct list_head wr_list_entry;
#ifdef EXTRACHECKS
struct task_struct *wr_task;
#endif
/* All are unprotected, since accessed only from a single write thread */
struct iscsi_cmnd *write_cmnd;
struct iovec *write_iop;
int write_iop_used;
struct iovec write_iov[2];
u32 write_size;
u32 write_offset;
int write_state;
/* Both don't need any protection */
struct file *file;
struct socket *sock;
void (*old_state_change)(struct sock *);
void (*old_data_ready)(struct sock *, int);
void (*old_write_space)(struct sock *);
/* Both read only */
int hdigest_type;
int ddigest_type;
/* All 4 protected by iscsi_rd_lock */
unsigned short rd_state;
unsigned short rd_data_ready:1;
unsigned short closing:1; /* Let's save some cache footprint by putting it here */
struct list_head rd_list_entry;
#ifdef EXTRACHECKS
struct task_struct *rd_task;
#endif
/* All are unprotected, since accessed only from a single read thread */
struct iscsi_cmnd *read_cmnd;
struct msghdr read_msg;
u32 read_size;
int read_state;
struct iovec *read_iov;
struct iscsi_target *target;
struct list_head conn_list_entry; /* list entry in session conn_list */
/* Doesn't need any protection */
u16 cid;
};
struct iscsi_pdu {
struct iscsi_hdr bhs;
void *ahs;
unsigned int ahssize;
unsigned int datasize;
};
typedef void (iscsi_show_info_t)(struct seq_file *seq, struct iscsi_target *target);
/* Command's states */
#define ISCSI_CMD_STATE_NEW 0 /* New command and SCST processes it */
#define ISCSI_CMD_STATE_RX_CMD 1 /* SCST processes cmd after scst_rx_cmd() */
#define ISCSI_CMD_STATE_AFTER_PREPROC 2 /* The command returned from preprocessing_done() */
#define ISCSI_CMD_STATE_RESTARTED 3 /* scst_restart_cmd() called and SCST processing it */
#define ISCSI_CMD_STATE_PROCESSED 4 /* SCST done processing */
/*
* Most of the fields don't need any protection, since accessed from only a
* single thread, except where noted.
*/
struct iscsi_cmnd {
struct iscsi_conn *conn;
/* Some flags protected by conn->write_list_lock */
unsigned int hashed:1;
unsigned int should_close_conn:1;
unsigned int pending:1;
unsigned int own_sg:1;
unsigned int on_write_list:1;
unsigned int write_processing_started:1;
unsigned int data_waiting:1;
unsigned int force_cleanup_done:1;
#ifdef EXTRACHECKS
unsigned int release_called:1;
#endif
unsigned long tmfabort; /* it's async. with the above flags */
struct list_head hash_list_entry;
spinlock_t rsp_cmd_lock; /* BH lock */
/* Unions are for readability and grepability */
/*
* IMPORTANT! If you find a cmd in rsp_cmd_list and immediately
* get_cmnd() it, it still can be destroyed immediately after you drop
* rsp_cmd_lock no matter how big is its ref_cnt!
*/
union {
/* Protected by rsp_cmd_lock */
struct list_head rsp_cmd_list;
struct list_head rsp_cmd_list_entry;
};
union {
struct list_head pending_list_entry;
struct list_head write_list_entry;
};
/*
* Unprotected, since could be accessed from only a single
* thread at time
*/
struct list_head rx_ddigest_cmd_list;
struct list_head rx_ddigest_cmd_list_entry;
struct iscsi_cmnd *parent_req;
struct iscsi_cmnd *cmd_req;
struct iscsi_target *target;
wait_queue_head_t scst_waitQ;
int scst_state;
struct scst_cmd *scst_cmd;
atomic_t ref_cnt;
#ifdef NET_PAGE_CALLBACKS_DEFINED
atomic_t net_ref_cnt;
#endif
struct iscsi_pdu pdu;
struct scatterlist *sg;
int bufflen;
u32 r2t_sn;
u32 r2t_length;
u32 is_unsolicited_data;
u32 target_task_tag;
u32 outstanding_r2t;
u32 hdigest;
u32 ddigest;
int sg_cnt; /* valid only if own_sg is 1 */
struct list_head cmd_list_entry;
};
#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
/* Flags for req_cmnd_release_force() */
#define ISCSI_FORCE_RELEASE_WRITE 1
extern struct mutex target_mgmt_mutex;
extern struct file_operations ctr_fops;
extern spinlock_t iscsi_rd_lock;
extern struct list_head iscsi_rd_list;
extern wait_queue_head_t iscsi_rd_waitQ;
extern spinlock_t iscsi_wr_lock;
extern struct list_head iscsi_wr_list;
extern wait_queue_head_t iscsi_wr_waitQ;
/* iscsi.c */
extern struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *,
struct iscsi_cmnd *parent);
extern int 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 req_cmnd_release(struct iscsi_cmnd *req);
extern void req_cmnd_release_force(struct iscsi_cmnd *req, int flags);
extern void rsp_cmnd_release(struct iscsi_cmnd *);
extern void cmnd_done(struct iscsi_cmnd *cmnd);
extern void conn_abort(struct iscsi_conn *conn);
/* 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 mark_conn_closed(struct iscsi_conn *);
extern void iscsi_make_conn_wr_active(struct iscsi_conn *);
extern void conn_info_show(struct seq_file *, struct iscsi_session *);
/* nthread.c */
extern int iscsi_send(struct iscsi_conn *conn);
#ifdef NET_PAGE_CALLBACKS_DEFINED
extern void iscsi_get_page_callback(struct page *page);
extern void iscsi_put_page_callback(struct page *page);
#endif
extern int istrd(void *arg);
extern int istwr(void *arg);
/* target.c */
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);
/* config.c */
extern int iscsi_procfs_init(void);
extern void iscsi_procfs_exit(void);
extern int iscsi_info_show(struct seq_file *, iscsi_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 int session_free(struct iscsi_session *session);
/* params.c */
extern int iscsi_param_set(struct iscsi_target *, struct iscsi_param_info *, int);
/* event.c */
extern int event_send(u32, u64, u32, u32, int);
extern int event_init(void);
extern void event_exit(void);
#define get_pgcnt(size, offset) ((((size) + ((offset) & ~PAGE_MASK)) + PAGE_SIZE - 1) >> PAGE_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]
extern struct scst_tgt_template iscsi_template;
static inline void cmnd_get(struct iscsi_cmnd *cmnd)
{
atomic_inc(&cmnd->ref_cnt);
TRACE_DBG("cmnd %p, new cmnd->ref_cnt %d", cmnd,
atomic_read(&cmnd->ref_cnt));
}
static inline void cmnd_get_ordered(struct iscsi_cmnd *cmnd)
{
cmnd_get(cmnd);
smp_mb__after_atomic_inc();
}
static inline void cmnd_put(struct iscsi_cmnd *cmnd)
{
TRACE_DBG("cmnd %p, new cmnd->ref_cnt %d", cmnd,
atomic_read(&cmnd->ref_cnt)-1);
sBUG_ON(atomic_read(&cmnd->ref_cnt) == 0);
if (atomic_dec_and_test(&cmnd->ref_cnt))
cmnd_done(cmnd);
}
/* conn->write_list_lock supposed to be locked */
static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
struct iscsi_cmnd *cmnd)
{
TRACE_DBG("%p", cmnd);
list_add_tail(&cmnd->write_list_entry, &conn->write_list);
cmnd->on_write_list = 1;
}
/* conn->write_list_lock supposed to be locked */
static inline void cmd_del_from_write_list(struct iscsi_cmnd *cmnd)
{
TRACE_DBG("%p", cmnd);
list_del(&cmnd->write_list_entry);
cmnd->on_write_list = 0;
}
static inline int test_write_ready(struct iscsi_conn *conn)
{
/*
* No need for write_list protection, in the worst case we will be
* restarted again.
*/
return (!list_empty(&conn->write_list) || conn->write_cmnd);
}
#ifdef EXTRACHECKS
#define iscsi_extracheck_is_rd_thread(conn) sBUG_ON(current != (conn)->rd_task)
#define iscsi_extracheck_is_wr_thread(conn) sBUG_ON(current != (conn)->wr_task)
#else
static inline void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn) {}
static inline void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn) {}
#endif
#endif /* __ISCSI_H__ */

View File

@@ -0,0 +1,59 @@
#ifndef ISCSI_DBG_H
#define ISCSI_DBG_H
#include <scst_debug.h>
#define TRACE_D_READ 0x80000000
#define TRACE_D_WRITE 0x40000000
#define TRACE_CONN_OC 0x20000000
#define TRACE_D_IOV 0x10000000
#define TRACE_D_DUMP_PDU 0x08000000
#define TRACE_D_DATA (TRACE_D_READ | TRACE_D_WRITE)
#define TRACE_ALL_NO_DATA (TRACE_ALL & ~TRACE_D_IOV & ~TRACE_D_DUMP_PDU & ~TRACE_D_DATA)
#define LOG_PREFIX "iscsi-scst"
#ifdef DEBUG
#define ISCSI_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_LINE | TRACE_PID | \
TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_DEBUG | \
TRACE_MINOR | TRACE_SPECIAL | TRACE_CONN_OC)
#else
#define ISCSI_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \
TRACE_OUT_OF_MEM | TRACE_MGMT | \
TRACE_MINOR | TRACE_SPECIAL)
#endif
#ifdef DEBUG
struct msghdr;
struct iscsi_pdu;
extern void iscsi_dump_iov(struct msghdr *msg);
extern void iscsi_dump_pdu(struct iscsi_pdu *pdu);
#else
#define iscsi_dump_iov(x) do {} while (0)
#define iscsi_dump_pdu(x) do {} while (0)
#endif
#if defined(DEBUG) || defined(TRACING)
extern unsigned long iscsi_trace_flag;
#define trace_flag iscsi_trace_flag
#define TRACE_CONN_CLOSE(format, args...) \
do { \
if (trace_flag & TRACE_CONN_OC) \
{ \
char *__tflag = LOG_FLAG; \
if (debug_print_prefix(trace_flag, __FUNCTION__, __LINE__) > 0) \
{ \
__tflag = NO_FLAG; \
} \
PRINT(NO_FLAG, "%s" format, __tflag, args); \
} \
} while(0)
#else /* defined(DEBUG) || defined(TRACING) */
#define TRACE_CONN_CLOSE(format, args...) {}
#endif
#endif

View File

@@ -0,0 +1,509 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#ifndef __ISCSI_HDR_H__
#define __ISCSI_HDR_H__
#include <linux/types.h>
#include <asm/byteorder.h>
#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_NOOP_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_NOOP_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;
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__ */

999
iscsi-scst/kernel/nthread.c Normal file
View File

@@ -0,0 +1,999 @@
/*
* Network thread.
* (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
* This code is licenced under the GPL.
*/
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/kthread.h>
#include <asm/ioctls.h>
#include <linux/delay.h>
#include <net/tcp.h>
#include "iscsi.h"
#include "digest.h"
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,
};
enum tx_state {
TX_INIT, /* Must be zero. */
TX_BHS_DATA,
TX_INIT_DDIGEST,
TX_DDIGEST,
TX_END,
};
/* No locks */
static void close_conn(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
struct iscsi_target *target = conn->target;
TRACE_ENTRY();
TRACE_CONN_CLOSE("conn %p, conn_ref_cnt=%d", conn,
atomic_read(&conn->conn_ref_cnt));
iscsi_extracheck_is_rd_thread(conn);
/* We want all our already send operations to complete */
conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
conn_abort(conn);
if (conn->read_state != RX_INIT_BHS) {
req_cmnd_release_force(conn->read_cmnd, 0);
conn->read_cmnd = NULL;
conn->read_state = RX_INIT_BHS;
}
/* ToDo: not the best way to wait */
while(atomic_read(&conn->conn_ref_cnt) != 0) {
struct iscsi_cmnd *cmnd;
if (!list_empty(&session->pending_list)) {
struct list_head *pending_list = &session->pending_list;
struct iscsi_cmnd *tmp;
TRACE_CONN_CLOSE("Disposing pending commands on conn "
"%p, conn_ref_cnt=%d", conn,
atomic_read(&conn->conn_ref_cnt));
list_for_each_entry_safe(cmnd, tmp, pending_list,
pending_list_entry) {
if (cmnd->conn == conn) {
TRACE_CONN_CLOSE("Freeing pending cmd %p",
cmnd);
list_del(&cmnd->pending_list_entry);
cmnd->pending = 0;
req_cmnd_release_force(cmnd, 0);
}
}
}
iscsi_make_conn_wr_active(conn);
msleep(50);
TRACE_CONN_CLOSE("conn %p, conn_ref_cnt %d left, wr_state %d",
conn, atomic_read(&conn->conn_ref_cnt), conn->wr_state);
#ifdef DEBUG
{
#ifdef NET_PAGE_CALLBACKS_DEFINED
struct iscsi_cmnd *rsp;
#endif
spin_lock_bh(&conn->cmd_list_lock);
list_for_each_entry(cmnd, &conn->cmd_list, cmd_list_entry) {
TRACE_DBG("cmd %p, scst_state %x, data_waiting "
"%d, ref_cnt %d, parent_req %p", cmnd,
cmnd->scst_state, cmnd->data_waiting,
atomic_read(&cmnd->ref_cnt), cmnd->parent_req);
#ifdef NET_PAGE_CALLBACKS_DEFINED
TRACE_DBG("net_ref_cnt %d, sg %p",
atomic_read(&cmnd->net_ref_cnt), cmnd->sg);
if (cmnd->sg != NULL) {
int sg_cnt, i;
sg_cnt = get_pgcnt(cmnd->bufflen,
cmnd->sg[0].offset);
for(i = 0; i < sg_cnt; i++) {
TRACE_DBG("page %p, net_priv %p, _count %d",
cmnd->sg[i].page, cmnd->sg[i].page->net_priv,
atomic_read(&cmnd->sg[i].page->_count));
}
}
sBUG_ON(cmnd->parent_req != NULL);
spin_lock_bh(&cmnd->rsp_cmd_lock);
list_for_each_entry(rsp, &cmnd->rsp_cmd_list, rsp_cmd_list_entry) {
TRACE_DBG(" rsp %p, ref_cnt %d, net_ref_cnt %d, "
"sg %p", rsp, atomic_read(&rsp->ref_cnt),
atomic_read(&rsp->net_ref_cnt), rsp->sg);
if ((rsp->sg != cmnd->sg) && (rsp->sg != NULL)) {
int sg_cnt, i;
sg_cnt = get_pgcnt(rsp->bufflen,
rsp->sg[0].offset);
sBUG_ON(rsp->sg_cnt != sg_cnt);
for(i = 0; i < sg_cnt; i++) {
TRACE_DBG(" page %p, net_priv %p, "
"_count %d", rsp->sg[i].page,
rsp->sg[i].page->net_priv,
atomic_read(&rsp->sg[i].page->_count));
}
}
}
spin_unlock_bh(&cmnd->rsp_cmd_lock);
#endif
}
spin_unlock_bh(&conn->cmd_list_lock);
}
#endif
}
write_lock_bh(&conn->sock->sk->sk_callback_lock);
conn->sock->sk->sk_state_change = conn->old_state_change;
conn->sock->sk->sk_data_ready = conn->old_data_ready;
conn->sock->sk->sk_write_space = conn->old_write_space;
write_unlock_bh(&conn->sock->sk->sk_callback_lock);
while(conn->wr_state != ISCSI_CONN_WR_STATE_IDLE) {
TRACE_CONN_CLOSE("Waiting for wr thread (conn %p), wr_state %x",
conn, conn->wr_state);
msleep(50);
}
mutex_lock(&target->target_mutex);
conn_free(conn);
if (list_empty(&session->conn_list))
session_del(target, session->sid);
mutex_unlock(&target->target_mutex);
TRACE_CONN_CLOSE("Notifying user space about closing conn %p", conn);
event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0);
TRACE_EXIT();
return;
}
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)
{
/* ToDo: __GFP_NOFAIL ?? */
cmnd->pdu.ahs = kmalloc(cmnd->pdu.ahssize, __GFP_NOFAIL|GFP_KERNEL);
sBUG_ON(cmnd->pdu.ahs == NULL);
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(&conn->write_list_lock);
if (!list_empty(&conn->write_list)) {
cmnd = list_entry(conn->write_list.next, struct iscsi_cmnd,
write_list_entry);
cmd_del_from_write_list(cmnd);
cmnd->write_processing_started = 1;
}
spin_unlock(&conn->write_list_lock);
return cmnd;
}
static int do_recv(struct iscsi_conn *conn, int state)
{
mm_segment_t oldfs;
struct msghdr msg;
int res, first_len;
if (unlikely(conn->closing)) {
res = -EIO;
goto out;
}
memset(&msg, 0, sizeof(msg));
msg.msg_iov = conn->read_msg.msg_iov;
msg.msg_iovlen = conn->read_msg.msg_iovlen;
first_len = msg.msg_iov->iov_len;
oldfs = get_fs();
set_fs(get_ds());
res = sock_recvmsg(conn->sock, &msg, conn->read_size, MSG_DONTWAIT | MSG_NOSIGNAL);
set_fs(oldfs);
if (res <= 0) {
switch (res) {
case -EAGAIN:
case -ERESTARTSYS:
TRACE_DBG("EAGAIN or ERESTARTSYS (%d) received for "
"conn %p", res, conn);
break;
default:
PRINT_ERROR_PR("sock_recvmsg() failed: %d", res);
mark_conn_closed(conn);
break;
}
} else {
/*
* To save some considerable effort and CPU power we suppose
* that TCP functions adjust conn->read_msg.msg_iov and
* conn->read_msg.msg_iovlen on amount of copied data. This
* BUG_ON is intended to catch if it is changed in the future.
*/
sBUG_ON((res >= first_len) &&
(conn->read_msg.msg_iov->iov_len != 0));
conn->read_size -= res;
if (conn->read_size) {
if (res >= first_len) {
int done = 1 + ((res - first_len) >> PAGE_SHIFT);
conn->read_msg.msg_iov += done;
conn->read_msg.msg_iovlen -= done;
}
} else
conn->read_state = state;
}
out:
TRACE_EXIT_RES(res);
return res;
}
static int rx_hdigest(struct iscsi_conn *conn)
{
struct iscsi_cmnd *cmnd = conn->read_cmnd;
int res = digest_rx_header(cmnd);
if (unlikely(res != 0)) {
PRINT_ERROR_PR("rx header digest for initiator %s failed "
"(%d)", conn->session->initiator_name, res);
mark_conn_closed(conn);
}
return res;
}
static struct iscsi_cmnd *create_cmnd(struct iscsi_conn *conn)
{
struct iscsi_cmnd *cmnd;
cmnd = cmnd_alloc(conn, NULL);
iscsi_conn_init_read(cmnd->conn, &cmnd->pdu.bhs, sizeof(cmnd->pdu.bhs));
conn->read_state = RX_BHS;
return cmnd;
}
/* Returns >0 for success, <=0 for error or successful finish */
static int recv(struct iscsi_conn *conn)
{
struct iscsi_cmnd *cmnd = conn->read_cmnd;
int hdigest, ddigest, res = 1, rc;
TRACE_ENTRY();
hdigest = conn->hdigest_type & DIGEST_NONE ? 0 : 1;
ddigest = conn->ddigest_type & DIGEST_NONE ? 0 : 1;
switch (conn->read_state) {
case RX_INIT_BHS:
sBUG_ON(cmnd != NULL);
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:
rc = rx_hdigest(conn);
if (likely(rc == 0))
conn->read_state = RX_INIT_DATA;
else {
res = rc;
break;
}
case RX_INIT_DATA:
rc = cmnd_rx_start(cmnd);
if (unlikely(rc != 0)) {
sBUG_ON(!conn->closing);
conn->read_state = RX_END;
res = rc;
/* cmnd will be freed in close_conn() */
goto out;
}
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:
conn->read_state = RX_END;
if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_CMD) {
TRACE_DBG("Adding RX ddigest cmd %p to digest list "
"of self", cmnd);
list_add_tail(&cmnd->rx_ddigest_cmd_list_entry,
&cmnd->rx_ddigest_cmd_list);
cmnd_get(cmnd);
conn->read_state = RX_END;
} else if (cmnd_opcode(cmnd) != ISCSI_OP_SCSI_DATA_OUT) {
/*
* We could get here only for NOP-Out. ISCSI RFC doesn't
* specify how to deal with digest errors in this case.
* Is closing connection correct?
*/
TRACE_DBG("cmnd %p, opcode %x: checking RX "
"ddigest inline", cmnd, cmnd_opcode(cmnd));
rc = digest_rx_data(cmnd);
if (unlikely(rc != 0)) {
conn->read_state = RX_CHECK_DDIGEST;
mark_conn_closed(conn);
}
}
break;
default:
PRINT_ERROR_PR("%d %x", conn->read_state, cmnd_opcode(cmnd));
sBUG();
}
if (res <= 0)
goto out;
if (conn->read_state != RX_END)
goto out;
if (conn->read_size) {
PRINT_ERROR_PR("%d %x %d", res, cmnd_opcode(cmnd), conn->read_size);
sBUG();
}
cmnd_rx_end(cmnd);
sBUG_ON(conn->read_size != 0);
conn->read_cmnd = NULL;
conn->read_state = RX_INIT_BHS;
res = 0;
out:
TRACE_EXIT_RES(res);
return res;
}
/* No locks, conn is rd processing */
static int process_read_io(struct iscsi_conn *conn, int *closed)
{
int res;
do {
res = recv(conn);
if (unlikely(conn->closing)) {
close_conn(conn);
*closed = 1;
break;
}
} while(res > 0);
TRACE_EXIT_RES(res);
return res;
}
/*
* Called under iscsi_rd_lock and BHs disabled, but will drop it inside,
* then reaquire.
*/
static void scst_do_job_rd(void)
{
TRACE_ENTRY();
/* We delete/add to tail connections to maintain fairness between them */
while(!list_empty(&iscsi_rd_list)) {
int rc, closed = 0;
struct iscsi_conn *conn = list_entry(iscsi_rd_list.next,
typeof(*conn), rd_list_entry);
list_del(&conn->rd_list_entry);
sBUG_ON(conn->rd_state == ISCSI_CONN_RD_STATE_PROCESSING);
conn->rd_data_ready = 0;
conn->rd_state = ISCSI_CONN_RD_STATE_PROCESSING;
#ifdef EXTRACHECKS
conn->rd_task = current;
#endif
spin_unlock_bh(&iscsi_rd_lock);
rc = process_read_io(conn, &closed);
spin_lock_bh(&iscsi_rd_lock);
if (closed)
continue;
#ifdef EXTRACHECKS
conn->rd_task = NULL;
#endif
if ((rc == 0) || conn->rd_data_ready) {
list_add_tail(&conn->rd_list_entry, &iscsi_rd_list);
conn->rd_state = ISCSI_CONN_RD_STATE_IN_LIST;
} else
conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
}
TRACE_EXIT();
return;
}
static inline int test_rd_list(void)
{
int res = !list_empty(&iscsi_rd_list) ||
unlikely(kthread_should_stop());
return res;
}
int istrd(void *arg)
{
TRACE_ENTRY();
current->flags |= PF_NOFREEZE;
spin_lock_bh(&iscsi_rd_lock);
while(!kthread_should_stop()) {
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
if (!test_rd_list()) {
add_wait_queue_exclusive(&iscsi_rd_waitQ, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (test_rd_list())
break;
spin_unlock_bh(&iscsi_rd_lock);
schedule();
spin_lock_bh(&iscsi_rd_lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&iscsi_rd_waitQ, &wait);
}
scst_do_job_rd();
}
spin_unlock_bh(&iscsi_rd_lock);
/*
* If kthread_should_stop() is true, we are guaranteed to be
* on the module unload, so iscsi_rd_list must be empty.
*/
sBUG_ON(!list_empty(&iscsi_rd_list));
TRACE_EXIT();
return 0;
}
#ifdef NET_PAGE_CALLBACKS_DEFINED
void iscsi_get_page_callback(struct page *page)
{
struct iscsi_cmnd *cmd = (struct iscsi_cmnd*)page->net_priv;
int v;
TRACE_DBG("cmd %p, page %p, _count %d, new net_ref_cnt %d",
cmd, page, atomic_read(&page->_count),
atomic_read(&cmd->net_ref_cnt)+1);
v = atomic_inc_return(&cmd->net_ref_cnt);
if (v == 1) {
TRACE_DBG("getting cmd %p for page %p", cmd, page);
cmnd_get(cmd);
}
}
void iscsi_put_page_callback(struct page *page)
{
struct iscsi_cmnd *cmd = (struct iscsi_cmnd*)page->net_priv;
TRACE_DBG("cmd %p, page %p, _count %d, new net_ref_cnt %d",
cmd, page, atomic_read(&page->_count),
atomic_read(&cmd->net_ref_cnt)-1);
if (atomic_dec_and_test(&cmd->net_ref_cnt)) {
int i, sg_cnt = get_pgcnt(cmd->bufflen, cmd->sg[0].offset);
for(i = 0; i < sg_cnt; i++) {
TRACE_DBG("Clearing page %p", cmd->sg[i].page);
cmd->sg[i].page->net_priv = NULL;
}
cmnd_put(cmd);
}
}
#endif
/* This is partially 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 iscsi_cmnd *write_cmnd = conn->write_cmnd;
struct iscsi_cmnd *ref_cmd;
struct scatterlist *sg;
struct iovec *iop;
int saved_size, size, sendsize;
int offset, idx;
int flags, res, count;
iscsi_extracheck_is_wr_thread(conn);
if (write_cmnd->own_sg == 0)
ref_cmd = write_cmnd->parent_req;
else
ref_cmd = write_cmnd;
file = conn->file;
saved_size = size = conn->write_size;
iop = conn->write_iop;
count = conn->write_iop_used;
if (iop) while (1) {
loff_t off = 0;
int rest;
sBUG_ON(count > sizeof(conn->write_iov)/sizeof(conn->write_iov[0]));
retry:
oldfs = get_fs();
set_fs(KERNEL_DS);
res = vfs_writev(file, (struct iovec __user *)iop, count, &off);
set_fs(oldfs);
TRACE(TRACE_D_DATA, "%#Lx:%u: %d(%ld)",
(unsigned long long) conn->session->sid, conn->cid,
res, (long) iop->iov_len);
if (unlikely(res <= 0)) {
if (res == -EAGAIN) {
conn->write_iop = iop;
conn->write_iop_used = count;
goto out_iov;
} else if (res == -EINTR)
goto retry;
goto err;
}
rest = res;
size -= res;
while (iop->iov_len <= rest && rest) {
rest -= iop->iov_len;
iop++;
count--;
}
if (count == 0) {
conn->write_iop = NULL;
conn->write_iop_used = 0;
if (size)
break;
goto out_iov;
}
sBUG_ON(iop > conn->write_iov +
sizeof(conn->write_iov)/sizeof(conn->write_iov[0]));
iop->iov_base += rest;
iop->iov_len -= rest;
}
sg = write_cmnd->sg;
if (sg == NULL) {
PRINT_ERROR_PR("%s", "warning data missing!");
return 0;
}
offset = conn->write_offset;
idx = offset >> PAGE_SHIFT;
offset &= ~PAGE_MASK;
sock = conn->sock;
#ifdef NET_PAGE_CALLBACKS_DEFINED
sendpage = sock->ops->sendpage;
#else
if ((write_cmnd->parent_req->scst_cmd != NULL) &&
scst_cmd_get_data_buff_alloced(write_cmnd->parent_req->scst_cmd))
sendpage = sock_no_sendpage;
else
sendpage = sock->ops->sendpage;
#endif
flags = MSG_DONTWAIT;
while (1) {
#ifdef NET_PAGE_CALLBACKS_DEFINED
if (unlikely((sg[idx].page->net_priv != NULL) &&
(sg[idx].page->net_priv != ref_cmd))) {
PRINT_ERROR_PR("net_priv isn't NULL and != ref_cmd "
"(write_cmnd %p, ref_cmd %p, sg %p, idx %d, "
"net_priv %p)", write_cmnd, ref_cmd, sg, idx,
sg[idx].page->net_priv);
sBUG();
}
sg[idx].page->net_priv = ref_cmd;
#endif
sendsize = PAGE_SIZE - offset;
if (size <= sendsize) {
retry2:
res = sendpage(sock, sg[idx].page, offset, size, flags);
TRACE(TRACE_D_DATA, "%s %#Lx:%u: %d(%lu,%u,%u)",
sock->ops->sendpage ? "sendpage" : "sock_no_sendpage",
(unsigned long long)conn->session->sid, conn->cid,
res, sg[idx].page->index, offset, size);
if (unlikely(res <= 0)) {
if (res == -EINTR)
goto retry2;
else
goto out_res;
}
if (res == size) {
conn->write_size = 0;
return saved_size;
}
offset += res;
size -= res;
continue;
}
retry1:
res = sendpage(sock, sg[idx].page, offset, sendsize,
flags | MSG_MORE);
TRACE(TRACE_D_DATA, "%s %#Lx:%u: %d(%lu,%u,%u)",
sock->ops->sendpage ? "sendpage" : "sock_no_sendpage",
(unsigned long long ) conn->session->sid, conn->cid,
res, sg[idx].page->index, offset, sendsize);
if (unlikely(res <= 0)) {
if (res == -EINTR)
goto retry1;
else
goto out_res;
}
if (res == sendsize) {
idx++;
offset = 0;
} else
offset += res;
size -= res;
}
out:
conn->write_offset = (idx << PAGE_SHIFT) + offset;
out_iov:
conn->write_size = size;
if ((saved_size == size) && res == -EAGAIN)
return res;
return saved_size - size;
out_res:
#ifdef NET_PAGE_CALLBACKS_DEFINED
if (atomic_read(&ref_cmd->net_ref_cnt) == 0) {
TRACE_DBG("sendpage() returned %d, zeroing net_priv", res);
sg[idx].page->net_priv = NULL;
}
#endif
if (res == -EAGAIN)
goto out;
/* else go through */
err:
#ifndef DEBUG
if (!conn->closing)
#endif
{
PRINT_ERROR_PR("error %d at sid:cid %#Lx:%u, cmnd %p", res,
(unsigned long long)conn->session->sid, conn->cid,
conn->write_cmnd);
}
return res;
}
static int exit_tx(struct iscsi_conn *conn, int res)
{
iscsi_extracheck_is_wr_thread(conn);
switch (res) {
case -EAGAIN:
case -ERESTARTSYS:
res = 0;
break;
default:
#ifndef DEBUG
if (!conn->closing)
#endif
{
PRINT_ERROR_PR("Sending data failed: initiator %s, "
"write_size %d, write_state %d, res %d",
conn->session->initiator_name, conn->write_size,
conn->write_state, res);
}
conn->write_state = TX_END;
conn->write_size = 0;
mark_conn_closed(conn);
break;
}
return res;
}
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;
iscsi_extracheck_is_wr_thread(cmnd->conn);
TRACE_DBG("Sending data digest %x (cmd %p)", cmnd->ddigest, cmnd);
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
res = 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;
iscsi_extracheck_is_wr_thread(conn);
digest_tx_header(cmnd);
sBUG_ON(conn->write_iop_used >= sizeof(conn->write_iov)/sizeof(conn->write_iov[0]));
iop = &conn->write_iop[conn->write_iop_used];
conn->write_iop_used++;
iop->iov_base = &(cmnd->hdigest);
iop->iov_len = sizeof(u32);
conn->write_size += sizeof(u32);
return;
}
static int iscsi_do_send(struct iscsi_conn *conn, int state)
{
int res;
iscsi_extracheck_is_wr_thread(conn);
res = write_data(conn);
if (res > 0) {
if (!conn->write_size)
conn->write_state = state;
} else
res = exit_tx(conn, res);
return res;
}
/* No locks, conn is wr processing */
int iscsi_send(struct iscsi_conn *conn)
{
struct iscsi_cmnd *cmnd = conn->write_cmnd;
int ddigest, res = 0;
TRACE_ENTRY();
TRACE_DBG("conn %p, write_cmnd %p", conn, cmnd);
iscsi_extracheck_is_wr_thread(conn);
ddigest = conn->ddigest_type != DIGEST_NONE ? 1 : 0;
switch (conn->write_state) {
case TX_INIT:
sBUG_ON(cmnd != NULL);
cmnd = conn->write_cmnd = iscsi_get_send_cmnd(conn);
if (!cmnd)
goto out;
cmnd_tx_start(cmnd);
if (!(conn->hdigest_type & DIGEST_NONE))
init_tx_hdigest(cmnd);
conn->write_state = TX_BHS_DATA;
case TX_BHS_DATA:
res = iscsi_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:
cmnd->conn->write_size = sizeof(u32);
conn->write_state = TX_DDIGEST;
case TX_DDIGEST:
res = tx_ddigest(cmnd, TX_END);
break;
default:
PRINT_ERROR_PR("%d %d %x", res, conn->write_state,
cmnd_opcode(cmnd));
sBUG();
}
if (res == 0)
goto out;
if (conn->write_state != TX_END)
goto out;
if (conn->write_size) {
PRINT_ERROR_PR("%d %x %u", res, cmnd_opcode(cmnd),
conn->write_size);
sBUG();
}
cmnd_tx_end(cmnd);
rsp_cmnd_release(cmnd);
conn->write_cmnd = NULL;
conn->write_state = TX_INIT;
out:
TRACE_EXIT_RES(res);
return res;
}
/* No locks, conn is wr processing */
static int process_write_queue(struct iscsi_conn *conn)
{
int res = 0;
TRACE_ENTRY();
if (likely(test_write_ready(conn)))
res = iscsi_send(conn);
TRACE_EXIT_RES(res);
return res;
}
/*
* Called under iscsi_wr_lock and BHs disabled, but will drop it inside,
* then reaquire.
*/
static void scst_do_job_wr(void)
{
TRACE_ENTRY();
/* We delete/add to tail connections to maintain fairness between them */
while(!list_empty(&iscsi_wr_list)) {
int rc;
struct iscsi_conn *conn = list_entry(iscsi_wr_list.next,
typeof(*conn), wr_list_entry);
TRACE_DBG("conn %p, wr_state %x, wr_space_ready %d, "
"write ready %d", conn, conn->wr_state,
conn->wr_space_ready, test_write_ready(conn));
list_del(&conn->wr_list_entry);
sBUG_ON(conn->wr_state == ISCSI_CONN_WR_STATE_PROCESSING);
conn->wr_state = ISCSI_CONN_WR_STATE_PROCESSING;
conn->wr_space_ready = 0;
#ifdef EXTRACHECKS
conn->wr_task = current;
#endif
spin_unlock_bh(&iscsi_wr_lock);
rc = process_write_queue(conn);
spin_lock_bh(&iscsi_wr_lock);
#ifdef EXTRACHECKS
conn->wr_task = NULL;
#endif
if ((rc == -EAGAIN) && !conn->wr_space_ready) {
conn->wr_state = ISCSI_CONN_WR_STATE_SPACE_WAIT;
continue;
}
if (test_write_ready(conn)) {
list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
} else
conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
}
TRACE_EXIT();
return;
}
static inline int test_wr_list(void)
{
int res = !list_empty(&iscsi_wr_list) ||
unlikely(kthread_should_stop());
return res;
}
int istwr(void *arg)
{
TRACE_ENTRY();
current->flags |= PF_NOFREEZE;
spin_lock_bh(&iscsi_wr_lock);
while(!kthread_should_stop()) {
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
if (!test_wr_list()) {
add_wait_queue_exclusive(&iscsi_wr_waitQ, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (test_wr_list())
break;
spin_unlock_bh(&iscsi_wr_lock);
schedule();
spin_lock_bh(&iscsi_wr_lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&iscsi_wr_waitQ, &wait);
}
scst_do_job_wr();
}
spin_unlock_bh(&iscsi_wr_lock);
/*
* If kthread_should_stop() is true, we are guaranteed to be
* on the module unload, so iscsi_wr_list must be empty.
*/
sBUG_ON(!list_empty(&iscsi_wr_list));
TRACE_EXIT();
return 0;
}

234
iscsi-scst/kernel/param.c Normal file
View File

@@ -0,0 +1,234 @@
/*
* (C) 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#include "iscsi.h"
#include "digest.h"
#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) { \
PRINT_ERROR_PR("%s: %u is out of range (%u %u)",\
#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 const char *get_bool_name(int val)
{
if (val)
return "Yes";
else
return "No";
}
static const char *get_digest_name(int val)
{
if (val == DIGEST_NONE)
return "None";
else if (val == DIGEST_CRC32C)
return "CRC32C";
else
return "Unknown";
}
static void log_params(struct iscsi_sess_param *param)
{
PRINT_INFO_PR("Negotiated parameters: InitialR2T %s, ImmediateData %s, "
"MaxConnections %d, MaxRecvDataSegmentLength %d, "
"MaxXmitDataSegmentLength %d, ", get_bool_name(param->initial_r2t),
get_bool_name(param->immediate_data), param->max_connections,
param->max_recv_data_length, param->max_xmit_data_length);
PRINT_INFO_PR(" MaxBurstLength %d, FirstBurstLength %d, "
"DefaultTime2Wait %d, DefaultTime2Retain %d, ",
param->max_burst_length, param->first_burst_length,
param->default_wait_time, param->default_retain_time);
PRINT_INFO_PR(" MaxOutstandingR2T %d, DataPDUInOrder %s, "
"DataSequenceInOrder %s, ErrorRecoveryLevel %d, ",
param->max_outstanding_r2t, get_bool_name(param->data_pdu_inorder),
get_bool_name(param->data_sequence_inorder),
param->error_recovery_level);
PRINT_INFO_PR(" HeaderDigest %s, DataDigest %s, OFMarker %s, "
"IFMarker %s, OFMarkInt %d, IFMarkInt %d",
get_digest_name(param->header_digest),
get_digest_name(param->data_digest),
get_bool_name(param->ofmarker), get_bool_name(param->ifmarker),
param->ofmarkint, param->ifmarkint);
}
/* target_mutex supposed to be locked */
static void sess_param_check(struct iscsi_param_info *info)
{
u32 *iparam = info->session_param;
CHECK_PARAM(info, iparam, max_connections, 1, 1);
CHECK_PARAM(info, iparam, max_recv_data_length, 512,
(u32) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
CHECK_PARAM(info, iparam, max_xmit_data_length, 512,
(u32) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
CHECK_PARAM(info, iparam, error_recovery_level, 0, 0);
CHECK_PARAM(info, iparam, data_pdu_inorder, 0, 1);
CHECK_PARAM(info, iparam, data_sequence_inorder, 0, 1);
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);
}
/* target_mutex supposed to be locked */
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);
}
/* target_mutex supposed to be locked */
static void trgt_param_check(struct iscsi_param_info *info)
{
u32 *iparam = info->target_param;
CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS, MAX_NR_QUEUED_CMNDS);
}
/* target_mutex supposed to be locked */
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;
SET_PARAM(param, info, iparam, queued_cmnds);
}
/* target_mutex supposed to be locked */
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, queued_cmnds);
}
/* target_mutex supposed to be locked */
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;
}
/* target_mutex supposed to be locked */
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;
if (set && !list_empty(&session->conn_list)) {
err = -EBUSY;
goto out;
}
param = &session->sess_param;
} else
param = &target->trgt_sess_param;
if (set) {
sess_param_set(param, info);
if (session != NULL)
log_params(param);
} else
sess_param_get(param, info);
err = 0;
out:
return err;
}
/* target_mutex supposed to be locked */
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;
}

View File

@@ -0,0 +1,245 @@
diff -upr linux-2.6.16.29/include/linux/mm.h linux-2.6.16.29/include/linux/mm.h
--- linux-2.6.16.29/include/linux/mm.h 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/include/linux/mm.h 2007-08-07 19:54:27.000000000 +0400
@@ -263,6 +263,15 @@ struct page {
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completeion. Not good to have this field here, it's better to have
+ * it in struct sk_buff, but it would make the code much more
+ * complicated and fragile, if maintained as a separate patch, since all
+ * skb then would have to contain only pages with the same value in this
+ * field.
+ */
+ void *net_priv;
};
#define page_private(page) ((page)->private)
diff -upr linux-2.6.16.29/include/linux/net.h linux-2.6.16.29/include/linux/net.h
--- linux-2.6.16.29/include/linux/net.h 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/include/linux/net.h 2007-08-07 19:55:27.000000000 +0400
@@ -22,6 +22,7 @@
#include <linux/wait.h>
#include <linux/stringify.h>
#include <asm/socket.h>
+#include <linux/mm.h>
struct poll_table_struct;
struct inode;
@@ -294,5 +295,30 @@ extern int net_msg_cost;
extern int net_msg_burst;
#endif
+/* Support for notification on zero-copy TCP transfer completeion */
+#define NET_PAGE_CALLBACKS_DEFINED
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
diff -upr linux-2.6.16.29/net/core/skbuff.c linux-2.6.16.29/net/core/skbuff.c
--- linux-2.6.16.29/net/core/skbuff.c 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/net/core/skbuff.c 2007-08-07 19:55:51.000000000 +0400
@@ -271,7 +271,7 @@ void skb_release_data(struct sk_buff *sk
if (skb_shinfo(skb)->nr_frags) {
int i;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
}
if (skb_shinfo(skb)->frag_list)
@@ -588,7 +588,7 @@ struct sk_buff *pskb_copy(struct sk_buff
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].page);
+ net_get_page(skb_shinfo(n)->frags[i].page);
}
skb_shinfo(n)->nr_frags = i;
}
@@ -642,7 +642,7 @@ int pskb_expand_head(struct sk_buff *skb
memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_clone_fraglist(skb);
@@ -794,7 +794,7 @@ int ___pskb_trim(struct sk_buff *skb, un
return -ENOMEM;
}
if (len <= offset) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb)->nr_frags--;
} else {
skb_shinfo(skb)->frags[i].size = len - offset;
@@ -940,7 +940,7 @@ pull_pages:
k = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
@@ -1522,7 +1522,7 @@ static inline void skb_split_no_header(s
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += len - pos;
skb_shinfo(skb1)->frags[0].size -= len - pos;
skb_shinfo(skb)->frags[i].size = len - pos;
diff -upr linux-2.6.16.29/net/core/utils.c linux-2.6.16.29/net/core/utils.c
--- linux-2.6.16.29/net/core/utils.c 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/net/core/utils.c 2007-08-07 19:54:27.000000000 +0400
@@ -24,11 +24,15 @@
#include <linux/random.h>
#include <linux/percpu.h>
#include <linux/init.h>
+#include <linux/skbuff.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+net_get_page_callback_t net_get_page_callback __read_mostly;
+net_put_page_callback_t net_put_page_callback __read_mostly;
+
/*
This is a maximally equidistributed combined Tausworthe generator
based on code from GNU Scientific Library 1.5 (30 Jun 2004)
@@ -190,3 +194,29 @@ __be32 in_aton(const char *str)
}
EXPORT_SYMBOL(in_aton);
+
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(net_set_get_put_page_callbacks);
diff -upr linux-2.6.16.29/net/ipv4/ip_output.c linux-2.6.16.29/net/ipv4/ip_output.c
--- linux-2.6.16.29/net/ipv4/ip_output.c 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/net/ipv4/ip_output.c 2007-08-07 19:54:27.000000000 +0400
@@ -997,7 +997,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
@@ -1155,7 +1155,7 @@ ssize_t ip_append_page(struct sock *sk,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_shinfo(skb)->frags[i-1].size += len;
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
diff -upr linux-2.6.16.29/net/ipv4/tcp.c linux-2.6.16.29/net/ipv4/tcp.c
--- linux-2.6.16.29/net/ipv4/tcp.c 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/net/ipv4/tcp.c 2007-08-07 19:54:27.000000000 +0400
@@ -558,7 +558,7 @@ new_segment:
if (can_coalesce) {
skb_shinfo(skb)->frags[i - 1].size += copy;
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
@@ -767,7 +767,7 @@ new_segment:
goto new_segment;
} else if (page) {
if (off == PAGE_SIZE) {
- put_page(page);
+ net_put_page(page);
TCP_PAGE(sk) = page = NULL;
off = 0;
}
@@ -808,9 +808,9 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, page, off, copy);
if (TCP_PAGE(sk)) {
- get_page(page);
+ net_get_page(page);
} else if (off + copy < PAGE_SIZE) {
- get_page(page);
+ net_get_page(page);
TCP_PAGE(sk) = page;
}
}
diff -upr linux-2.6.16.29/net/ipv4/tcp_output.c linux-2.6.16.29/net/ipv4/tcp_output.c
--- linux-2.6.16.29/net/ipv4/tcp_output.c 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/net/ipv4/tcp_output.c 2007-08-07 19:54:27.000000000 +0400
@@ -633,7 +633,7 @@ static unsigned char *__pskb_trim_head(s
k = 0;
for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
diff -upr linux-2.6.16.29/net/ipv6/ip6_output.c linux-2.6.16.29/net/ipv6/ip6_output.c
--- linux-2.6.16.29/net/ipv6/ip6_output.c 2006-09-12 22:02:10.000000000 +0400
+++ linux-2.6.16.29/net/ipv6/ip6_output.c 2007-08-07 19:54:27.000000000 +0400
@@ -1100,7 +1100,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}

View File

@@ -0,0 +1,254 @@
diff -upr linux-2.6.18.1/include/linux/mm.h linux-2.6.18.1/include/linux/mm.h
--- linux-2.6.18.1/include/linux/mm.h 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/include/linux/mm.h 2007-08-07 19:35:51.000000000 +0400
@@ -267,6 +267,15 @@ struct page {
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completeion. Not good to have this field here, it's better to have
+ * it in struct sk_buff, but it would make the code much more
+ * complicated and fragile, if maintained as a separate patch, since all
+ * skb then would have to contain only pages with the same value in this
+ * field.
+ */
+ void *net_priv;
};
#define page_private(page) ((page)->private)
diff -upr linux-2.6.18.1/include/linux/net.h linux-2.6.18.1/include/linux/net.h
--- linux-2.6.18.1/include/linux/net.h 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/include/linux/net.h 2007-08-07 19:35:51.000000000 +0400
@@ -19,6 +19,7 @@
#define _LINUX_NET_H
#include <linux/wait.h>
+#include <linux/mm.h>
#include <asm/socket.h>
struct poll_table_struct;
@@ -304,5 +305,30 @@ extern int net_msg_cost;
extern int net_msg_burst;
#endif
+/* Support for notification on zero-copy TCP transfer completeion */
+#define NET_PAGE_CALLBACKS_DEFINED
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
diff -upr linux-2.6.18.1/net/core/skbuff.c linux-2.6.18.1/net/core/skbuff.c
--- linux-2.6.18.1/net/core/skbuff.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/net/core/skbuff.c 2007-08-07 19:35:51.000000000 +0400
@@ -309,7 +309,7 @@ static void skb_release_data(struct sk_b
if (skb_shinfo(skb)->nr_frags) {
int i;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
}
if (skb_shinfo(skb)->frag_list)
@@ -646,7 +646,7 @@ struct sk_buff *pskb_copy(struct sk_buff
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].page);
+ net_get_page(skb_shinfo(n)->frags[i].page);
}
skb_shinfo(n)->nr_frags = i;
}
@@ -700,7 +700,7 @@ int pskb_expand_head(struct sk_buff *skb
memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_clone_fraglist(skb);
@@ -882,7 +882,7 @@ drop_pages:
skb_shinfo(skb)->nr_frags = i;
for (; i < nfrags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_drop_fraglist(skb);
@@ -1051,7 +1051,7 @@ pull_pages:
k = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
@@ -1633,7 +1633,7 @@ static inline void skb_split_no_header(s
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += len - pos;
skb_shinfo(skb1)->frags[0].size -= len - pos;
skb_shinfo(skb)->frags[i].size = len - pos;
@@ -2002,7 +2002,7 @@ struct sk_buff *skb_segment(struct sk_bu
BUG_ON(i >= nfrags);
*frag = skb_shinfo(skb)->frags[i];
- get_page(frag->page);
+ net_get_page(frag->page);
size = frag->size;
if (pos < offset) {
diff -upr linux-2.6.18.1/net/core/utils.c linux-2.6.18.1/net/core/utils.c
--- linux-2.6.18.1/net/core/utils.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/net/core/utils.c 2007-08-07 19:36:59.000000000 +0400
@@ -24,11 +24,15 @@
#include <linux/random.h>
#include <linux/percpu.h>
#include <linux/init.h>
+#include <linux/skbuff.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+net_get_page_callback_t net_get_page_callback __read_mostly;
+net_put_page_callback_t net_put_page_callback __read_mostly;
+
/*
This is a maximally equidistributed combined Tausworthe generator
based on code from GNU Scientific Library 1.5 (30 Jun 2004)
@@ -191,3 +195,29 @@ __be32 in_aton(const char *str)
}
EXPORT_SYMBOL(in_aton);
+
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(net_set_get_put_page_callbacks);
diff -upr linux-2.6.18.1/net/ipv4/ip_output.c linux-2.6.18.1/net/ipv4/ip_output.c
--- linux-2.6.18.1/net/ipv4/ip_output.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/net/ipv4/ip_output.c 2007-08-07 19:37:24.000000000 +0400
@@ -999,7 +999,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
@@ -1159,7 +1159,7 @@ ssize_t ip_append_page(struct sock *sk,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_shinfo(skb)->frags[i-1].size += len;
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
diff -upr linux-2.6.18.1/net/ipv4/tcp.c linux-2.6.18.1/net/ipv4/tcp.c
--- linux-2.6.18.1/net/ipv4/tcp.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/net/ipv4/tcp.c 2007-08-07 19:35:51.000000000 +0400
@@ -559,7 +559,7 @@ new_segment:
if (can_coalesce) {
skb_shinfo(skb)->frags[i - 1].size += copy;
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
@@ -762,7 +762,7 @@ new_segment:
goto new_segment;
} else if (page) {
if (off == PAGE_SIZE) {
- put_page(page);
+ net_put_page(page);
TCP_PAGE(sk) = page = NULL;
off = 0;
}
@@ -803,9 +803,9 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, page, off, copy);
if (TCP_PAGE(sk)) {
- get_page(page);
+ net_get_page(page);
} else if (off + copy < PAGE_SIZE) {
- get_page(page);
+ net_get_page(page);
TCP_PAGE(sk) = page;
}
}
diff -upr linux-2.6.18.1/net/ipv4/tcp_output.c linux-2.6.18.1/net/ipv4/tcp_output.c
--- linux-2.6.18.1/net/ipv4/tcp_output.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/net/ipv4/tcp_output.c 2007-08-07 19:35:51.000000000 +0400
@@ -657,7 +657,7 @@ static void __pskb_trim_head(struct sk_b
k = 0;
for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
diff -upr linux-2.6.18.1/net/ipv6/ip6_output.c linux-2.6.18.1/net/ipv6/ip6_output.c
--- linux-2.6.18.1/net/ipv6/ip6_output.c 2006-09-20 07:42:06.000000000 +0400
+++ linux-2.6.18.1/net/ipv6/ip6_output.c 2007-08-07 19:35:51.000000000 +0400
@@ -1149,7 +1149,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}

View File

@@ -0,0 +1,256 @@
diff -upr linux-2.6.21.1/include/linux/mm_types.h linux-2.6.21.1/include/linux/mm_types.h
--- linux-2.6.21.1/include/linux/mm_types.h 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/include/linux/mm_types.h 2007-07-04 12:56:56.000000000 +0400
@@ -62,6 +62,15 @@ struct page {
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completeion. Not good to have this field here, it's better to have
+ * it in struct sk_buff, but it would make the code much more
+ * complicated and fragile, if maintained as a separate patch, since all
+ * skb then would have to contain only pages with the same value in this
+ * field.
+ */
+ void *net_priv;
};
#endif /* _LINUX_MM_TYPES_H */
diff -upr linux-2.6.21.1/include/linux/net.h linux-2.6.21.1/include/linux/net.h
--- linux-2.6.21.1/include/linux/net.h 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/include/linux/net.h 2007-07-24 19:09:40.000000000 +0400
@@ -19,6 +19,7 @@
#define _LINUX_NET_H
#include <linux/wait.h>
+#include <linux/mm.h>
#include <asm/socket.h>
struct poll_table_struct;
@@ -319,5 +320,30 @@ extern int net_msg_cost;
extern int net_msg_burst;
#endif
+/* Support for notification on zero-copy TCP transfer completeion */
+#define NET_PAGE_CALLBACKS_DEFINED
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
diff -upr linux-2.6.21.1/net/core/skbuff.c linux-2.6.21.1/net/core/skbuff.c
--- linux-2.6.21.1/net/core/skbuff.c 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/net/core/skbuff.c 2007-07-04 13:18:04.000000000 +0400
@@ -257,7 +257,7 @@ static void skb_release_data(struct sk_b
if (skb_shinfo(skb)->nr_frags) {
int i;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
}
if (skb_shinfo(skb)->frag_list)
@@ -596,7 +596,7 @@ struct sk_buff *pskb_copy(struct sk_buff
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].page);
+ net_get_page(skb_shinfo(n)->frags[i].page);
}
skb_shinfo(n)->nr_frags = i;
}
@@ -650,7 +650,7 @@ int pskb_expand_head(struct sk_buff *skb
memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_clone_fraglist(skb);
@@ -832,7 +832,7 @@ drop_pages:
skb_shinfo(skb)->nr_frags = i;
for (; i < nfrags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_drop_fraglist(skb);
@@ -1001,7 +1001,7 @@ pull_pages:
k = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
@@ -1583,7 +1583,7 @@ static inline void skb_split_no_header(s
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += len - pos;
skb_shinfo(skb1)->frags[0].size -= len - pos;
skb_shinfo(skb)->frags[i].size = len - pos;
@@ -1951,7 +1951,7 @@ struct sk_buff *skb_segment(struct sk_bu
BUG_ON(i >= nfrags);
*frag = skb_shinfo(skb)->frags[i];
- get_page(frag->page);
+ net_get_page(frag->page);
size = frag->size;
if (pos < offset) {
diff -upr linux-2.6.21.1/net/core/utils.c linux-2.6.21.1/net/core/utils.c
--- linux-2.6.21.1/net/core/utils.c 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/net/core/utils.c 2007-07-25 12:17:19.000000000 +0400
@@ -25,6 +25,7 @@
#include <linux/random.h>
#include <linux/percpu.h>
#include <linux/init.h>
+#include <linux/skbuff.h>
#include <asm/byteorder.h>
#include <asm/system.h>
@@ -33,6 +34,9 @@
int net_msg_cost = 5*HZ;
int net_msg_burst = 10;
+net_get_page_callback_t net_get_page_callback __read_mostly;
+net_put_page_callback_t net_put_page_callback __read_mostly;
+
/*
* All net warning printk()s should be guarded by this function.
*/
@@ -290,3 +294,29 @@ out:
}
EXPORT_SYMBOL(in6_pton);
+
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(net_set_get_put_page_callbacks);
diff -upr linux-2.6.21.1/net/ipv4/ip_output.c linux-2.6.21.1/net/ipv4/ip_output.c
--- linux-2.6.21.1/net/ipv4/ip_output.c 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/net/ipv4/ip_output.c 2007-07-04 13:17:53.000000000 +0400
@@ -991,7 +991,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
@@ -1151,7 +1151,7 @@ ssize_t ip_append_page(struct sock *sk,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_shinfo(skb)->frags[i-1].size += len;
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
diff -upr linux-2.6.21.1/net/ipv4/tcp.c linux-2.6.21.1/net/ipv4/tcp.c
--- linux-2.6.21.1/net/ipv4/tcp.c 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/net/ipv4/tcp.c 2007-07-04 13:17:58.000000000 +0400
@@ -561,7 +561,7 @@ new_segment:
if (can_coalesce) {
skb_shinfo(skb)->frags[i - 1].size += copy;
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
@@ -764,7 +764,7 @@ new_segment:
goto new_segment;
} else if (page) {
if (off == PAGE_SIZE) {
- put_page(page);
+ net_put_page(page);
TCP_PAGE(sk) = page = NULL;
off = 0;
}
@@ -805,9 +805,9 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, page, off, copy);
if (TCP_PAGE(sk)) {
- get_page(page);
+ net_get_page(page);
} else if (off + copy < PAGE_SIZE) {
- get_page(page);
+ net_get_page(page);
TCP_PAGE(sk) = page;
}
}
diff -upr linux-2.6.21.1/net/ipv4/tcp_output.c linux-2.6.21.1/net/ipv4/tcp_output.c
--- linux-2.6.21.1/net/ipv4/tcp_output.c 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/net/ipv4/tcp_output.c 2007-07-04 13:17:05.000000000 +0400
@@ -722,7 +722,7 @@ static void __pskb_trim_head(struct sk_b
k = 0;
for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
diff -upr linux-2.6.21.1/net/ipv6/ip6_output.c linux-2.6.21.1/net/ipv6/ip6_output.c
--- linux-2.6.21.1/net/ipv6/ip6_output.c 2007-04-28 01:49:26.000000000 +0400
+++ linux-2.6.21.1/net/ipv6/ip6_output.c 2007-07-04 13:17:49.000000000 +0400
@@ -1227,7 +1227,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}

View File

@@ -0,0 +1,256 @@
diff -upr linux-2.6.22/include/linux/mm_types.h linux-2.6.22/include/linux/mm_types.h
--- linux-2.6.22/include/linux/mm_types.h 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/include/linux/mm_types.h 2007-08-07 19:12:10.000000000 +0400
@@ -78,6 +78,15 @@ struct page {
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
+ /*
+ * Used to implement support for notification on zero-copy TCP transfer
+ * completeion. Not good to have this field here, it's better to have
+ * it in struct sk_buff, but it would make the code much more
+ * complicated and fragile, if maintained as a separate patch, since all
+ * skb then would have to contain only pages with the same value in this
+ * field.
+ */
+ void *net_priv;
};
#endif /* _LINUX_MM_TYPES_H */
diff -upr linux-2.6.22/include/linux/net.h linux-2.6.22/include/linux/net.h
--- linux-2.6.22/include/linux/net.h 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/include/linux/net.h 2007-08-07 19:12:10.000000000 +0400
@@ -19,6 +19,7 @@
#define _LINUX_NET_H
#include <linux/wait.h>
+#include <linux/mm.h>
#include <asm/socket.h>
struct poll_table_struct;
@@ -319,5 +320,30 @@ extern int net_msg_cost;
extern int net_msg_burst;
#endif
+/* Support for notification on zero-copy TCP transfer completeion */
+#define NET_PAGE_CALLBACKS_DEFINED
+typedef void (*net_get_page_callback_t)(struct page *page);
+typedef void (*net_put_page_callback_t)(struct page *page);
+
+extern net_get_page_callback_t net_get_page_callback;
+extern net_put_page_callback_t net_put_page_callback;
+
+extern int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback);
+
+static inline void net_get_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_get_page_callback(page);
+ get_page(page);
+}
+static inline void net_put_page(struct page *page)
+{
+ if (page->net_priv != 0)
+ net_put_page_callback(page);
+ put_page(page);
+}
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
diff -upr linux-2.6.22/net/core/skbuff.c linux-2.6.22/net/core/skbuff.c
--- linux-2.6.22/net/core/skbuff.c 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/net/core/skbuff.c 2007-08-07 19:12:10.000000000 +0400
@@ -262,7 +262,7 @@ static void skb_release_data(struct sk_b
if (skb_shinfo(skb)->nr_frags) {
int i;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
}
if (skb_shinfo(skb)->frag_list)
@@ -590,7 +590,7 @@ struct sk_buff *pskb_copy(struct sk_buff
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].page);
+ net_get_page(skb_shinfo(n)->frags[i].page);
}
skb_shinfo(n)->nr_frags = i;
}
@@ -653,7 +653,7 @@ int pskb_expand_head(struct sk_buff *skb
sizeof(struct skb_shared_info));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_clone_fraglist(skb);
@@ -850,7 +850,7 @@ drop_pages:
skb_shinfo(skb)->nr_frags = i;
for (; i < nfrags; i++)
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
if (skb_shinfo(skb)->frag_list)
skb_drop_fraglist(skb);
@@ -1019,7 +1019,7 @@ pull_pages:
k = 0;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
@@ -1588,7 +1588,7 @@ static inline void skb_split_no_header(s
* where splitting is expensive.
* 2. Split is accurately. We make this.
*/
- get_page(skb_shinfo(skb)->frags[i].page);
+ net_get_page(skb_shinfo(skb)->frags[i].page);
skb_shinfo(skb1)->frags[0].page_offset += len - pos;
skb_shinfo(skb1)->frags[0].size -= len - pos;
skb_shinfo(skb)->frags[i].size = len - pos;
@@ -1963,7 +1963,7 @@ struct sk_buff *skb_segment(struct sk_bu
BUG_ON(i >= nfrags);
*frag = skb_shinfo(skb)->frags[i];
- get_page(frag->page);
+ net_get_page(frag->page);
size = frag->size;
if (pos < offset) {
diff -upr linux-2.6.22/net/core/utils.c linux-2.6.22/net/core/utils.c
--- linux-2.6.22/net/core/utils.c 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/net/core/utils.c 2007-08-07 19:12:10.000000000 +0400
@@ -25,6 +25,7 @@
#include <linux/random.h>
#include <linux/percpu.h>
#include <linux/init.h>
+#include <linux/skbuff.h>
#include <asm/byteorder.h>
#include <asm/system.h>
@@ -35,6 +36,9 @@ int net_msg_burst __read_mostly = 10;
int net_msg_warn __read_mostly = 1;
EXPORT_SYMBOL(net_msg_warn);
+net_get_page_callback_t net_get_page_callback __read_mostly;
+net_put_page_callback_t net_put_page_callback __read_mostly;
+
/*
* All net warning printk()s should be guarded by this function.
*/
@@ -292,3 +296,29 @@ out:
}
EXPORT_SYMBOL(in6_pton);
+
+int net_set_get_put_page_callbacks(
+ net_get_page_callback_t get_callback,
+ net_put_page_callback_t put_callback)
+{
+ int res = 0;
+
+ if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
+ (net_get_page_callback != get_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
+ (net_put_page_callback != put_callback)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ net_get_page_callback = get_callback;
+ net_put_page_callback = put_callback;
+
+out:
+ return res;
+}
+EXPORT_SYMBOL(net_set_get_put_page_callbacks);
diff -upr linux-2.6.22/net/ipv4/ip_output.c linux-2.6.22/net/ipv4/ip_output.c
--- linux-2.6.22/net/ipv4/ip_output.c 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/net/ipv4/ip_output.c 2007-08-07 19:12:10.000000000 +0400
@@ -996,7 +996,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}
@@ -1154,7 +1154,7 @@ ssize_t ip_append_page(struct sock *sk,
if (skb_can_coalesce(skb, i, page, offset)) {
skb_shinfo(skb)->frags[i-1].size += len;
} else if (i < MAX_SKB_FRAGS) {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, len);
} else {
err = -EMSGSIZE;
diff -upr linux-2.6.22/net/ipv4/tcp.c linux-2.6.22/net/ipv4/tcp.c
--- linux-2.6.22/net/ipv4/tcp.c 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/net/ipv4/tcp.c 2007-08-07 19:12:10.000000000 +0400
@@ -560,7 +560,7 @@ new_segment:
if (can_coalesce) {
skb_shinfo(skb)->frags[i - 1].size += copy;
} else {
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
}
@@ -764,7 +764,7 @@ new_segment:
goto new_segment;
} else if (page) {
if (off == PAGE_SIZE) {
- put_page(page);
+ net_put_page(page);
TCP_PAGE(sk) = page = NULL;
off = 0;
}
@@ -805,9 +805,9 @@ new_segment:
} else {
skb_fill_page_desc(skb, i, page, off, copy);
if (TCP_PAGE(sk)) {
- get_page(page);
+ net_get_page(page);
} else if (off + copy < PAGE_SIZE) {
- get_page(page);
+ net_get_page(page);
TCP_PAGE(sk) = page;
}
}
diff -upr linux-2.6.22/net/ipv4/tcp_output.c linux-2.6.22/net/ipv4/tcp_output.c
--- linux-2.6.22/net/ipv4/tcp_output.c 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/net/ipv4/tcp_output.c 2007-08-07 19:12:10.000000000 +0400
@@ -721,7 +721,7 @@ static void __pskb_trim_head(struct sk_b
k = 0;
for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
if (skb_shinfo(skb)->frags[i].size <= eat) {
- put_page(skb_shinfo(skb)->frags[i].page);
+ net_put_page(skb_shinfo(skb)->frags[i].page);
eat -= skb_shinfo(skb)->frags[i].size;
} else {
skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
diff -upr linux-2.6.22/net/ipv6/ip6_output.c linux-2.6.22/net/ipv6/ip6_output.c
--- linux-2.6.22/net/ipv6/ip6_output.c 2007-07-09 03:32:17.000000000 +0400
+++ linux-2.6.22/net/ipv6/ip6_output.c 2007-08-07 19:12:10.000000000 +0400
@@ -1291,7 +1291,7 @@ alloc_new_skb:
err = -EMSGSIZE;
goto error;
}
- get_page(page);
+ net_get_page(page);
skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
frag = &skb_shinfo(skb)->frags[i];
}

180
iscsi-scst/kernel/session.c Normal file
View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include "iscsi.h"
/* target_mutex supposed to be locked */
struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
{
struct iscsi_session *session;
list_for_each_entry(session, &target->session_list, session_list_entry) {
if (session->sid == sid)
return session;
}
return NULL;
}
/* target_mutex supposed to be locked */
static int iscsi_session_alloc(struct iscsi_target *target, struct session_info *info)
{
int err, i;
struct iscsi_session *session;
char *name = NULL;
TRACE_MGMT_DBG("Creating session: target %p, tid %u, sid %#Lx",
target, target->tid, (unsigned long long) info->sid);
if (!(session = kzalloc(sizeof(*session), GFP_KERNEL)))
return -ENOMEM;
session->target = target;
session->sid = info->sid;
BUILD_BUG_ON(sizeof(session->sess_param) !=
sizeof(target->trgt_sess_param));
memcpy(&session->sess_param, &target->trgt_sess_param,
sizeof(session->sess_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_name = kstrdup(info->initiator_name, GFP_KERNEL);
if (!session->initiator_name) {
err = -ENOMEM;
goto err;
}
name = kmalloc(strlen(info->user_name) + strlen(info->initiator_name) +
1, GFP_KERNEL);
if (name == NULL){
err = -ENOMEM;
goto err;
}
if (info->user_name[0] != '\0')
sprintf(name, "%s@%s", info->user_name, info->initiator_name);
else
sprintf(name, "%s", info->initiator_name);
INIT_LIST_HEAD(&session->conn_list);
INIT_LIST_HEAD(&session->pending_list);
spin_lock_init(&session->sn_lock);
spin_lock_init(&session->cmnd_hash_lock);
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
INIT_LIST_HEAD(&session->cmnd_hash[i]);
session->next_ttt = 1;
session->scst_sess = scst_register_session(target->scst_tgt, 0,
name, NULL, NULL);
if (session->scst_sess == NULL) {
PRINT_ERROR_PR("%s", "scst_register_session() failed");
err = -ENOMEM;
goto err;
}
kfree(name);
list_add(&session->session_list_entry, &target->session_list);
return 0;
err:
if (session) {
kfree(session->initiator_name);
kfree(session);
kfree(name);
}
return err;
}
/* target_mutex supposed to be locked */
int session_add(struct iscsi_target *target, struct session_info *info)
{
struct iscsi_session *session;
int err = -EEXIST;
session = session_lookup(target, info->sid);
if (session)
return err;
err = iscsi_session_alloc(target, info);
return err;
}
/* target_mutex supposed to be locked */
int session_free(struct iscsi_session *session)
{
int i;
TRACE_MGMT_DBG("Freeing session %p:%#Lx", session,
(unsigned long long)session->sid);
sBUG_ON(!list_empty(&session->conn_list));
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
sBUG_ON(!list_empty(&session->cmnd_hash[i]));
scst_unregister_session(session->scst_sess, 1, NULL);
list_del(&session->session_list_entry);
kfree(session->initiator_name);
kfree(session);
return 0;
}
/* target_mutex supposed to be locked */
int session_del(struct iscsi_target *target, u64 sid)
{
struct iscsi_session *session;
session = session_lookup(target, sid);
if (!session)
return -ENOENT;
if (!list_empty(&session->conn_list)) {
PRINT_ERROR_PR("%llu still have connections",
(unsigned long long)session->sid);
return -EBUSY;
}
return session_free(session);
}
/* target_mutex supposed to be locked */
static void iscsi_session_info_show(struct seq_file *seq, struct iscsi_target *target)
{
struct iscsi_session *session;
list_for_each_entry(session, &target->session_list, session_list_entry) {
seq_printf(seq, "\tsid:%llu initiator:%s\n",
(unsigned long long) session->sid, session->initiator_name);
conn_info_show(seq, session);
}
}
static int iscsi_sessions_info_show(struct seq_file *seq, void *v)
{
return iscsi_info_show(seq, iscsi_session_info_show);
}
static int iscsi_session_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, iscsi_sessions_info_show, NULL);
}
struct file_operations session_seq_fops = {
.owner = THIS_MODULE,
.open = iscsi_session_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

278
iscsi-scst/kernel/target.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <linux/delay.h>
#include "iscsi.h"
#include "digest.h"
#define MAX_NR_TARGETS (1UL << 30)
DEFINE_MUTEX(target_mgmt_mutex);
/* All 3 protected by target_mgmt_mutex */
static LIST_HEAD(target_list);
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 = 20,
.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 = {
.queued_cmnds = DEFAULT_NR_QUEUED_CMNDS,
};
/* target_mgmt_mutex supposed to be locked */
struct iscsi_target *target_lookup_by_id(u32 id)
{
struct iscsi_target *target;
list_for_each_entry(target, &target_list, target_list_entry) {
if (target->tid == id)
return target;
}
return NULL;
}
/* target_mgmt_mutex supposed to be locked */
static struct iscsi_target *target_lookup_by_name(char *name)
{
struct iscsi_target *target;
list_for_each_entry(target, &target_list, target_list_entry) {
if (!strcmp(target->name, name))
return target;
}
return NULL;
}
/* target_mgmt_mutex supposed to be locked */
static int iscsi_target_create(struct target_info *info, u32 tid)
{
int err = -EINVAL, len;
char *name = info->name;
struct iscsi_target *target;
TRACE_MGMT_DBG("Creating target tid %u, name %s", tid, name);
if (!(len = strlen(name))) {
PRINT_ERROR_PR("The length of the target name is zero %u", tid);
goto out;
}
if (!try_module_get(THIS_MODULE)) {
PRINT_ERROR_PR("Fail to get module %u", tid);
goto out;
}
if (!(target = kzalloc(sizeof(*target), GFP_KERNEL))) {
err = -ENOMEM;
goto out_put;
}
target->tid = info->tid = tid;
memcpy(&target->trgt_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);
INIT_LIST_HEAD(&target->session_list);
list_add(&target->target_list_entry, &target_list);
target->scst_tgt = scst_register(&iscsi_template, target->name);
if (!target->scst_tgt) {
PRINT_ERROR_PR("%s", "scst_register() failed");
goto out_free;
}
return 0;
out_free:
kfree(target);
out_put:
module_put(THIS_MODULE);
out:
return err;
}
/* target_mgmt_mutex supposed to be locked */
int target_add(struct target_info *info)
{
int err = -EEXIST;
u32 tid = info->tid;
if (nr_targets > MAX_NR_TARGETS) {
err = -EBUSY;
goto out;
}
if (target_lookup_by_name(info->name))
goto out;
if (tid && target_lookup_by_id(tid))
goto out;
if (!tid) {
do {
if (!++next_target_id)
++next_target_id;
} while(target_lookup_by_id(next_target_id));
tid = next_target_id;
}
if (!(err = iscsi_target_create(info, tid)))
nr_targets++;
out:
return err;
}
static void target_destroy(struct iscsi_target *target)
{
TRACE_MGMT_DBG("Destroying target tid %u", target->tid);
scst_unregister(target->scst_tgt);
kfree(target);
module_put(THIS_MODULE);
}
/* target_mgmt_mutex supposed to be locked */
int target_del(u32 id)
{
struct iscsi_target *target;
int err;
if (!(target = target_lookup_by_id(id))) {
err = -ENOENT;
goto out;
}
mutex_lock(&target->target_mutex);
if (!list_empty(&target->session_list)) {
err = -EBUSY;
goto out_unlock;
}
list_del(&target->target_list_entry);
nr_targets--;
mutex_unlock(&target->target_mutex);
target_destroy(target);
return 0;
out_unlock:
mutex_unlock(&target->target_mutex);
out:
return err;
}
void target_del_all(void)
{
struct iscsi_target *target, *t;
TRACE_ENTRY();
TRACE(TRACE_MGMT, "%s", "Deleting all targets");
/* Complete brain damage, ToDo */
while(1) {
mutex_lock(&target_mgmt_mutex);
if (list_empty(&target_list))
break;
list_for_each_entry_safe(target, t, &target_list, target_list_entry) {
struct iscsi_session *session, *ts;
mutex_lock(&target->target_mutex);
if (!list_empty(&target->session_list)) {
TRACE_DBG("target %p", target);
list_for_each_entry_safe(session, ts, &target->session_list,
session_list_entry) {
TRACE_DBG("session %p", session);
if (!list_empty(&session->conn_list)) {
struct iscsi_conn *conn, *tc;
list_for_each_entry_safe(conn, tc,
&session->conn_list,
conn_list_entry) {
TRACE_DBG("conn %p", conn);
mark_conn_closed(conn);
}
} else {
TRACE_DBG("session %p with empty "
"connection list", session);
}
}
mutex_unlock(&target->target_mutex);
} else {
TRACE_DBG("deleting target %p", target);
list_del(&target->target_list_entry);
nr_targets--;
mutex_unlock(&target->target_mutex);
target_destroy(target);
continue;
}
}
mutex_unlock(&target_mgmt_mutex);
msleep(100);
}
mutex_unlock(&target_mgmt_mutex);
TRACE_EXIT();
return;
}
int iscsi_info_show(struct seq_file *seq, iscsi_show_info_t *func)
{
int err;
struct iscsi_target *target;
if ((err = mutex_lock_interruptible(&target_mgmt_mutex)) < 0)
return err;
list_for_each_entry(target, &target_list, target_list_entry) {
seq_printf(seq, "tid:%u name:%s\n", target->tid, target->name);
mutex_lock(&target->target_mutex);
func(seq, target);
mutex_unlock(&target->target_mutex);
}
mutex_unlock(&target_mgmt_mutex);
return 0;
}

38
iscsi-scst/usr/Makefile Normal file
View File

@@ -0,0 +1,38 @@
SRCS_D = iscsid.c iscsi_scstd.c conn.c session.c target.c message.c ctldev.c \
log.c chap.c event.c param.c plain.c isns.c
OBJS_D = $(SRCS_D:.c=.o)
SRCS_ADM = iscsi_adm.c param.c
OBJS_ADM = $(SRCS_ADM:.c=.o)
CFLAGS += -O2 -fno-inline -Wall -Wstrict-prototypes -g -I../include
PROGRAMS = iscsi-scstd iscsi-scst-adm
LIBS = -lcrypto
all: $(PROGRAMS)
iscsi-scstd: .depend_d $(OBJS_D)
$(CC) $(OBJS_D) $(LIBS) -o $@
iscsi-scst-adm: .depend_adm $(OBJS_ADM)
$(CC) $(OBJS_ADM) $(LIBS) -o $@
ifeq (.depend_d,$(wildcard .depend_d))
-include .depend_d
endif
ifeq (.depend_adm,$(wildcard .depend_adm))
-include .depend_adm
endif
%.o: %.c Makefile
$(CC) -c -o $(@) $(CFLAGS) $(<)
.depend_d:
$(CC) -M $(CFLAGS) $(SRCS_D) >$(@)
.depend_adm:
$(CC) -M $(CFLAGS) $(SRCS_ADM) >$(@)
clean:
rm -f *.o $(PROGRAMS) .depend*

633
iscsi-scst/usr/chap.c Normal file
View File

@@ -0,0 +1,633 @@
/*
* chap.c - support for (mutual) CHAP authentication.
* (C) 2004 Xiranet Communications GmbH <arne.redlich@xiranet.com>
* available under the terms of the GNU GPL v2.0
*
* heavily based on code from iscsid.c:
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>,
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/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)
{
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(digest, &ctx);
}
static inline void chap_calc_digest_sha1(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest)
{
SHA_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(digest, &ctx);
}
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;
}
conn->user = strdup(value);
if (conn->user == NULL) {
log_error("Unable to dublicate initiator's USER %s", value);
}
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;
}

19
iscsi-scst/usr/config.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef CONFIG_H
#define CONFIG_H
struct config_operations {
int (*init) (char *, char **, int *);
int (*default_load) (char *);
int (*target_add) (u32 *, char *);
int (*target_stop) (u32);
int (*target_del) (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 (*initiator_access) (u32, int);
};
extern struct config_operations *cops;
#endif

151
iscsi-scst/usr/conn.c Normal file
View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <ctype.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#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);
return conn;
}
void conn_free(struct connection *conn)
{
free(conn->initiator);
free(conn->user);
free(conn);
}
int conn_test(struct connection *conn)
{
FILE *f;
char buf[8192], *p;
u32 tid, t_tid, cid, t_cid;
u64 sid, t_sid;
int err = -ENOENT, find = 0;
t_tid = conn->tid;
t_sid = conn->session->sid.id64;
t_cid = conn->cid;
if ((f = fopen(PROC_SESSION, "r")) == NULL) {
fprintf(stderr, "Can't open %s\n", PROC_SESSION);
return -errno;
}
while (fgets(buf, sizeof(buf), f)) {
p = buf;
while (isspace((int) *p))
p++;
if (!strncmp(p, "tid:", 4)) {
if (sscanf(p, "tid:%u", &tid) != 1) {
err = -EIO;
goto out;
}
if (tid == t_tid)
find = 1;
else
find = 0;
} else if (!strncmp(p, "sid:", 4)) {
if (!find)
continue;
if (sscanf(p, "sid:%" SCNu64, &sid) != 1) {
err = -EIO;
goto out;
}
if (sid == t_sid)
find = 1;
else
find = 0;
} else if (!strncmp(p, "cid:", 4)) {
if (!find)
continue;
if (sscanf(p, "cid:%u", &cid) != 1) {
err = -EIO;
goto out;
}
if (cid == t_cid) {
err = 0;
goto out;
}
}
}
out:
fclose(f);
return err;
}
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);
conn->session->conn_cnt++;
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].exec_val,
conn->session_param[key_data_digest].exec_val);
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_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;
}
if (conn->rsp.data) {
free(conn->rsp.data);
conn->rsp.data = NULL;
}
}

435
iscsi-scst/usr/ctldev.c Normal file
View File

@@ -0,0 +1,435 @@
/*
* (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "iscsid.h"
#define CTL_DEVICE "/dev/iscsi-scst-ctl"
struct session_file_operations {
int (*target_op) (int fd, u32 tid, void *arg);
int (*session_op) (int fd, u32 tid, u64 sid, void *arg);
int (*connection_op) (int fd, u32 tid, u64 sid, u32 cid, void *arg);
};
static int ctrdev_open(void)
{
FILE *f;
char devname[256];
char buf[256];
int devn;
int ctlfd;
if (!(f = fopen("/proc/devices", "r"))) {
perror("Cannot open control path to the driver\n");
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, "iscsi-scst-ctl")) {
break;
}
devn = 0;
}
fclose(f);
if (!devn) {
printf
("cannot find iscsictl in /proc/devices - "
"make sure the module is loaded\n");
return -1;
}
unlink(CTL_DEVICE);
if (mknod(CTL_DEVICE, (S_IFCHR | 0600), (devn << 8))) {
printf("cannot create %s %d\n", CTL_DEVICE, errno);
return -1;
}
ctlfd = open(CTL_DEVICE, O_RDWR);
if (ctlfd < 0) {
printf("cannot open %s %d\n", CTL_DEVICE, errno);
return -1;
}
return ctlfd;
}
static int iscsi_target_create(u32 *tid, char *name)
{
int err;
struct target_info info;
memset(&info, 0, sizeof(info));
memcpy(info.name, name, sizeof(info.name) - 1);
info.tid = *tid;
if ((err = ioctl(ctrl_fd, ADD_TARGET, &info)) < 0)
log_warning("can't create a target %d %u\n", errno, info.tid);
*tid = info.tid;
return err;
}
static int iscsi_target_destroy(u32 tid)
{
struct target_info info;
memset(&info, 0, sizeof(info));
info.tid = tid;
return ioctl(ctrl_fd, DEL_TARGET, &info);
}
static int iscsi_conn_destroy(u32 tid, u64 sid, u32 cid)
{
int err;
struct conn_info info;
info.tid = tid;
info.sid = sid;
info.cid = cid;
if ((err = ioctl(ctrl_fd, DEL_CONN, &info)) < 0)
err = errno;
return err;
}
/**
** ToDo: the below code is a brain damage, rewrite it.
**/
static int __conn_close(int fd, u32 tid, u64 sid, u32 cid, void *arg)
{
return ki->conn_destroy(tid, sid, cid);
}
static int __target_del(int fd, u32 tid, void *arg)
{
return ki->target_destroy(tid);
}
static int proc_session_parse(int fd, struct session_file_operations *ops,
int op_tid, void *arg)
{
FILE *f;
char buf[8192], *p;
u32 tid, cid;
u64 sid;
int err, skip, done = 0;
if ((f = fopen(PROC_SESSION, "r")) == NULL) {
fprintf(stderr, "Can't open %s\n", PROC_SESSION);
return errno;
}
skip = 0;
while (fgets(buf, sizeof(buf), f)) {
p = buf;
while (isspace((int) *p))
p++;
if (!strncmp(p, "tid:", 4)) {
if (sscanf(p, "tid:%u", &tid) != 1)
break;
if (op_tid != -1) {
if (tid == op_tid)
skip = 0;
else {
skip = 1;
if (done)
break;
else
continue;
}
}
if (ops->target_op)
if ((err = ops->target_op(fd, tid, arg)) < 0)
goto out;
continue;
}
if (skip)
continue;
if (!strncmp(p, "sid:", 4)) {
if (sscanf(p, "sid:%" SCNu64, &sid) != 1) {
log_error("Unknown %s sid syntax: %s\n", PROC_SESSION, p);
break;
}
if (ops->session_op)
if ((err = ops->session_op(fd, tid, sid, arg)) < 0)
goto out;
} else if (!strncmp(p, "cid:", 4)) {
if (sscanf(p, "cid:%u", &cid) != 1) {
log_error("Unknown %s cid syntax: %s\n", PROC_SESSION, p);
break;
}
if (ops->connection_op)
if ((err = ops->connection_op(fd, tid, sid, cid, arg)) < 0)
goto out;
} else
log_error("Unknown %s string: %s\n", PROC_SESSION, p);
done = 1;
}
err = 0;
out:
fclose(f);
return err;
}
static int session_retry (int fd, u32 tid, u64 sid, void *arg)
{
return -EAGAIN;
}
static int conn_retry (int fd, u32 tid, u64 sid, u32 cid, void *arg)
{
return -EAGAIN;
}
static int __sess_cleanup(int fd, u32 tid, void *arg)
{
wait_4_iscsi_event(100);
return 0;
}
static struct session_file_operations conn_close_ops = {
.connection_op = __conn_close,
};
static struct session_file_operations conn_sess_cleanup_ops = {
.target_op = __sess_cleanup,
};
static struct session_file_operations shutdown_wait_ops = {
.session_op = session_retry,
.connection_op = conn_retry,
};
static struct session_file_operations target_del_ops = {
.target_op = __target_del,
};
int server_stop(void)
{
conn_blocked = 1;
proc_session_parse(ctrl_fd, &conn_close_ops, -1, NULL);
while (proc_session_parse(ctrl_fd, &shutdown_wait_ops, -1, NULL) < 0)
sleep(1);
proc_session_parse(ctrl_fd, &target_del_ops, -1, NULL);
isns_exit();
return 0;
}
int target_destroy(u32 tid)
{
int err;
conn_blocked = 1;
proc_session_parse(ctrl_fd, &conn_close_ops, tid, NULL);
while (proc_session_parse(ctrl_fd, &shutdown_wait_ops, tid, NULL) < 0) {
sleep(1);
}
proc_session_parse(ctrl_fd, &conn_sess_cleanup_ops, tid, NULL);
err = proc_session_parse(ctrl_fd, &target_del_ops, tid, NULL);
conn_blocked = 0;
return err;
}
struct session_conn_close_arg {
u64 sid;
};
static int session_conn_close(int fd, u32 tid, u64 sid, u32 cid, void *opaque)
{
struct session_conn_close_arg *arg = (struct session_conn_close_arg *) opaque;
int err;
if (arg->sid == sid)
err = ki->conn_destroy(tid, sid, cid);
return 0;
}
struct session_file_operations session_conns_close_ops = {
.connection_op = session_conn_close,
};
int session_conns_close(u32 tid, u64 sid)
{
int err;
struct session_conn_close_arg arg = {sid};
err = proc_session_parse(ctrl_fd, &session_conns_close_ops, tid, &arg);
return err;
}
static int iscsi_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param,
int local)
{
int err, i;
struct iscsi_param_info info;
memset(&info, 0, sizeof(info));
info.tid = tid;
info.sid = sid;
info.param_type = type;
if ((err = ioctl(ctrl_fd, ISCSI_PARAM_GET, &info)) < 0)
log_error("Can't get session param %d %d\n", info.tid, errno);
if (local) {
if (type == key_session)
for (i = 0; i < session_key_last; i++)
param[i].local_val = info.session_param[i];
else
for (i = 0; i < target_key_last; i++)
param[i].local_val = info.target_param[i];
} else {
if (type == key_session)
for (i = 0; i < session_key_last; i++)
param[i].exec_val = info.session_param[i];
else
for (i = 0; i < target_key_last; i++)
param[i].exec_val = info.target_param[i];
}
return err;
}
static int iscsi_param_set(u32 tid, u64 sid, int type, u32 partial,
struct iscsi_param *param, int local)
{
int i, err;
struct iscsi_param_info info;
memset(&info, 0, sizeof(info));
info.tid = tid;
info.sid = sid;
info.param_type = type;
info.partial = partial;
if (local) {
if (info.param_type == key_session)
for (i = 0; i < session_key_last; i++)
info.session_param[i] = param[i].local_val;
else
for (i = 0; i < target_key_last; i++)
info.target_param[i] = param[i].local_val;
} else {
if (info.param_type == key_session)
for (i = 0; i < session_key_last; i++)
info.session_param[i] = param[i].exec_val;
else
for (i = 0; i < target_key_last; i++)
info.target_param[i] = param[i].exec_val;
}
if ((err = ioctl(ctrl_fd, ISCSI_PARAM_SET, &info)) < 0)
fprintf(stderr, "%d %d %u " "%" PRIu64 " %d %u\n",
err, errno, tid, sid, type, partial);
return err;
}
static int iscsi_session_create(u32 tid, u64 sid, u32 exp_cmd_sn,
u32 max_cmd_sn, char *name, char *user)
{
struct session_info info;
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);
strncpy(info.user_name, user, sizeof(info.user_name) - 1);
return ioctl(ctrl_fd, ADD_SESSION, &info);
}
static int iscsi_session_destroy(u32 tid, u64 sid)
{
struct session_info info;
int res;
memset(&info, 0, sizeof(info));
info.tid = tid;
info.sid = sid;
do {
res = ioctl(ctrl_fd, DEL_SESSION, &info);
} while (res < 0 && errno == EINTR);
return res;
}
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;
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;
return ioctl(ctrl_fd, ADD_CONN, &info);
}
struct iscsi_kernel_interface ioctl_ki = {
.ctldev_open = ctrdev_open,
.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,
.conn_create = iscsi_conn_create,
.conn_destroy = iscsi_conn_destroy,
};
struct iscsi_kernel_interface *ki = &ioctl_ki;

132
iscsi-scst/usr/event.c Normal file
View File

@@ -0,0 +1,132 @@
/*
* Event notification code.
* (C) 2005 FUJITA Tomonori <tomof@acm.org>
* This code is licenced under the GPL.
*
* Some functions are based on open-iscsi code
* written by Dmitry Yusupov, Alex Aizman.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#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;
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 iscsi_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 %#" PRIx64 " 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))) {
log_warning("session %#" PRIx64 " not found?", event.sid);
goto retry;
}
if (!--session->conn_cnt)
session_remove(session);
break;
default:
log_warning("%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_ISCSI_SCST);
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;
}

559
iscsi-scst/usr/iscsi_adm.c Normal file
View File

@@ -0,0 +1,559 @@
/*
* iscsi_adm - manage iSCSI Enterprise Target software.
*
* (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include "iscsid.h"
#include "iscsi_adm.h"
#define SET_TARGET (1 << 0)
#define SET_SESSION (1 << 1)
#define SET_CONNECTION (1 << 2)
#define SET_USER (1 << 4)
enum iscsi_adm_op {
OP_NEW,
OP_DELETE,
OP_UPDATE,
OP_SHOW,
};
static char program_name[] = "iscsi-scst-adm";
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'},
{"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 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 SCST iSCSI target parameters of specific\n\
target with [id]. You can use parameters in iscsi-scstd.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\
--version display version and exit\n\
--help display this help and exit\n\
\n\
Report bugs to <iscsitarget-devel@sourceforge.net>.\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 iscsid_request_send(int fd, struct iscsi_adm_req *req)
{
int err, ret;
do {
ret = write(fd, req, sizeof(*req));
} while (ret < 0 && errno == EINTR);
if (ret != 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 iscsid_response_recv(int fd, struct iscsi_adm_req *req)
{
int err, ret;
struct iovec iov[2];
struct iscsi_adm_rsp rsp;
iov[0].iov_base = req;
iov[0].iov_len = sizeof(*req);
iov[1].iov_base = &rsp;
iov[1].iov_len = sizeof(rsp);
do {
ret = readv(fd, iov, 2);
} while (ret < 0 && errno == EINTR);
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;
return err;
}
static int iscsid_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, ISCSI_ADM_NAMESPACE, strlen(ISCSI_ADM_NAMESPACE));
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)))
fd = -errno;
return fd;
}
static int iscsid_request(struct iscsi_adm_req *req)
{
int fd = -1, err = -EIO;
if ((fd = iscsid_connect()) < 0) {
err = fd;
goto out;
}
if ((err = iscsid_request_send(fd, req)) < 0)
goto out;
err = iscsid_response_recv(fd, req);
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].local_val, p);
printf("%s\n", buf);
}
}
static int parse_trgt_params(struct msg_trgt *msg, char *params)
{
char *p, *q;
while ((p = strsep(&params, ",")) != 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].local_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].local_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 iscsi_adm_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:
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 = iscsid_request(&req);
if (!err && req.rcmnd == C_TRGT_SHOW)
show_iscsi_param(key_target, req.u.trgt.target_param);
out:
return err;
}
static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params)
{
int err = -EINVAL;
struct iscsi_adm_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 = iscsid_request(&req);
if (!err)
show_iscsi_param(key_session, req.u.trgt.session_param);
break;
}
out:
return err;
}
static int user_handle(int op, u32 set, u32 tid, char *params)
{
int err = -EINVAL;
char *p, *q, *user = NULL, *pass = NULL;
struct iscsi_adm_req req;
if (set & ~(SET_TARGET | SET_USER))
goto out;
memset(&req, 0, sizeof(req));
req.tid = tid;
switch (op) {
case OP_NEW:
req.rcmnd = C_ACCT_NEW;
break;
case OP_DELETE:
req.rcmnd = C_ACCT_DEL;
break;
case OP_UPDATE:
case OP_SHOW:
fprintf(stderr, "Unsupported.\n");
goto out;
}
while ((p = strsep(&params, ",")) != 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 user %s\n", q);
user = q;
req.u.acnt.auth_dir = AUTH_DIR_INCOMING;
} else if (!strcasecmp(p, "OutgoingUser")) {
if (user)
fprintf(stderr, "Already specified user %s\n", q);
user = q;
req.u.acnt.auth_dir = AUTH_DIR_OUTGOING;
} else if (!strcasecmp(p, "Password")) {
if (pass)
fprintf(stderr, "Already specified pass %s\n", q);
pass = q;
} else {
fprintf(stderr, "Unknown parameter %p\n", q);
goto out;
}
}
if ((op == OP_NEW && ((user && !pass) || (!user && pass) || (!user && !pass))) ||
(op == OP_DELETE && ((!user && pass) || (!user && !pass)))) {
fprintf(stderr,
"You need to specify a user and its password %s %s\n", pass, user);
goto out;
}
strncpy(req.u.acnt.user, user, sizeof(req.u.acnt.user) - 1);
if (pass)
strncpy(req.u.acnt.pass, pass, sizeof(req.u.acnt.pass) - 1);
err = iscsid_request(&req);
out:
return err;
}
static int conn_handle(int op, u32 set, u32 tid, u64 sid, u32 cid, char *params)
{
int err = -EINVAL;
struct iscsi_adm_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 = iscsid_request(&req);
out:
return err;
}
static int sys_handle(int op, u32 set, char *params)
{
int err = -EINVAL;
struct iscsi_adm_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 = iscsid_request(&req);
return err;
}
int main(int argc, char **argv)
{
int ch, longindex;
int err = -EINVAL, op = -1;
u32 tid = 0, cid = 0, set = 0;
u64 sid = 0;
char *params = NULL;
while ((ch = getopt_long(argc, argv, "o:t:s:c:l: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 'p':
params = optarg;
break;
case 'u':
set |= SET_USER;
break;
case 'v':
printf("%s version %s\n", program_name, ISCSI_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_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;
}

View File

@@ -0,0 +1,68 @@
#ifndef _ISCSI_ADM_H
#define _ISCSI_ADM_H
#define ISCSI_ADM_NAMESPACE "ISCSI_SCST_ADM"
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;
char user[ISCSI_NAME_LEN];
char pass[ISCSI_NAME_LEN];
};
enum iscsi_adm_cmnd {
C_TRGT_NEW,
C_TRGT_DEL,
C_TRGT_UPDATE,
C_TRGT_SHOW,
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_ACCT_NEW,
C_ACCT_DEL,
C_ACCT_UPDATE,
C_ACCT_SHOW,
C_SYS_NEW,
C_SYS_DEL,
C_SYS_UPDATE,
C_SYS_SHOW,
};
struct iscsi_adm_req {
enum iscsi_adm_cmnd rcmnd;
u32 tid;
u64 sid;
u32 cid;
u32 lun;
union {
struct msg_trgt trgt;
struct msg_acnt acnt;
} u;
};
struct iscsi_adm_rsp {
int err;
};
#endif

213
iscsi-scst/usr/iscsi_hdr.h Normal file
View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* 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_NOOP_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_NOOP_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_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;
#endif /* ISCSI_HDR_H */

View File

@@ -0,0 +1,585 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <netdb.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "iscsid.h"
#include "iscsi_adm.h"
#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,
};
static char* server_address;
uint16_t server_port = ISCSI_LISTEN_PORT;
static struct pollfd poll_array[POLL_MAX];
static struct connection *incoming[INCOMING_MAX];
static int incoming_cnt;
int ctrl_fd, ipc_fd, nl_fd;
int conn_blocked;
static char program_name[] = "iscsi-scstd";
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 comfigurable by command line options */
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 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 sock_set_keepalive(int sock, int timeout)
{
if (timeout) { /* timeout [s] */
int opt = 2;
if (setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &opt, sizeof(opt)))
log_warning("unable to set TCP_KEEPCNT on server socket (%s)!", strerror(errno));
if (setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &timeout, sizeof(timeout)))
log_warning("unable to set TCP_KEEPIDLE on server socket (%s)!", strerror(errno));
opt = 3;
if (setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &opt, sizeof(opt)))
log_warning("unable to set KEEPINTVL on server socket (%s)!", strerror(errno));
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));
}
}
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)!", 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;
}
sock_set_keepalive(sock, 50);
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)\n");
exit(1);
}
return;
}
if (conn_blocked) {
log_warning("A connection refused\n");
close(fd);
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);
}
static void event_conn(struct connection *conn, struct pollfd *pollfd)
{
int res, opt;
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 out;
}
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 out;
}
}
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,
pollfd->fd);
exit(1);
}
out:
return;
}
void wait_4_iscsi_event(int timeout)
{
int res;
do {
res = poll(&poll_array[POLL_NL], 1, timeout);
} while (res < 0 && errno == EINTR);
if (poll_array[POLL_NL].revents && res > 0)
handle_iscsi_events(nl_fd);
else {
log_error("%s: unexpected error %d %d\n", __FUNCTION__, res,
errno);
}
}
static void event_loop(int timeout)
{
int res, i;
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) {
log_error("%s:poll() %d", __FUNCTION__, errno);
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)
iscsi_adm_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++) {
struct connection *conn = incoming[i];
struct pollfd *pollfd = &poll_array[POLL_INCOMING + i];
if (!conn || !pollfd->revents)
continue;
pollfd->revents = 0;
event_conn(conn, pollfd);
if (conn->state == STATE_CLOSE) {
log_debug(0, "connection closed");
conn_free_pdu(conn);
conn_free(conn);
close(pollfd->fd);
pollfd->fd = -1;
incoming[i] = NULL;
incoming_cnt--;
}
}
}
}
int main(int argc, char **argv)
{
int ch, longindex, timeout = -1;
char *config = NULL;
uid_t uid = 0;
gid_t gid = 0;
char *isns = NULL;
int isns_ac = 0;
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, ISCSI_VERSION_STRING);
exit(0);
break;
case 'h':
usage(0);
break;
default:
usage(1);
break;
}
}
if ((nl_fd = nl_open()) < 0) {
perror("netlink fd\n");
exit(-1);
};
if ((ctrl_fd = ki->ctldev_open()) < 0) {
perror("ctldev fd\n");
exit(-1);
}
if ((ipc_fd = iscsi_adm_request_listen()) < 0) {
perror("ipc fd\n");
exit(-1);
}
log_init();
if (log_daemon) {
char buf[64];
pid_t pid;
int fd;
fd = open("/var/run/iscsi-scstd.pid", O_WRONLY|O_CREAT, 0644);
if (fd < 0) {
log_error("unable to create pid file");
exit(1);
}
pid = fork();
if (pid < 0) {
log_error("starting daemon failed");
exit(1);
} else if (pid)
exit(0);
chdir("/");
if (lockf(fd, F_TLOCK, 0) < 0) {
log_error("unable to lock pid file");
exit(1);
}
ftruncate(fd, 0);
sprintf(buf, "%d\n", getpid());
write(fd, buf, strlen(buf));
close(0);
open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
setsid();
}
cops->init(config, &isns, &isns_ac);
if (isns)
timeout = isns_init(isns, isns_ac);
cops->default_load(config);
if (gid && setgid(gid) < 0)
perror("setgid\n");
if (uid && setuid(uid) < 0)
perror("setuid\n");
event_loop(timeout);
return 0;
}

765
iscsi-scst/usr/iscsid.c Normal file
View File

@@ -0,0 +1,765 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "iscsid.h"
static struct iscsi_key login_keys[] = {
{"InitiatorName",},
{"InitiatorAlias",},
{"SessionType",},
{"TargetName",},
{NULL, 0, 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;
}
void text_key_add(struct connection *conn, char *key, char *value)
{
int keylen = strlen(key);
int valuelen = strlen(value);
int len = keylen + valuelen + 2;
char *buffer;
if (!conn->rsp.datasize) {
if (!conn->rsp_buffer) {
conn->rsp_buffer = malloc(INCOMING_BUFSIZE);
if (!conn->rsp_buffer) {
log_error("Failed to alloc send buffer");
return;
}
}
conn->rsp.data = conn->rsp_buffer;
}
if (conn->rwsize + len > INCOMING_BUFSIZE) {
log_warning("Dropping key (%s=%s)", key, value);
return;
}
buffer = conn->rsp_buffer;
buffer += conn->rsp.datasize;
conn->rsp.datasize += len;
strcpy(buffer, key);
buffer += keylen;
*buffer++ = '=';
strcpy(buffer, value);
}
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 text_scan_security(struct connection *conn)
{
struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs;
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) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_AUTH_FAILED;
conn->state = STATE_EXIT;
}
}
static void login_security_done(struct connection *conn)
{
int err;
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;
struct session *session;
if (!conn->tid)
return;
if ((session = session_find_name(conn->tid, conn->initiator, req->sid))) {
if (!req->sid.id.tsih) {
/* do session reinstatement */
session_conns_close(conn->tid, session->sid.id64);
session = NULL;
} else if (req->sid.id.tsih != session->sid.id.tsih) {
/* fail the login */
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_SESSION_NOT_FOUND;
conn->state = STATE_EXIT;
return;
} else if ((err = conn_test(conn)) == -ENOENT) {
/* do connection reinstatement */
}
/* add a new connection to the session */
conn->session = session;
} else {
if (req->sid.id.tsih) {
/* fail the login */
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_SESSION_NOT_FOUND;
conn->state = STATE_EXIT;
return;
}
/* instantiate a new session */
}
}
static void text_scan_login(struct connection *conn)
{
char *key, *value, *data;
int datasize, idx;
struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs;
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)) {
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 {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_INIT_ERR;
conn->state = STATE_EXIT;
goto out;
}
}
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 (iscsi_is_key_declarative(idx))
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].exec_val) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_INIT_ERR;
conn->state = STATE_EXIT;
log_warning("%s %u %u\n", key,
val, conn->session_param[idx].exec_val);
goto out;
}
break;
case KEY_STATE_DONE:
break;
}
conn->session_param[idx].state = KEY_STATE_DONE;
} else
text_key_add(conn, key, "NotUnderstood");
}
out:
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].exec_val != session_keys[i].rfc_def) {
switch (conn->state) {
case STATE_LOGIN_FULL:
case STATE_SECURITY_FULL:
if (iscsi_is_key_declarative(i)) {
p[i].state = KEY_STATE_DONE;
continue;
}
break;
case STATE_LOGIN:
if (iscsi_is_key_declarative(i))
continue;
memset(buf, 0, sizeof(buf));
param_val_to_str(session_keys, i, p[i].exec_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 (iscsi_is_key_declarative(i))
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;
struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs;
char *name, *alias, *session_type, *target_name;
conn->cid = be16_to_cpu(req->cid);
conn->sid.id64 = req->sid.id64;
if (!conn->sid.id64) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_MISSING_FIELDS;
conn->state = STATE_EXIT;
return;
}
name = text_key_find(conn, "InitiatorName");
if (!name) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_MISSING_FIELDS;
conn->state = STATE_EXIT;
return;
}
conn->initiator = strdup(name);
alias = text_key_find(conn, "InitiatorAlias");
session_type = text_key_find(conn, "SessionType");
target_name = text_key_find(conn, "TargetName");
conn->auth_method = -1;
conn->session_type = SESSION_NORMAL;
if (session_type) {
if (!strcmp(session_type, "Discovery"))
conn->session_type = SESSION_DISCOVERY;
else if (strcmp(session_type, "Normal")) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_INV_SESSION_TYPE;
conn->state = STATE_EXIT;
return;
}
}
if (conn->session_type == SESSION_NORMAL) {
if (!target_name) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_MISSING_FIELDS;
conn->state = STATE_EXIT;
return;
}
if (!(conn->tid = target_find_by_name(target_name)) ||
cops->initiator_access(conn->tid, conn->fd) ||
isns_scn_access(conn->tid, conn->fd, name)) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_TGT_NOT_FOUND;
conn->state = STATE_EXIT;
return;
}
if (ki->param_get(conn->tid, 0, key_session,
conn->session_param, 1)) {
rsp->status_class = ISCSI_STATUS_TARGET_ERROR;
rsp->status_detail = ISCSI_STATUS_SVC_UNAVAILABLE;
conn->state = STATE_EXIT;
}
conn->session_param[key_max_recv_data_length].exec_val =
conn->session_param[key_max_recv_data_length].local_val;
}
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)
{
switch (conn->session_type) {
case SESSION_NORMAL:
if (!conn->session)
session_create(conn);
conn->sid = conn->session->sid;
break;
case SESSION_DISCOVERY:
/* set a dummy tsih value */
conn->sid.id.tsih = 1;
break;
}
}
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)) {
//reject
}
rsp->opcode = ISCSI_OP_LOGIN_RSP;
rsp->max_version = ISCSI_VERSION;
rsp->active_version = ISCSI_VERSION;
rsp->itt = req->itt;
if (/*req->max_version < ISCSI_VERSION ||*/
req->min_version > ISCSI_VERSION) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_NO_VERSION;
conn->state = STATE_EXIT;
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;
login_security_done(conn);
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;
login_security_done(conn);
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);
}
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:
rsp->flags = 0;
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_INIT_ERR;
conn->state = STATE_EXIT;
return;
auth_err:
rsp->flags = 0;
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_AUTH_FAILED;
conn->state = STATE_EXIT;
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")) {
struct sockaddr_storage ss;
socklen_t slen, blen;
char *p, *addr, buf[NI_MAXHOST + 128];
int suppress_ip6 = 0;
if (value[0] == 0)
continue;
p = addr = buf + 1;
buf[0] = '[';
blen = sizeof(buf) - 1;
slen = sizeof(ss);
getpeername(conn->fd, (struct sockaddr *) &ss, &slen);
if (ss.ss_family == AF_INET)
suppress_ip6 = 1;
else {
getnameinfo((struct sockaddr *) &ss, slen, p,
blen, NULL, 0, NI_NUMERICHOST);
if (strstr(p, "::ffff:") == p)
suppress_ip6 = 1; // ipv4-mapped ipv6 ?
}
getsockname(conn->fd, (struct sockaddr *) &ss, &slen);
slen = sizeof(ss);
getnameinfo((struct sockaddr *) &ss, slen, p, blen,
NULL, 0, NI_NUMERICHOST);
if (ss.ss_family == AF_INET6 && suppress_ip6) {
if (strstr(p, "::ffff:") != p) {
log_error("%s, %s:%d.", __FILE__,
__FUNCTION__, __LINE__);
suppress_ip6 = 0;
} else
addr += 7;
}
p += strlen(p);
if (ss.ss_family == AF_INET6 && !suppress_ip6) {
*p++ = ']';
addr = buf;
}
sprintf(p, ":%d,1", server_port);
target_list_build(conn, addr,
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);
if (be32_to_cpu(req->ttt) != 0xffffffff) {
/* reject */;
}
rsp->opcode = ISCSI_OP_TEXT_RSP;
rsp->itt = req->itt;
//rsp->ttt = rsp->ttt;
rsp->ttt = 0xffffffff;
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);
text_scan_text(conn);
if (req->flags & ISCSI_FLG_FINAL)
rsp->flags = ISCSI_FLG_FINAL;
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)
{
int res = 1;
switch (conn->req.bhs.opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN_CMD:
//if conn->state == STATE_FULL -> reject
cmnd_exec_login(conn);
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);
break;
case ISCSI_OP_TEXT_CMD:
//if conn->state != STATE_FULL -> reject
cmnd_exec_text(conn);
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);
break;
case ISCSI_OP_LOGOUT_CMD:
//if conn->state != STATE_FULL -> reject
cmnd_exec_logout(conn);
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);
break;
default:
//reject
res = 0;
break;
}
return res;
}
void cmnd_finish(struct connection *conn)
{
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;
}
}

236
iscsi-scst/usr/iscsid.h Normal file
View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#ifndef ISCSID_H
#define ISCSID_H
#include <search.h>
#include <sys/types.h>
#include "types.h"
#include "iscsi_hdr.h"
#include "iscsi_u.h"
#include "param.h"
#include "config.h"
#include "misc.h"
#define PROC_SESSION "/proc/scsi_tgt/iscsi/session"
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;
char *user;
union iscsi_sid sid;
u16 cid;
u16 pad;
int session_type;
int auth_method;
u32 stat_sn;
u32 exp_stat_sn;
u32 cmd_sn;
u32 exp_cmd_sn;
u32 max_cmd_sn;
struct PDU req;
void *req_buffer;
struct PDU rsp;
void *rsp_buffer;
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
struct target {
struct qelem tlist;
struct qelem sessions_list;
u32 tid;
char name[ISCSI_NAME_LEN];
char *alias;
int max_nr_sessions;
int nr_sessions;
struct qelem isns_head;
};
extern struct config_operations plain_ops;
extern int ctrl_fd;
extern int conn_blocked;
/* 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);
/* iscsi_scstd.c */
extern uint16_t server_port;
extern void isns_set_fd(int isns, int scn_listen, int scn);
extern void wait_4_iscsi_event(int timeout);
/* 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 void session_create(struct connection *conn);
extern void session_remove(struct session *session);
/* target.c */
extern struct qelem targets_list;
extern int target_add(u32 *, char *);
extern int target_del(u32);
extern u32 target_find_by_name(const char *name);
struct target * target_find_by_id(u32);
extern void target_list_build(struct connection *, char *, char *);
/* message.c */
extern int iscsi_adm_request_listen(void);
extern int iscsi_adm_request_handle(int accept_fd);
/* ctldev.c */
struct iscsi_kernel_interface {
int (*ctldev_open) (void);
int (*param_get) (u32, u64, int, struct iscsi_param *, int);
int (*param_set) (u32, u64, int, u32, struct iscsi_param *, int);
int (*target_create) (u32 *, char *);
int (*target_destroy) (u32);
int (*session_create) (u32, u64, u32, u32, char *, char *);
int (*session_destroy) (u32, u64);
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;
/* the following functions should be killed */
extern int session_conns_close(u32 tid, u64 sid);
extern int server_stop(void);
extern int target_destroy(u32 tid);
/* 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_access(u32 tid, int fd, char *name);
extern int isns_target_register(char *name);
extern int isns_target_deregister(char *name);
extern void isns_exit(void);
#endif /* ISCSID_H */

960
iscsi-scst/usr/isns.c Normal file
View File

@@ -0,0 +1,960 @@
/*
* iSNS functions
*
* Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
*
* 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 <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#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]; /* SCST iSCSI supports only one portal */
static struct sockaddr_storage ss;
int isns_scn_access(uint32_t tid, int fd, char *name)
{
struct isns_initiator *ini;
struct target *target = target_find_by_id(tid);
if (!use_isns || !use_isns_ac)
return 0;
if (!target)
return -EPERM;
list_for_each_entry(ini, &target->isns_head, ilist) {
if (!strcmp(ini->name, name))
return 0;
}
return -EPERM;
}
static int isns_get_ip(int fd)
{
int err, i;
uint32_t addr;
struct sockaddr_storage lss;
socklen_t slen = sizeof(lss);
err = getsockname(fd, (struct sockaddr *) &lss, &slen);
if (err) {
log_error("getsockname error %s!", gai_strerror(err));
return err;
}
err = getnameinfo((struct sockaddr *) &lss, sizeof(lss),
eid, sizeof(eid), NULL, 0, 0);
if (err) {
log_error("getaddrinfo error %s!", gai_strerror(err));
return err;
}
switch (lss.ss_family) {
case AF_INET:
addr = (((struct sockaddr_in *) &lss)->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] = ((struct sockaddr_in6 *) &lss)->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), name);
length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), 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), target->name);
length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME,
strlen(target->name), 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;
scn_flags = htonl(set_scn_flag(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), 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), 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), target->name);
length += isns_tlv_set(&tlv, 0, 0, 0);
length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
strlen(eid), 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(ISCSI_LISTEN_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), target->name);
length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
strlen(eid), eid);
length += isns_tlv_set(&tlv, 0, 0, 0);
length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
strlen(eid), 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), 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);
}
}
static struct target *target_lookup_by_name(char *name)
{
uint32_t tid;
tid = target_find_by_name(name);
if (!tid)
return NULL;
return target_find_by_id(tid);
}
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_lookup_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), name);
length += isns_tlv_set(&tlv, 0, 0, 0);
if (last)
length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
strlen(eid), eid);
else
length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME,
strlen(name), 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 function, length, flags, transaction, sequence;
char *name = NULL;
get_hdr_param(hdr, function, length, flags, transaction, sequence);
while (length) {
uint32_t vlen = ntohl(tlv->length);
switch (ntohl(tlv->tag)) {
case ISNS_ATTR_ISCSI_NAME:
log_error("scn name: %u, %s", vlen, (char *) tlv->value);
if (!name)
name = (char *) tlv->value;
break;
case ISNS_ATTR_TIMESTAMP:
/* log_error("%u : %u : %" PRIx64, ntohl(tlv->tag), vlen, */
/* *((uint64_t *) tlv->value)); */
break;
case ISNS_ATTR_ISCSI_SCN_BITMAP:
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 function, length, flags, transaction, sequence;
uint32_t status = (uint32_t) (*hdr->pdu);
struct isns_qry_mgmt *mgmt, *n;
struct target *target;
struct isns_initiator *ini;
char *name = NULL;
get_hdr_param(hdr, function, length, flags, transaction, sequence);
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_lookup_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);
length -= 4;
while (length) {
uint32_t vlen = ntohl(tlv->length);
switch (ntohl(tlv->tag)) {
case ISNS_ATTR_ISCSI_NAME:
name = (char *) tlv->value;
break;
case ISNS_ATTR_ISCSI_NODE_TYPE:
if (ntohl(*(tlv->value)) == ISNS_NODE_INITIATOR && name) {
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), 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;
uint32_t result;
uint16_t function, length, flags, transaction, sequence;
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;
}
get_hdr_param(hdr, function, length, flags, transaction, sequence);
result = ntohl((uint32_t) hdr->pdu[0]);
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), 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, length, flags, transaction, sequence;
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;
}
get_hdr_param(hdr, function, length, flags, transaction, sequence);
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;
struct sockaddr_storage lss;
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(lss);
err = getsockname(fd, (struct sockaddr *) &lss, &slen);
if (err) {
log_error("%s %d: %s\n", __FUNCTION__, __LINE__, strerror(errno));
goto out;
}
/* protocol independent way ? */
if (lss.ss_family == AF_INET6)
scn_listen_port = ntohs(((struct sockaddr_in6 *) &lss)->sin6_port);
else
scn_listen_port = ntohs(((struct sockaddr_in *) &lss)->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(ss));
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);
}

200
iscsi-scst/usr/isns_proto.h Normal file
View File

@@ -0,0 +1,200 @@
/*
* iSNS protocol data types
*
* Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
*
* 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

127
iscsi-scst/usr/log.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/time.h>
#include "iscsid.h"
int log_daemon = 1;
int log_level = 0;
void log_init(void)
{
if (log_daemon)
openlog("iscsi-scstd", 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);
}
}
static void __dump_line(int level, unsigned char *buf, int *cp)
{
char line[16*3+5], *lp = line;
int i, cnt;
cnt = *cp;
if (!cnt)
return;
for (i = 0; i < 16; 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] = ' ';
}
log_debug(level, "%s %.16s |", line, buf);
*cp = 0;
}
static void __dump_char(int level, unsigned char *buf, int *cp, int ch)
{
int cnt = (*cp)++;
buf[cnt] = ch;
if (cnt == 15)
__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[16];
int char_cnt = 0;
unsigned char *buf;
int i;
return;
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();
}

172
iscsi-scst/usr/message.c Normal file
View File

@@ -0,0 +1,172 @@
/*
* (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include "iscsid.h"
#include "iscsi_adm.h"
int iscsi_adm_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, ISCSI_ADM_NAMESPACE,
strlen(ISCSI_ADM_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 iscsi_adm_request_exec(struct iscsi_adm_req *req, struct iscsi_adm_rsp *rsp)
{
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, 0);
break;
case C_SESS_NEW:
case C_SESS_DEL:
case C_SESS_UPDATE:
break;
case C_SESS_SHOW:
err = ki->param_get(req->tid, req->sid, key_session,
req->u.trgt.session_param, 0);
break;
case C_CONN_NEW:
case C_CONN_DEL:
conn_blocked = 1;
err = ki->conn_destroy(req->tid, req->sid, req->cid);
sleep(1);
conn_blocked = 0;
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.user,
req->u.acnt.pass);
break;
case C_ACCT_DEL:
err = cops->account_del(req->tid, req->u.acnt.auth_dir, req->u.acnt.user);
break;
case C_ACCT_UPDATE:
case C_ACCT_SHOW:
break;
case C_SYS_NEW:
break;
case C_SYS_DEL:
err = server_stop();
break;
case C_SYS_UPDATE:
case C_SYS_SHOW:
break;
default:
break;
}
out:
rsp->err = err;
}
int iscsi_adm_request_handle(int accept_fd)
{
struct sockaddr addr;
struct ucred cred;
int fd, err;
socklen_t len;
struct iscsi_adm_req req;
struct iscsi_adm_rsp rsp;
struct iovec iov[2];
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;
}
iscsi_adm_request_exec(&req, &rsp);
send:
iov[0].iov_base = &req;
iov[0].iov_len = sizeof(req);
iov[1].iov_base = &rsp;
iov[1].iov_len = sizeof(rsp);
err = writev(fd, iov, 2);
out:
if (fd > 0)
close(fd);
return err;
}

62
iscsi-scst/usr/misc.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* 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 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))
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 26
#endif
#endif

304
iscsi-scst/usr/param.c Normal file
View File

@@ -0,0 +1,304 @@
/*
* (C) 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#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++) {
if (i == key_max_recv_data_length)
params[i].exec_val = keys[i].local_def;
else
params[i].exec_val = keys[i].rfc_def;
params[i].local_val = keys[i].local_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 int or_set_val(struct iscsi_param *param, int idx, unsigned int *val)
{
*val |= param[idx].local_val;
param[idx].exec_val = *val;
return 0;
}
static int and_set_val(struct iscsi_param *param, int idx, unsigned int *val)
{
*val &= param[idx].local_val;
param[idx].exec_val = *val;
return 0;
}
static int num_check_val(struct iscsi_key *key, unsigned int *val)
{
int err = 0;
if (*val < key->min) {
*val = key->min;
err = -EINVAL;
} else if (*val > key->max) {
*val = key->max;
err = -EINVAL;
}
return err;
}
static int minimum_set_val(struct iscsi_param *param, int idx, unsigned int *val)
{
if (*val > param[idx].local_val)
*val = param[idx].local_val;
param[idx].exec_val = *val;
return 0;
}
static int maximum_set_val(struct iscsi_param *param, int idx, unsigned int *val)
{
if (param[idx].local_val > *val)
*val = param[idx].local_val;
param[idx].exec_val = *val;
return 0;
}
static int digest_val_to_str(unsigned int val, char *str)
{
int err = 0;
if (val & DIGEST_CRC32C)
strcpy(str, "CRC32C");
else if (val & DIGEST_NONE)
strcpy(str, "None");
else
err = -EINVAL;
return err;
}
static int digest_str_to_val(char *str, unsigned int *val)
{
int err = 0;
char *p, *q;
p = str;
*val = DIGEST_NONE;
do {
if (!strncmp(p, "None", strlen("None")))
*val |= DIGEST_NONE;
else if (!strncmp(p, "CRC32C", strlen("CRC32C")))
*val |= DIGEST_CRC32C;
else {
err = -EINVAL;
break;
}
if ((q = strchr(p, ',')))
p = q + 1;
} while (q);
return err;
}
static int digest_set_val(struct iscsi_param *param, int idx, unsigned int *val)
{
if (*val & DIGEST_CRC32C && param[idx].local_val & DIGEST_CRC32C)
*val = DIGEST_CRC32C;
else
*val = DIGEST_NONE;
param[idx].exec_val = *val;
return 0;
}
static int marker_val_to_str(unsigned int val, char *str)
{
if (val == 0)
strcpy(str, "Irrelevant");
else
strcpy(str, "Reject");
return 0;
}
static int 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].exec_val = *val;
return 0;
}
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;
}
int param_set_val(struct iscsi_key *keys, struct iscsi_param *param,
int idx, unsigned int *val)
{
if (keys[idx].ops->set_val)
return keys[idx].ops->set_val(param, idx, val);
else
return 0;
}
static struct iscsi_key_ops minimum_ops = {
.val_to_str = range_val_to_str,
.str_to_val = range_str_to_val,
.check_val = num_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 = num_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_NR_##x,DEFAULT_NR_##x,MIN_NR_##x,MAX_NR_##x
struct iscsi_key target_keys[] = {
{"QueuedCommands", SET_KEY_VALUES(QUEUED_CMNDS), &minimum_ops},
{NULL,},
};
struct iscsi_key session_keys[] = {
{"InitialR2T", 1, 0, 0, 1, &or_ops},
{"ImmediateData", 1, 1, 0, 1, &and_ops},
{"MaxConnections", 1, 1, 1, 1, &minimum_ops},
{"MaxRecvDataSegmentLength", 8192, MAX_DATA_SEG_LEN, 512, MAX_DATA_SEG_LEN, &minimum_ops},
{"MaxXmitDataSegmentLength", 8192, MAX_DATA_SEG_LEN, 512, MAX_DATA_SEG_LEN, &minimum_ops},
{"MaxBurstLength", 262144, MAX_DATA_SEG_LEN, 512, MAX_DATA_SEG_LEN, &minimum_ops},
{"FirstBurstLength", 65536, MAX_DATA_SEG_LEN, 512, MAX_DATA_SEG_LEN, &minimum_ops},
{"DefaultTime2Wait", 2, 2, 0, 3600, &maximum_ops},
{"DefaultTime2Retain", 20, 20, 0, 3600, &minimum_ops},
{"MaxOutstandingR2T", 1, 20, 1, 65535, &minimum_ops},
{"DataPDUInOrder", 1, 0, 0, 1, &or_ops},
{"DataSequenceInOrder", 1, 0, 0, 1, &or_ops},
{"ErrorRecoveryLevel", 0, 0, 0, 0, &minimum_ops},
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, &digest_ops},
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, &digest_ops},
{"OFMarker", 0, 0, 0, 0, &and_ops},
{"IFMarker", 0, 0, 0, 0, &and_ops},
{"OFMarkInt", 2048, 2048, 1, 65535, &marker_ops},
{"IFMarkInt", 2048, 2048, 1, 65535, &marker_ops},
{NULL,},
};

44
iscsi-scst/usr/param.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* (C) 2005 FUJITA Tomonori <tomof@acm.org>
*
* This code is licenced under the GPL.
*/
#ifndef PARAMS_H
#define PARAMS_H
struct iscsi_key;
struct iscsi_param {
int state;
unsigned int exec_val;
unsigned int local_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 *);
int (*set_val)(struct iscsi_param *, int, unsigned int *);
};
struct iscsi_key {
char *name;
unsigned int rfc_def;
unsigned int local_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 int param_set_val(struct iscsi_key *, struct iscsi_param *, int, unsigned int *);
#endif

609
iscsi-scst/usr/plain.c Normal file
View File

@@ -0,0 +1,609 @@
/*
* Plain file-based configuration file code.
*
* (C) 2005 FUJITA Tomonori <tomof@acm.org>
* This code is licenced under the GPL.
*/
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "iscsid.h"
#define BUFSIZE 4096
#define CONFIG_FILE "/etc/iscsi-scstd.conf"
#define ACCT_CONFIG_FILE CONFIG_FILE
/*
* Account configuration code
*/
struct user {
struct qelem ulist;
u32 tid;
char *name;
char *password;
};
/* 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;
}
static int plain_account_init(char *filename)
{
FILE *fp;
char buf[BUFSIZE], *p, *q;
u32 tid;
int idx;
if (!(fp = fopen(filename, "r")))
return -EIO;
tid = 0;
while (fgets(buf, sizeof(buf), 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;
tid = target_find_by_name(p);
} 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 (cops->account_add(tid, idx, name, pass) < 0)
fprintf(stderr, "%s %s\n", name, pass);
}
}
fclose(fp);
return 0;
}
/* 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 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; */
}
/* Check for minimum RFC defined value */
if (strlen(pass) < 12) {
log_error("Secret for user %s is too short. At least 12 bytes "
"are required\n", name);
return -EINVAL;
}
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 && !list_empty(list)) {
struct user *old;
log_warning("Only one outgoing %s account is supported."
" Replacing the old one.\n",
tid ? "target" : "discovery");
old = (struct user *) list->q_forw;
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;
mbit = strtoul(buf, NULL, 0);
if (mbit < 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(int fd, char *str)
{
struct sockaddr_storage from;
struct addrinfo hints, *res;
socklen_t len;
char *p, *q;
int err = 0;
len = sizeof(from);
if (getpeername(fd, (struct sockaddr *) &from, &len) < 0)
return 0;
while ((p = strsep(&str, ","))) {
while (isspace(*p))
p++;
if (!strcmp(p, "ALL"))
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)
return 0;
if (q)
err = netmask_match(res->ai_addr,
(struct sockaddr *) &from, q);
else
err = address_match(res->ai_addr,
(struct sockaddr *) &from);
freeaddrinfo(res);
if (err)
break;
}
return err;
}
static int initiator_match(u32 tid, int fd, char *filename)
{
int err = 0;
FILE *fp;
char buf[BUFSIZE], *p;
if (!(fp = fopen(filename, "r")))
return err;
/*
* 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))) {
if (!p || *p == '#')
continue;
p = &buf[strlen(buf) - 1];
if (*p != '\n')
continue;
*p = '\0';
if (!(p = strchr(buf, ' ')))
continue;
*(p++) = '\0';
if (target_find_by_name(buf) != tid && strcmp(buf, "ALL"))
continue;
err = __initiator_match(fd, p);
break;
}
fclose(fp);
return err;
}
static int plain_initiator_access(u32 tid, int fd)
{
if (initiator_match(tid, fd, "/etc/initiators.deny") &&
!initiator_match(tid, fd, "/etc/initiators.allow"))
return -EPERM;
else
return 0;
}
/*
* Main configuration code
*/
static int __plain_target_create(u32 *tid, char *name, int update)
{
int err;
struct iscsi_param params[session_key_last];
if (target_find_by_name(name)) {
log_error("duplicated target %s", name);
return -EINVAL;
}
if ((err = target_add(tid, name)) < 0)
return err;
param_set_defaults(params, session_keys);
if ((err = ki->param_set(*tid, 0, key_session, 0, params, 1)) < 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_param_set(u32 tid, u64 sid, int type,
u32 partial, struct iscsi_param *param, int update)
{
int err;
if ((err = ki->param_set(tid, sid, type, partial, param, 1)) < 0)
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;
param[key].local_val = val;
return __plain_param_set(tid, sid, type, 1 << key, param, 0);
}
static int plain_main_init(char *filename)
{
FILE *config;
char buf[BUFSIZE];
char *p, *q;
int idx;
u32 tid, val;
if (!(config = fopen(filename, "r")))
return -errno;
tid = 0;
while (fgets(buf, BUFSIZE, config)) {
q = buf;
p = target_sep_string(&q);
if (!p || *p == '#')
continue;
if (!strcasecmp(p, "Target")) {
tid = 0;
if (!(p = target_sep_string(&q)))
continue;
if (__plain_target_create(&tid, p, 0))
log_debug(1, "creating target %s", p);
} else if (!strcasecmp(p, "Alias") && tid) {
;
} 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("Wrong value %u for parameter %s\n",
val, target_keys[idx].name);
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) {
log_warning("Wrong value %s for parameter %s\n",
str, session_keys[idx].name);
continue;
}
if (param_check_val(session_keys, idx, &val) < 0)
log_warning("Wrong value %u for parameter %s\n",
val, session_keys[idx].name);
iscsi_param_partial_set(tid, 0, key_session, idx, val);
} else if (param_index_by_name(p, user_keys) < 0)
log_warning("Unknown iscsi-scstd.conf param: %s\n", p);
}
fclose(config);
return 0;
}
static int plain_default_load(char *params)
{
int i, err;
for (i = 0; i < 1 << HASH_ORDER; i++) {
INIT_LIST_HEAD(&trgt_acct_in[i]);
INIT_LIST_HEAD(&trgt_acct_out[i]);
}
/* First, we must finish the main configuration. */
if ((err = plain_main_init(params ? params : CONFIG_FILE)))
return err;
if ((err = plain_account_init(ACCT_CONFIG_FILE)) < 0)
return err;
/* TODO: error handling */
return err;
}
static int plain_init(char *params, char **isns, int *isns_ac)
{
FILE *config;
char buf[BUFSIZE];
char *p, *q;
if (!(config = fopen(params ? : CONFIG_FILE, "r")))
return -errno;
while (fgets(buf, BUFSIZE, config)) {
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;
}
}
fclose(config);
return 0;
}
struct config_operations plain_ops = {
.init = plain_init,
.default_load = plain_default_load,
.target_add = plain_target_create,
.target_del = plain_target_destroy,
.param_set = plain_param_set,
.account_add = plain_account_add,
.account_del = plain_account_del,
.account_query = plain_account_query,
.initiator_access = plain_initiator_access,
};

173
iscsi-scst/usr/session.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <ctype.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#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;
}
static int session_test(u32 t_tid, u64 t_sid)
{
FILE *f;
char buf[8192], *p;
u32 tid;
u64 sid;
int err = -ENOENT, find = 0;
if ((f = fopen(PROC_SESSION, "r")) == NULL) {
fprintf(stderr, "Can't open %s\n", PROC_SESSION);
return -errno;
}
while (fgets(buf, sizeof(buf), f)) {
p = buf;
while (isspace((int) *p))
p++;
if (!strncmp(p, "tid:", 4)) {
if (sscanf(p, "tid:%u", &tid) != 1) {
err = -EIO;
goto out;
}
if (tid == t_tid)
find = 1;
else
find = 0;
} else if (!strncmp(p, "sid:", 4)) {
if (!find)
continue;
if (sscanf(p, "sid:%" SCNu64, &sid) != 1) {
err = -EIO;
goto out;
}
if (sid == t_sid) {
err = 0;
goto out;
}
}
}
out:
fclose(f);
return err;
}
void session_create(struct connection *conn)
{
struct session *session;
static u16 tsih = 1;
char *user;
if (!(session = session_alloc(conn->tid)))
return;
session->sid = conn->sid;
session->sid.id.tsih = tsih;
while (1) {
int err = session_test(conn->tid, session->sid.id64);
if (err == -ENOENT)
break;
else if (err < 0)
return;
session->sid.id.tsih++;
}
tsih = session->sid.id.tsih + 1;
conn->session = session;
conn->session->initiator = strdup(conn->initiator);
log_debug(1, "session_create: %#" PRIx64, session->sid.id64);
if (conn->user != NULL)
user = conn->user;
else
user = "";
ki->session_create(conn->tid, session->sid.id64, conn->exp_cmd_sn,
conn->max_cmd_sn, session->initiator, user);
ki->param_set(conn->tid, session->sid.id64, key_session, 0,
conn->session_param, 0);
}
void session_remove(struct session *session)
{
log_debug(1, "session_remove: %#" PRIx64, session->sid.id64);
if (!session->sid.id.tsih)
ki->session_destroy(session->target->tid, session->sid.id64);
if (session->target) {
remque(&session->slist);
/* session->target->nr_sessions--; */
}
free(session->initiator);
free(session);
}

133
iscsi-scst/usr/target.c Normal file
View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "iscsid.h"
struct qelem targets_list = LIST_HEAD_INIT(targets_list);
void target_list_build(struct connection *conn, char *addr, char *name)
{
struct target *target;
list_for_each_entry(target, &targets_list, tlist) {
if (name && strcmp(target->name, name))
continue;
if (cops->initiator_access(target->tid, conn->fd) ||
isns_scn_access(target->tid, conn->fd, conn->initiator))
continue;
text_key_add(conn, "TargetName", target->name);
text_key_add(conn, "TargetAddress", addr);
}
}
u32 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->tid;
}
return 0;
}
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)
{
int err;
struct target* target;
if (!(target = target_find_by_id(tid)))
return -ENOENT;
if (target->nr_sessions)
return -EBUSY;
if ((err = target_destroy(tid)) < 0)
return err;
remque(&target->tlist);
if (!list_empty(&target->sessions_list)) {
log_error("%s still have sessions %d\n", __FUNCTION__, tid);
exit(-1);
}
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);
if ((err = ki->target_create(tid, name)) < 0) {
log_warning("can't create a target %d %u\n", errno, *tid);
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);
return 0;
out:
free(target);
return err;
}

35
iscsi-scst/usr/types.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#ifndef TYPES_H
#define TYPES_H
#include <sys/types.h>
#include <byteswap.h>
#include <endian.h>
#include <stdint.h>
#include <inttypes.h>
#if __BYTE_ORDER == __BIG_ENDIAN
#define cpu_to_be16(x) (x)
#define be16_to_cpu(x) (x)
#define cpu_to_be32(x) (x)
#define be32_to_cpu(x) (x)
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#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)
#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 */