summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorxeb <xeb@mail.ru>2009-06-17 00:56:34 +0400
committerxeb <xeb@mail.ru>2009-06-17 00:56:34 +0400
commitdf2441c834cf341d9b969dacc2dd8dac07cd588e (patch)
treeca0c7d8bade520ac35f5cd5c34dec54b136bd491 /kernel
downloadaccel-ppp-xebd-df2441c834cf341d9b969dacc2dd8dac07cd588e.tar.gz
accel-ppp-xebd-df2441c834cf341d9b969dacc2dd8dac07cd588e.zip
initial import
Diffstat (limited to 'kernel')
-rw-r--r--kernel/driver/AUTHORS1
-rw-r--r--kernel/driver/COPYING340
-rw-r--r--kernel/driver/ChangeLog0
-rw-r--r--kernel/driver/Doxyfile275
-rw-r--r--kernel/driver/INSTALL167
-rw-r--r--kernel/driver/Makefile41
-rw-r--r--kernel/driver/NEWS0
-rw-r--r--kernel/driver/README0
-rw-r--r--kernel/driver/TODO0
-rwxr-xr-xkernel/driver/find_kernel_headers21
-rw-r--r--kernel/driver/if_pppox.h222
-rw-r--r--kernel/driver/ppp_generic_smp.c3042
-rw-r--r--kernel/driver/pptp.c1070
-rw-r--r--kernel/patch/patch-2.6.181110
-rw-r--r--kernel/patch/ppp-generic-smp-2.6.26.patch165
-rw-r--r--kernel/patch/ppp-generic-smp-2.6.28.patch163
16 files changed, 6617 insertions, 0 deletions
diff --git a/kernel/driver/AUTHORS b/kernel/driver/AUTHORS
new file mode 100644
index 0000000..b93df1d
--- /dev/null
+++ b/kernel/driver/AUTHORS
@@ -0,0 +1 @@
+Kozlov D. <xeb@mail.ru>
diff --git a/kernel/driver/COPYING b/kernel/driver/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/kernel/driver/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/kernel/driver/ChangeLog b/kernel/driver/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kernel/driver/ChangeLog
diff --git a/kernel/driver/Doxyfile b/kernel/driver/Doxyfile
new file mode 100644
index 0000000..1a7d258
--- /dev/null
+++ b/kernel/driver/Doxyfile
@@ -0,0 +1,275 @@
+# Doxyfile 1.4.1-KDevelop
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = pptp.kdevelop
+PROJECT_NUMBER = 0.1
+OUTPUT_DIRECTORY =
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+USE_WINDOWS_ENCODING = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH = /home/dima/Projects/bg2/sectrr/
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP = NO
+INHERIT_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+SUBGROUPING = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_DIRECTORIES = YES
+FILE_VERSION_FILTER =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = /home/dima/Projects/pptp/pptp
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.cs \
+ *.php \
+ *.php3 \
+ *.inc \
+ *.m \
+ *.mm \
+ *.dox \
+ *.C \
+ *.CC \
+ *.C++ \
+ *.II \
+ *.I++ \
+ *.H \
+ *.HH \
+ *.H++ \
+ *.CS \
+ *.PHP \
+ *.PHP3 \
+ *.M \
+ *.MM \
+ *.C \
+ *.H \
+ *.tlh \
+ *.diff \
+ *.patch \
+ *.moc \
+ *.xpm \
+ *.dox
+RECURSIVE = yes
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = yes
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE = pptp.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = NO
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 1000
+DOT_TRANSPARENT = NO
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/kernel/driver/INSTALL b/kernel/driver/INSTALL
new file mode 100644
index 0000000..02a4a07
--- /dev/null
+++ b/kernel/driver/INSTALL
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 4. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/kernel/driver/Makefile b/kernel/driver/Makefile
new file mode 100644
index 0000000..f36be86
--- /dev/null
+++ b/kernel/driver/Makefile
@@ -0,0 +1,41 @@
+MDIR = extra
+
+KDIR ?= $(shell sh find_kernel_headers)
+
+obj-m += pptp.o
+#obj-m += ppp_generic_smp.o
+
+CURRENT = $(shell uname -r)
+
+
+ifndef MAKING_MODULES
+all: kernel_headers
+ make -C $(KDIR) SUBDIRS=$(PWD) modules
+endif
+
+ifneq (,$(findstring 2.4.,$(CURRENT)))
+install:
+ @if test ! -d /lib/modules/$(CURRENT)/extra; then \
+ mkdir /lib/modules/$(CURRENT)/extra; \
+ fi; \
+ cp -v $(TARGET).o /lib/modules/$(CURRENT)/extra/$(TARGET).o && /sbin/depmod -a
+else
+install:
+ make -C $(KDIR) M=$(PWD) modules_install
+endif
+
+kernel_headers:
+ @if test -z "$(KDIR)"; then \
+ echo "kernel headers not found"; \
+ exit 1; \
+ else \
+ echo "using \"$(KDIR)\" kernel headers"; \
+ fi
+
+
+clean:
+ -rm -f *.o *.ko .*.cmd .*.flags *.mod.c
+
+ifneq (,$(findstring 2.4.,$(CURRENT)))
+include $(KDIR)/Rules.make
+endif
diff --git a/kernel/driver/NEWS b/kernel/driver/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kernel/driver/NEWS
diff --git a/kernel/driver/README b/kernel/driver/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kernel/driver/README
diff --git a/kernel/driver/TODO b/kernel/driver/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kernel/driver/TODO
diff --git a/kernel/driver/find_kernel_headers b/kernel/driver/find_kernel_headers
new file mode 100755
index 0000000..3d0e505
--- /dev/null
+++ b/kernel/driver/find_kernel_headers
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+if test -n "${KDIR}"; then
+ if test -f ${KDIR}/include/linux/version.h; then
+ echo ${KDIR}
+ exit 0
+ else
+ exit 1
+ fi
+else
+ if test -f /usr/src/linux/include/linux/version.h; then
+ echo /usr/src/linux
+ exit 0
+ elif test -f /lib/modules/`uname -r`/build/include/linux/version.h; then
+ echo /lib/modules/`uname -r`/build
+ exit 0
+ else
+ exit 1
+ fi
+fi
+ \ No newline at end of file
diff --git a/kernel/driver/if_pppox.h b/kernel/driver/if_pppox.h
new file mode 100644
index 0000000..bc05b53
--- /dev/null
+++ b/kernel/driver/if_pppox.h
@@ -0,0 +1,222 @@
+/***************************************************************************
+ * Linux PPP over X - Generic PPP transport layer sockets
+ * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516)
+ *
+ * This file supplies definitions required by the PPP over Ethernet driver
+ * (pppox.c). All version information wrt this file is located in pppox.c
+ *
+ * License:
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_IF_PPPOX_H
+#define __LINUX_IF_PPPOX_H
+
+
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#include <linux/version.h>
+
+#ifdef __KERNEL__
+#include <linux/in.h>
+#include <linux/if_ether.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/ppp_channel.h>
+#endif /* __KERNEL__ */
+
+/* For user-space programs to pick up these definitions
+ * which they wouldn't get otherwise without defining __KERNEL__
+ */
+#ifndef AF_PPPOX
+#define AF_PPPOX 24
+#define PF_PPPOX AF_PPPOX
+#endif /* !(AF_PPPOX) */
+
+/************************************************************************
+ * PPPoE addressing definition
+ */
+typedef __u16 sid_t;
+struct pppoe_addr{
+ sid_t sid; /* Session identifier */
+ unsigned char remote[ETH_ALEN]; /* Remote address */
+ char dev[IFNAMSIZ]; /* Local device to use */
+};
+
+struct pptp_addr{
+ __u16 call_id;
+ struct in_addr sin_addr;
+};
+/************************************************************************
+ * Protocols supported by AF_PPPOX
+ */
+#define PX_PROTO_OE 0 /* Currently just PPPoE */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
+#define PX_PROTO_PPTP 1
+#define PX_MAX_PROTO 2
+#else
+#define PX_PROTO_PPTP 2
+#define PX_MAX_PROTO 3
+#endif
+
+struct sockaddr_pppox {
+ sa_family_t sa_family; /* address family, AF_PPPOX */
+ unsigned int sa_protocol; /* protocol identifier */
+ union{
+ struct pppoe_addr pppoe;
+ struct pptp_addr pptp;
+ }sa_addr;
+}__attribute__ ((packed));
+
+
+/*********************************************************************
+ *
+ * ioctl interface for defining forwarding of connections
+ *
+ ********************************************************************/
+
+#define PPPOEIOCSFWD _IOW(0xB1 ,0, size_t)
+#define PPPOEIOCDFWD _IO(0xB1 ,1)
+/*#define PPPOEIOCGFWD _IOWR(0xB1,2, size_t)*/
+
+/* Codes to identify message types */
+#define PADI_CODE 0x09
+#define PADO_CODE 0x07
+#define PADR_CODE 0x19
+#define PADS_CODE 0x65
+#define PADT_CODE 0xa7
+struct pppoe_tag {
+ __u16 tag_type;
+ __u16 tag_len;
+ char tag_data[0];
+} __attribute ((packed));
+
+/* Tag identifiers */
+#define PTT_EOL __constant_htons(0x0000)
+#define PTT_SRV_NAME __constant_htons(0x0101)
+#define PTT_AC_NAME __constant_htons(0x0102)
+#define PTT_HOST_UNIQ __constant_htons(0x0103)
+#define PTT_AC_COOKIE __constant_htons(0x0104)
+#define PTT_VENDOR __constant_htons(0x0105)
+#define PTT_RELAY_SID __constant_htons(0x0110)
+#define PTT_SRV_ERR __constant_htons(0x0201)
+#define PTT_SYS_ERR __constant_htons(0x0202)
+#define PTT_GEN_ERR __constant_htons(0x0203)
+
+struct pppoe_hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 ver : 4;
+ __u8 type : 4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 type : 4;
+ __u8 ver : 4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 code;
+ __u16 sid;
+ __u16 length;
+ struct pppoe_tag tag[0];
+} __attribute__ ((packed));
+
+
+/* Socket options */
+#define PPTP_SO_TIMEOUT 1
+
+
+#ifdef __KERNEL__
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+struct pppoe_opt {
+ struct net_device *dev; /* device associated with socket*/
+ struct pppoe_addr pa; /* what this socket is bound to*/
+ struct sockaddr_pppox relay; /* what socket data will be
+ relayed to (PPPoE relaying) */
+};
+#endif
+struct pptp_opt {
+ struct pptp_addr src_addr;
+ struct pptp_addr dst_addr;
+ __u32 ack_sent, ack_recv;
+ __u32 seq_sent, seq_recv;
+ int ppp_flags;
+};
+#define PPTP_FLAG_PAUSE 0
+#define PPTP_FLAG_PROC 1
+
+#include <net/sock.h>
+
+struct pppox_sock {
+ /* struct sock must be the first member of pppox_sock */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ struct ppp_channel chan;
+ struct sock *sk;
+ #else
+ struct sock sk;
+ struct ppp_channel chan;
+ #endif
+ struct pppox_sock *next; /* for hash table */
+ union {
+ struct pppoe_opt pppoe;
+ struct pptp_opt pptp;
+ } proto;
+ unsigned short num;
+};
+#define pppoe_dev proto.pppoe.dev
+#define pppoe_pa proto.pppoe.pa
+#define pppoe_relay proto.pppoe.relay
+
+static inline struct pppox_sock *pppox_sk(struct sock *sk)
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ return (struct pppox_sock *)sk->protinfo.pppox;
+ #else
+ return (struct pppox_sock *)sk;
+ #endif
+}
+
+static inline struct sock *sk_pppox(struct pppox_sock *po)
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ return po->sk;
+ #else
+ return (struct sock *)po;
+ #endif
+}
+
+struct module;
+
+struct pppox_proto {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ int (*create)(struct socket *sock);
+ #else
+ int (*create)(struct net *net, struct socket *sock);
+ #endif
+ int (*ioctl)(struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ struct module *owner;
+ #endif
+};
+
+extern int register_pppox_proto(int proto_num, struct pppox_proto *pp);
+extern void unregister_pppox_proto(int proto_num);
+extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */
+extern int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+
+/* PPPoX socket states */
+enum {
+ PPPOX_NONE = 0, /* initial state */
+ PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */
+ PPPOX_BOUND = 2, /* bound to ppp device */
+ PPPOX_RELAY = 4, /* forwarding is enabled */
+ PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */
+ PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* !(__LINUX_IF_PPPOX_H) */
diff --git a/kernel/driver/ppp_generic_smp.c b/kernel/driver/ppp_generic_smp.c
new file mode 100644
index 0000000..cbb202c
--- /dev/null
+++ b/kernel/driver/ppp_generic_smp.c
@@ -0,0 +1,3042 @@
+/*
+ * Generic PPP layer for Linux.
+ *
+ * Copyright 1999-2002 Paul Mackerras.
+ *
+ * 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.
+ *
+ * The generic PPP layer handles the PPP network interfaces, the
+ * /dev/ppp device, packet and VJ compression, and multilink.
+ * It talks to PPP `channels' via the interface defined in
+ * include/linux/ppp_channel.h. Channels provide the basic means for
+ * sending and receiving PPP frames on some kind of communications
+ * channel.
+ *
+ * Part of the code in this driver was inspired by the old async-only
+ * PPP driver, written by Michael Callahan and Al Longyear, and
+ * subsequently hacked by Paul Mackerras.
+ *
+ * ==FILEVERSION 20041108==
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/ppp_defs.h>
+#include <linux/filter.h>
+#include <linux/if_ppp.h>
+#include <linux/ppp_channel.h>
+#include <linux/ppp-comp.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/rwsem.h>
+#include <linux/stddef.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <net/slhc_vj.h>
+#include <asm/atomic.h>
+
+#define PPP_VERSION "2.4.2"
+
+/*
+ * Network protocols we support.
+ */
+#define NP_IP 0 /* Internet Protocol V4 */
+#define NP_IPV6 1 /* Internet Protocol V6 */
+#define NP_IPX 2 /* IPX protocol */
+#define NP_AT 3 /* Appletalk protocol */
+#define NP_MPLS_UC 4 /* MPLS unicast */
+#define NP_MPLS_MC 5 /* MPLS multicast */
+#define NUM_NP 6 /* Number of NPs. */
+
+#define MPHDRLEN 6 /* multilink protocol header length */
+#define MPHDRLEN_SSN 4 /* ditto with short sequence numbers */
+#define MIN_FRAG_SIZE 64
+
+struct worker_thread_t {
+ struct list_head entry;
+ struct task_struct *task;
+};
+
+struct ppp_work_head_t {
+ struct list_head entry;
+ spinlock_t lock;
+ struct ppp *ppp;
+ struct channel *pch;
+ int queued;
+};
+
+/*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel. In both cases, file->private_data
+ * points to one of these.
+ */
+struct ppp_file {
+ enum {
+ INTERFACE=1, CHANNEL
+ } kind;
+ struct sk_buff_head xq; /* pppd transmit queue */
+ struct sk_buff_head rq; /* receive queue for pppd */
+ wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
+ atomic_t refcnt; /* # refs (incl /dev/ppp attached) */
+ int hdrlen; /* space to leave for headers */
+ int index; /* interface unit / channel number */
+ int dead; /* unit/channel has been shut down */
+};
+
+#define PF_TO_X(pf, X) container_of(pf, X, file)
+
+#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
+#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
+
+/*
+ * Data structure describing one ppp unit.
+ * A ppp unit corresponds to a ppp network interface device
+ * and represents a multilink bundle.
+ * It can have 0 or more ppp channels connected to it.
+ */
+struct ppp {
+ struct ppp_work_head_t work;
+ struct ppp_file file; /* stuff for read/write/poll 0 */
+ struct file *owner; /* file that owns this unit 48 */
+ struct list_head channels; /* list of attached channels 4c */
+ int n_channels; /* how many channels are attached 54 */
+ spinlock_t rlock; /* lock for receive side 58 */
+ spinlock_t wlock; /* lock for transmit side 5c */
+ int mru; /* max receive unit 60 */
+ unsigned int flags; /* control bits 64 */
+ unsigned int xstate; /* transmit state bits 68 */
+ unsigned int rstate; /* receive state bits 6c */
+ int debug; /* debug flags 70 */
+ struct slcompress *vj; /* state for VJ header compression */
+ enum NPmode npmode[NUM_NP]; /* what to do with each net proto 78 */
+ struct sk_buff *xmit_pending; /* a packet ready to go out 88 */
+ struct compressor *xcomp; /* transmit packet compressor 8c */
+ void *xc_state; /* its internal state 90 */
+ struct compressor *rcomp; /* receive decompressor 94 */
+ void *rc_state; /* its internal state 98 */
+ unsigned long last_xmit; /* jiffies when last pkt sent 9c */
+ unsigned long last_recv; /* jiffies when last pkt rcvd a0 */
+ struct net_device *dev; /* network interface device a4 */
+ int closing;
+
+#ifdef CONFIG_PPP_MULTILINK
+ int nxchan; /* next channel to send something on */
+ u32 nxseq; /* next sequence number to send */
+ int mrru; /* MP: max reconst. receive unit */
+ u32 nextseq; /* MP: seq no of next packet */
+ u32 minseq; /* MP: min of most recent seqnos */
+ struct sk_buff_head mrq; /* MP: receive reconstruction queue */
+#endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+ struct sock_filter *pass_filter; /* filter for packets to pass */
+ struct sock_filter *active_filter;/* filter for pkts to reset idle */
+ unsigned pass_len, active_len;
+#endif /* CONFIG_PPP_FILTER */
+};
+
+/*
+ * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP,
+ * SC_MUST_COMP
+ * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
+ * Bits in xstate: SC_COMP_RUN
+ */
+#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
+ |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \
+ |SC_COMP_TCP|SC_REJ_COMP_TCP|SC_MUST_COMP)
+
+/*
+ * Private data structure for each channel.
+ * This includes the data structure used for multilink.
+ */
+struct channel {
+ struct ppp_work_head_t work;
+ struct ppp_file file; /* stuff for read/write/poll */
+ struct list_head list; /* link in all/new_channels list */
+ struct ppp_channel *chan; /* public channel data structure */
+ struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */
+ spinlock_t downl; /* protects `chan', file.xq dequeue */
+ struct ppp *ppp; /* ppp unit we're connected to */
+ struct list_head clist; /* link in list of channels per unit */
+ rwlock_t upl; /* protects `ppp' */
+
+ struct sk_buff_head rq;
+#ifdef CONFIG_PPP_MULTILINK
+ u8 avail; /* flag used in multilink stuff */
+ u8 had_frag; /* >= 1 fragments have been sent */
+ u32 lastseq; /* MP: last sequence # received */
+#endif /* CONFIG_PPP_MULTILINK */
+};
+
+/*
+ * SMP locking issues:
+ * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
+ * list and the ppp.n_channels field, you need to take both locks
+ * before you modify them.
+ * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
+ * channel.downl.
+ */
+
+/*
+ * A cardmap represents a mapping from unsigned integers to pointers,
+ * and provides a fast "find lowest unused number" operation.
+ * It uses a broad (32-way) tree with a bitmap at each level.
+ * It is designed to be space-efficient for small numbers of entries
+ * and time-efficient for large numbers of entries.
+ */
+#define CARDMAP_ORDER 5
+#define CARDMAP_WIDTH (1U << CARDMAP_ORDER)
+#define CARDMAP_MASK (CARDMAP_WIDTH - 1)
+
+struct cardmap {
+ int shift;
+ unsigned long inuse;
+ struct cardmap *parent;
+ void *ptr[CARDMAP_WIDTH];
+};
+static void *cardmap_get(struct cardmap *map, unsigned int nr);
+static int cardmap_set(struct cardmap **map, unsigned int nr, void *ptr);
+static unsigned int cardmap_find_first_free(struct cardmap *map);
+static void cardmap_destroy(struct cardmap **map);
+
+/*
+ * all_ppp_mutex protects the all_ppp_units mapping.
+ * It also ensures that finding a ppp unit in the all_ppp_units map
+ * and updating its file.refcnt field is atomic.
+ */
+static DEFINE_MUTEX(all_ppp_mutex);
+static struct cardmap *all_ppp_units;
+static atomic_t ppp_unit_count = ATOMIC_INIT(0);
+
+/*
+ * all_channels_lock protects all_channels and last_channel_index,
+ * and the atomicity of find a channel and updating its file.refcnt
+ * field.
+ */
+static DEFINE_SPINLOCK(all_channels_lock);
+static LIST_HEAD(all_channels);
+static LIST_HEAD(new_channels);
+static int last_channel_index;
+static atomic_t channel_count = ATOMIC_INIT(0);
+
+/* Get the PPP protocol number from a skb */
+#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
+
+/* We limit the length of ppp->file.rq to this (arbitrary) value */
+#define PPP_MAX_RQLEN 32
+
+/*
+ * Maximum number of multilink fragments queued up.
+ * This has to be large enough to cope with the maximum latency of
+ * the slowest channel relative to the others. Strictly it should
+ * depend on the number of channels and their characteristics.
+ */
+#define PPP_MP_MAX_QLEN 128
+
+/* Multilink header bits. */
+#define B 0x80 /* this fragment begins a packet */
+#define E 0x40 /* this fragment ends a packet */
+
+/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
+#define seq_before(a, b) ((s32)((a) - (b)) < 0)
+#define seq_after(a, b) ((s32)((a) - (b)) > 0)
+
+/* Prototypes. */
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_push(struct ppp *ppp);
+static void ppp_channel_push(struct channel *pch);
+static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
+ struct sk_buff *skb);
+#ifdef CONFIG_PPP_MULTILINK
+static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
+#endif /* CONFIG_PPP_MULTILINK */
+static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
+static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
+static void ppp_ccp_closed(struct ppp *ppp);
+static struct compressor *find_compressor(int type);
+static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
+static struct ppp *ppp_create_interface(int unit, int *retp);
+static void init_ppp_file(struct ppp_file *pf, int kind);
+static void ppp_shutdown_interface(struct ppp *ppp);
+static void ppp_destroy_interface(struct ppp *ppp);
+static struct ppp *ppp_find_unit(int unit);
+static struct channel *ppp_find_channel(int unit);
+static int ppp_connect_channel(struct channel *pch, int unit);
+static int ppp_disconnect_channel(struct channel *pch);
+static void ppp_destroy_channel(struct channel *pch);
+
+static int run_worker_threads(void);
+static void stop_worker_threads(void);
+static int __ppp_queue_work(struct ppp_work_head_t *h);
+static void ppp_queue_work(struct ppp_work_head_t *h);
+
+static struct class *ppp_class;
+
+static LIST_HEAD(worker_thread_list);
+static DEFINE_SPINLOCK(ppp_wq_lock);
+static LIST_HEAD(ppp_work_queue);
+static DECLARE_WAIT_QUEUE_HEAD(ppp_wq_wait);
+static int smp_affinity=0xffff;
+
+/* Translates a PPP protocol number to a NP index (NP == network protocol) */
+static inline int proto_to_npindex(int proto)
+{
+ switch (proto) {
+ case PPP_IP:
+ return NP_IP;
+ case PPP_IPV6:
+ return NP_IPV6;
+ case PPP_IPX:
+ return NP_IPX;
+ case PPP_AT:
+ return NP_AT;
+ case PPP_MPLS_UC:
+ return NP_MPLS_UC;
+ case PPP_MPLS_MC:
+ return NP_MPLS_MC;
+ }
+ return -EINVAL;
+}
+
+/* Translates an NP index into a PPP protocol number */
+static const int npindex_to_proto[NUM_NP] = {
+ PPP_IP,
+ PPP_IPV6,
+ PPP_IPX,
+ PPP_AT,
+ PPP_MPLS_UC,
+ PPP_MPLS_MC,
+};
+
+/* Translates an ethertype into an NP index */
+static inline int ethertype_to_npindex(int ethertype)
+{
+ switch (ethertype) {
+ case ETH_P_IP:
+ return NP_IP;
+ case ETH_P_IPV6:
+ return NP_IPV6;
+ case ETH_P_IPX:
+ return NP_IPX;
+ case ETH_P_PPPTALK:
+ case ETH_P_ATALK:
+ return NP_AT;
+ case ETH_P_MPLS_UC:
+ return NP_MPLS_UC;
+ case ETH_P_MPLS_MC:
+ return NP_MPLS_MC;
+ }
+ return -1;
+}
+
+/* Translates an NP index into an ethertype */
+static const int npindex_to_ethertype[NUM_NP] = {
+ ETH_P_IP,
+ ETH_P_IPV6,
+ ETH_P_IPX,
+ ETH_P_PPPTALK,
+ ETH_P_MPLS_UC,
+ ETH_P_MPLS_MC,
+};
+
+/*
+ * Locking shorthand.
+ */
+#define ppp_xmit_lock(ppp) spin_lock_bh(&(ppp)->wlock)
+#define ppp_xmit_unlock(ppp) spin_unlock_bh(&(ppp)->wlock)
+#define ppp_recv_lock(ppp) spin_lock_bh(&(ppp)->rlock)
+#define ppp_recv_unlock(ppp) spin_unlock_bh(&(ppp)->rlock)
+#define ppp_lock(ppp) do { ppp_xmit_lock(ppp); \
+ ppp_recv_lock(ppp); } while (0)
+#define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \
+ ppp_xmit_unlock(ppp); } while (0)
+
+/*
+ * /dev/ppp device routines.
+ * The /dev/ppp device is used by pppd to control the ppp unit.
+ * It supports the read, write, ioctl and poll functions.
+ * Open instances of /dev/ppp can be in one of three states:
+ * unattached, attached to a ppp unit, or attached to a ppp channel.
+ */
+static int ppp_open(struct inode *inode, struct file *file)
+{
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26)
+ cycle_kernel_lock();
+ #endif
+ /*
+ * This could (should?) be enforced by the permissions on /dev/ppp.
+ */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+static int ppp_release(struct inode *unused, struct file *file)
+{
+ struct ppp_file *pf = file->private_data;
+ struct ppp *ppp;
+
+ if (pf) {
+ file->private_data = NULL;
+ if (pf->kind == INTERFACE) {
+ ppp = PF_TO_PPP(pf);
+ if (file == ppp->owner)
+ ppp_shutdown_interface(ppp);
+ }
+ if (atomic_dec_and_test(&pf->refcnt)) {
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_destroy_interface(PF_TO_PPP(pf));
+ break;
+ case CHANNEL:
+ ppp_destroy_channel(PF_TO_CHANNEL(pf));
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static ssize_t ppp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ppp_file *pf = file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret;
+ struct sk_buff *skb = NULL;
+
+ ret = count;
+
+ if (!pf)
+ return -ENXIO;
+ add_wait_queue(&pf->rwait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ skb = skb_dequeue(&pf->rq);
+ if (skb)
+ break;
+ ret = 0;
+ if (pf->dead)
+ break;
+ if (pf->kind == INTERFACE) {
+ /*
+ * Return 0 (EOF) on an interface that has no
+ * channels connected, unless it is looping
+ * network traffic (demand mode).
+ */
+ struct ppp *ppp = PF_TO_PPP(pf);
+ if (ppp->n_channels == 0
+ && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+ break;
+ }
+ ret = -EAGAIN;
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ ret = -ERESTARTSYS;
+ if (signal_pending(current))
+ break;
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&pf->rwait, &wait);
+
+ if (!skb)
+ goto out;
+
+ ret = -EOVERFLOW;
+ if (skb->len > count)
+ goto outf;
+ ret = -EFAULT;
+ if (copy_to_user(buf, skb->data, skb->len))
+ goto outf;
+ ret = skb->len;
+
+ outf:
+ kfree_skb(skb);
+ out:
+ return ret;
+}
+
+static ssize_t ppp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ppp_file *pf = file->private_data;
+ struct sk_buff *skb;
+ struct ppp *ppp;
+ ssize_t ret;
+
+ if (!pf)
+ return -ENXIO;
+ ret = -ENOMEM;
+ skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
+ if (!skb)
+ goto out;
+ skb_reserve(skb, pf->hdrlen);
+ ret = -EFAULT;
+ if (copy_from_user(skb_put(skb, count), buf, count)) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ skb_queue_tail(&pf->xq, skb);
+
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp=PF_TO_PPP(pf);
+ ppp_queue_work(&ppp->work);
+ break;
+ case CHANNEL:
+ ppp_channel_push(PF_TO_CHANNEL(pf));
+ break;
+ }
+
+ ret = count;
+
+ out:
+ return ret;
+}
+
+/* No kernel lock - fine */
+static unsigned int ppp_poll(struct file *file, poll_table *wait)
+{
+ struct ppp_file *pf = file->private_data;
+ unsigned int mask;
+
+ if (!pf)
+ return 0;
+ poll_wait(file, &pf->rwait, wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (skb_peek(&pf->rq))
+ mask |= POLLIN | POLLRDNORM;
+ if (pf->dead)
+ mask |= POLLHUP;
+ else if (pf->kind == INTERFACE) {
+ /* see comment in ppp_read */
+ struct ppp *ppp = PF_TO_PPP(pf);
+ if (ppp->n_channels == 0
+ && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+#ifdef CONFIG_PPP_FILTER
+static int get_filter(void __user *arg, struct sock_filter **p)
+{
+ struct sock_fprog uprog;
+ struct sock_filter *code = NULL;
+ int len, err;
+
+ if (copy_from_user(&uprog, arg, sizeof(uprog)))
+ return -EFAULT;
+
+ if (!uprog.len) {
+ *p = NULL;
+ return 0;
+ }
+
+ len = uprog.len * sizeof(struct sock_filter);
+ code = kmalloc(len, GFP_KERNEL);
+ if (code == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(code, uprog.filter, len)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ err = sk_chk_filter(code, uprog.len);
+ if (err) {
+ kfree(code);
+ return err;
+ }
+
+ *p = code;
+ return uprog.len;
+}
+#endif /* CONFIG_PPP_FILTER */
+
+static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct ppp_file *pf = file->private_data;
+ struct ppp *ppp;
+ int err = -EFAULT, val, val2, i;
+ struct ppp_idle idle;
+ struct npioctl npi;
+ int unit, cflags;
+ struct slcompress *vj;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+
+ if (!pf)
+ return ppp_unattached_ioctl(pf, file, cmd, arg);
+
+ if (cmd == PPPIOCDETACH) {
+ /*
+ * We have to be careful here... if the file descriptor
+ * has been dup'd, we could have another process in the
+ * middle of a poll using the same file *, so we had
+ * better not free the interface data structures -
+ * instead we fail the ioctl. Even in this case, we
+ * shut down the interface if we are the owner of it.
+ * Actually, we should get rid of PPPIOCDETACH, userland
+ * (i.e. pppd) could achieve the same effect by closing
+ * this fd and reopening /dev/ppp.
+ */
+ err = -EINVAL;
+ lock_kernel();
+ if (pf->kind == INTERFACE) {
+ ppp = PF_TO_PPP(pf);
+ if (file == ppp->owner)
+ ppp_shutdown_interface(ppp);
+ }
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ if (atomic_read(&file->f_count) <= 2) {
+ #else
+ if (atomic_long_read(&file->f_count) <= 2) {
+ #endif
+ ppp_release(NULL, file);
+ err = 0;
+ } else
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n",
+ atomic_read(&file->f_count));
+ #else
+ printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n",
+ atomic_long_read(&file->f_count));
+ #endif
+ unlock_kernel();
+ return err;
+ }
+
+ if (pf->kind == CHANNEL) {
+ struct channel *pch;
+ struct ppp_channel *chan;
+
+ lock_kernel();
+ pch = PF_TO_CHANNEL(pf);
+
+ switch (cmd) {
+ case PPPIOCCONNECT:
+ if (get_user(unit, p))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+
+ case PPPIOCDISCONN:
+ err = ppp_disconnect_channel(pch);
+ break;
+
+ default:
+ down_read(&pch->chan_sem);
+ chan = pch->chan;
+ err = -ENOTTY;
+ if (chan && chan->ops->ioctl)
+ err = chan->ops->ioctl(chan, cmd, arg);
+ up_read(&pch->chan_sem);
+ }
+ unlock_kernel();
+ return err;
+ }
+
+ if (pf->kind != INTERFACE) {
+ /* can't happen */
+ printk(KERN_ERR "PPP: not interface or channel??\n");
+ return -EINVAL;
+ }
+
+ lock_kernel();
+ ppp = PF_TO_PPP(pf);
+ switch (cmd) {
+ case PPPIOCSMRU:
+ if (get_user(val, p))
+ break;
+ ppp->mru = val;
+ err = 0;
+ break;
+
+ case PPPIOCSFLAGS:
+ if (get_user(val, p))
+ break;
+ ppp_lock(ppp);
+ cflags = ppp->flags & ~val;
+ ppp->flags = val & SC_FLAG_BITS;
+ ppp_unlock(ppp);
+ if (cflags & SC_CCP_OPEN)
+ ppp_ccp_closed(ppp);
+ err = 0;
+ break;
+
+ case PPPIOCGFLAGS:
+ val = ppp->flags | ppp->xstate | ppp->rstate;
+ if (put_user(val, p))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCSCOMPRESS:
+ err = ppp_set_compress(ppp, arg);
+ break;
+
+ case PPPIOCGUNIT:
+ if (put_user(ppp->file.index, p))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCSDEBUG:
+ if (get_user(val, p))
+ break;
+ ppp->debug = val;
+ err = 0;
+ break;
+
+ case PPPIOCGDEBUG:
+ if (put_user(ppp->debug, p))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGIDLE:
+ idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+ idle.recv_idle = (jiffies - ppp->last_recv) / HZ;
+ if (copy_to_user(argp, &idle, sizeof(idle)))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCSMAXCID:
+ if (get_user(val, p))
+ break;
+ val2 = 15;
+ if ((val >> 16) != 0) {
+ val2 = val >> 16;
+ val &= 0xffff;
+ }
+ vj = slhc_init(val2+1, val+1);
+ if (!vj) {
+ printk(KERN_ERR "PPP: no memory (VJ compressor)\n");
+ err = -ENOMEM;
+ break;
+ }
+ ppp_lock(ppp);
+ if (ppp->vj)
+ slhc_free(ppp->vj);
+ ppp->vj = vj;
+ ppp_unlock(ppp);
+ err = 0;
+ break;
+
+ case PPPIOCGNPMODE:
+ case PPPIOCSNPMODE:
+ if (copy_from_user(&npi, argp, sizeof(npi)))
+ break;
+ err = proto_to_npindex(npi.protocol);
+ if (err < 0)
+ break;
+ i = err;
+ if (cmd == PPPIOCGNPMODE) {
+ err = -EFAULT;
+ npi.mode = ppp->npmode[i];
+ if (copy_to_user(argp, &npi, sizeof(npi)))
+ break;
+ } else {
+ ppp->npmode[i] = npi.mode;
+ /* we may be able to transmit more packets now (??) */
+ netif_wake_queue(ppp->dev);
+ }
+ err = 0;
+ break;
+
+#ifdef CONFIG_PPP_FILTER
+ case PPPIOCSPASS:
+ {
+ struct sock_filter *code;
+ err = get_filter(argp, &code);
+ if (err >= 0) {
+ ppp_lock(ppp);
+ kfree(ppp->pass_filter);
+ ppp->pass_filter = code;
+ ppp->pass_len = err;
+ ppp_unlock(ppp);
+ err = 0;
+ }
+ break;
+ }
+ case PPPIOCSACTIVE:
+ {
+ struct sock_filter *code;
+ err = get_filter(argp, &code);
+ if (err >= 0) {
+ ppp_lock(ppp);
+ kfree(ppp->active_filter);
+ ppp->active_filter = code;
+ ppp->active_len = err;
+ ppp_unlock(ppp);
+ err = 0;
+ }
+ break;
+ }
+#endif /* CONFIG_PPP_FILTER */
+
+#ifdef CONFIG_PPP_MULTILINK
+ case PPPIOCSMRRU:
+ if (get_user(val, p))
+ break;
+ ppp_recv_lock(ppp);
+ ppp->mrru = val;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ break;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ default:
+ err = -ENOTTY;
+ }
+
+ unlock_kernel();
+ return err;
+}
+
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int unit, err = -EFAULT;
+ struct ppp *ppp;
+ struct channel *chan;
+ int __user *p = (int __user *)arg;
+
+ lock_kernel();
+ switch (cmd) {
+ case PPPIOCNEWUNIT:
+ /* Create a new ppp unit */
+ if (get_user(unit, p))
+ break;
+ ppp = ppp_create_interface(unit, &err);
+ if (!ppp)
+ break;
+ file->private_data = &ppp->file;
+ ppp->owner = file;
+ err = -EFAULT;
+ if (put_user(ppp->file.index, p))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCATTACH:
+ /* Attach to an existing ppp unit */
+ if (get_user(unit, p))
+ break;
+ mutex_lock(&all_ppp_mutex);
+ err = -ENXIO;
+ ppp = ppp_find_unit(unit);
+ if (ppp) {
+ atomic_inc(&ppp->file.refcnt);
+ file->private_data = &ppp->file;
+ err = 0;
+ }
+ mutex_unlock(&all_ppp_mutex);
+ break;
+
+ case PPPIOCATTCHAN:
+ if (get_user(unit, p))
+ break;
+ spin_lock_bh(&all_channels_lock);
+ err = -ENXIO;
+ chan = ppp_find_channel(unit);
+ if (chan) {
+ atomic_inc(&chan->file.refcnt);
+ file->private_data = &chan->file;
+ err = 0;
+ }
+ spin_unlock_bh(&all_channels_lock);
+ break;
+
+ default:
+ err = -ENOTTY;
+ }
+
+ unlock_kernel();
+ return err;
+}
+
+static const struct file_operations ppp_device_fops = {
+ .owner = THIS_MODULE,
+ .read = ppp_read,
+ .write = ppp_write,
+ .poll = ppp_poll,
+ .unlocked_ioctl = ppp_ioctl,
+ .open = ppp_open,
+ .release = ppp_release
+};
+
+#define PPP_MAJOR 108
+
+/* Called at boot time if ppp is compiled into the kernel,
+ or at module load time (from init_module) if compiled as a module. */
+static int __init ppp_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
+
+ err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
+ if (!err) {
+ if ((err=run_worker_threads()))
+ stop_worker_threads();
+ else {
+ ppp_class = class_create(THIS_MODULE, "ppp");
+ if (IS_ERR(ppp_class)) {
+ err = PTR_ERR(ppp_class);
+ goto out_chrdev;
+ }
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ device_create_drvdata(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
+ #else
+ device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
+ #endif
+ }
+ }
+
+out:
+ if (err) {
+ printk(KERN_ERR "failed to register PPP device (%d)\n", err);
+ }
+ return err;
+
+out_chrdev:
+ unregister_chrdev(PPP_MAJOR, "ppp");
+ goto out;
+}
+
+/*
+ * Network interface unit routines.
+ */
+static int
+ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ppp *ppp = (struct ppp *) dev->priv;
+ int npi, proto;
+ unsigned char *pp;
+ int wakeup=0;
+
+ npi = ethertype_to_npindex(ntohs(skb->protocol));
+ if (npi < 0)
+ goto outf;
+
+ /* Drop, accept or reject the packet */
+ switch (ppp->npmode[npi]) {
+ case NPMODE_PASS:
+ break;
+ case NPMODE_QUEUE:
+ /* it would be nice to have a way to tell the network
+ system to queue this one up for later. */
+ goto outf;
+ case NPMODE_DROP:
+ case NPMODE_ERROR:
+ goto outf;
+ }
+
+ /* Put the 2-byte PPP protocol number on the front,
+ making sure there is room for the address and control fields. */
+ if (skb_cow_head(skb, PPP_HDRLEN))
+ goto outf;
+
+ pp = skb_push(skb, 2);
+ proto = npindex_to_proto[npi];
+ pp[0] = proto >> 8;
+ pp[1] = proto;
+
+ netif_stop_queue(dev);
+ spin_lock_bh(&ppp->work.lock);
+ skb_queue_tail(&ppp->file.xq, skb);
+ wakeup=__ppp_queue_work(&ppp->work);
+ spin_unlock_bh(&ppp->work.lock);
+
+ if (wakeup) wake_up_interruptible(&ppp_wq_wait);
+
+ return 0;
+
+ outf:
+ kfree_skb(skb);
+ ++ppp->dev->stats.tx_dropped;
+ return 0;
+}
+
+static int
+ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ppp *ppp = dev->priv;
+ int err = -EFAULT;
+ void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data;
+ struct ppp_stats stats;
+ struct ppp_comp_stats cstats;
+ char *vers;
+
+ switch (cmd) {
+ case SIOCGPPPSTATS:
+ ppp_get_stats(ppp, &stats);
+ if (copy_to_user(addr, &stats, sizeof(stats)))
+ break;
+ err = 0;
+ break;
+
+ case SIOCGPPPCSTATS:
+ memset(&cstats, 0, sizeof(cstats));
+ if (ppp->xc_state)
+ ppp->xcomp->comp_stat(ppp->xc_state, &cstats.c);
+ if (ppp->rc_state)
+ ppp->rcomp->decomp_stat(ppp->rc_state, &cstats.d);
+ if (copy_to_user(addr, &cstats, sizeof(cstats)))
+ break;
+ err = 0;
+ break;
+
+ case SIOCGPPPVER:
+ vers = PPP_VERSION;
+ if (copy_to_user(addr, vers, strlen(vers) + 1))
+ break;
+ err = 0;
+ break;
+
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static void ppp_setup(struct net_device *dev)
+{
+ dev->hard_header_len = PPP_HDRLEN;
+ dev->mtu = PPP_MTU;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 3;
+ dev->type = ARPHRD_PPP;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+}
+
+static inline struct sk_buff *
+pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
+{
+ struct sk_buff *new_skb;
+ int len;
+ int new_skb_size = ppp->dev->mtu +
+ ppp->xcomp->comp_extra + ppp->dev->hard_header_len;
+ int compressor_skb_size = ppp->dev->mtu +
+ ppp->xcomp->comp_extra + PPP_HDRLEN;
+ new_skb = alloc_skb(new_skb_size, GFP_ATOMIC);
+ if (!new_skb) {
+ if (net_ratelimit())
+ printk(KERN_ERR "PPP: no memory (comp pkt)\n");
+ return NULL;
+ }
+ if (ppp->dev->hard_header_len > PPP_HDRLEN)
+ skb_reserve(new_skb,
+ ppp->dev->hard_header_len - PPP_HDRLEN);
+
+ /* compressor still expects A/C bytes in hdr */
+ len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2,
+ new_skb->data, skb->len + 2,
+ compressor_skb_size);
+ if (len > 0 && (ppp->flags & SC_CCP_UP)) {
+ kfree_skb(skb);
+ skb = new_skb;
+ skb_put(skb, len);
+ skb_pull(skb, 2); /* pull off A/C bytes */
+ } else if (len == 0) {
+ /* didn't compress, or CCP not up yet */
+ kfree_skb(new_skb);
+ new_skb = skb;
+ } else {
+ /*
+ * (len < 0)
+ * MPPE requires that we do not send unencrypted
+ * frames. The compressor will return -1 if we
+ * should drop the frame. We cannot simply test
+ * the compress_proto because MPPE and MPPC share
+ * the same number.
+ */
+ if (net_ratelimit())
+ printk(KERN_ERR "ppp: compressor dropped pkt\n");
+ kfree_skb(skb);
+ kfree_skb(new_skb);
+ new_skb = NULL;
+ }
+ return new_skb;
+}
+
+/*
+ * Compress and send a frame.
+ * The caller should have locked the xmit path,
+ * and xmit_pending should be 0.
+ */
+static void
+ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+ int proto = PPP_PROTO(skb);
+ struct sk_buff *new_skb;
+ int len;
+ unsigned char *cp;
+
+ if (proto < 0x8000) {
+#ifdef CONFIG_PPP_FILTER
+ /* check if we should pass this packet */
+ /* the filter instructions are constructed assuming
+ a four-byte PPP header on each packet */
+ *skb_push(skb, 2) = 1;
+ if (ppp->pass_filter
+ && sk_run_filter(skb, ppp->pass_filter,
+ ppp->pass_len) == 0) {
+ if (ppp->debug & 1)
+ printk(KERN_DEBUG "PPP: outbound frame not passed\n");
+ kfree_skb(skb);
+ return;
+ }
+ /* if this packet passes the active filter, record the time */
+ if (!(ppp->active_filter
+ && sk_run_filter(skb, ppp->active_filter,
+ ppp->active_len) == 0))
+ ppp->last_xmit = jiffies;
+ skb_pull(skb, 2);
+#else
+ /* for data packets, record the time */
+ ppp->last_xmit = jiffies;
+#endif /* CONFIG_PPP_FILTER */
+ }
+
+ ++ppp->dev->stats.tx_packets;
+ ppp->dev->stats.tx_bytes += skb->len - 2;
+
+ switch (proto) {
+ case PPP_IP:
+ if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0)
+ break;
+ /* try to do VJ TCP header compression */
+ new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2,
+ GFP_ATOMIC);
+ if (!new_skb) {
+ printk(KERN_ERR "PPP: no memory (VJ comp pkt)\n");
+ goto drop;
+ }
+ skb_reserve(new_skb, ppp->dev->hard_header_len - 2);
+ cp = skb->data + 2;
+ len = slhc_compress(ppp->vj, cp, skb->len - 2,
+ new_skb->data + 2, &cp,
+ !(ppp->flags & SC_NO_TCP_CCID));
+ if (cp == skb->data + 2) {
+ /* didn't compress */
+ kfree_skb(new_skb);
+ } else {
+ if (cp[0] & SL_TYPE_COMPRESSED_TCP) {
+ proto = PPP_VJC_COMP;
+ cp[0] &= ~SL_TYPE_COMPRESSED_TCP;
+ } else {
+ proto = PPP_VJC_UNCOMP;
+ cp[0] = skb->data[2];
+ }
+ kfree_skb(skb);
+ skb = new_skb;
+ cp = skb_put(skb, len + 2);
+ cp[0] = 0;
+ cp[1] = proto;
+ }
+ break;
+
+ case PPP_CCP:
+ /* peek at outbound CCP frames */
+ ppp_ccp_peek(ppp, skb, 0);
+ break;
+ }
+
+ /* try to do packet compression */
+ if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state
+ && proto != PPP_LCP && proto != PPP_CCP) {
+ if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ppp: compression required but down - pkt dropped.\n");
+ goto drop;
+ }
+ skb = pad_compress_skb(ppp, skb);
+ if (!skb)
+ goto drop;
+ }
+
+ /*
+ * If we are waiting for traffic (demand dialling),
+ * queue it up for pppd to receive.
+ */
+ if (ppp->flags & SC_LOOP_TRAFFIC) {
+ if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
+ goto drop;
+ skb_queue_tail(&ppp->file.rq, skb);
+ wake_up_interruptible(&ppp->file.rwait);
+ return;
+ }
+
+ ppp->xmit_pending = skb;
+ ppp_push(ppp);
+ return;
+
+ drop:
+ if (skb)
+ kfree_skb(skb);
+ ++ppp->dev->stats.tx_errors;
+}
+
+/*
+ * Try to send the frame in xmit_pending.
+ * The caller should have the xmit path locked.
+ */
+static void
+ppp_push(struct ppp *ppp)
+{
+ struct list_head *list;
+ struct channel *pch;
+ struct sk_buff *skb = ppp->xmit_pending;
+
+ if (!skb)
+ return;
+
+ list = &ppp->channels;
+ if (list_empty(list)) {
+ /* nowhere to send the packet, just drop it */
+ ppp->xmit_pending = NULL;
+ kfree_skb(skb);
+ return;
+ }
+
+ if ((ppp->flags & SC_MULTILINK) == 0) {
+ /* not doing multilink: send it down the first channel */
+ list = list->next;
+ pch = list_entry(list, struct channel, clist);
+
+ spin_lock_bh(&pch->downl);
+ if (pch->chan) {
+ if (pch->chan->ops->start_xmit(pch->chan, skb))
+ ppp->xmit_pending = NULL;
+ } else {
+ /* channel got unregistered */
+ kfree_skb(skb);
+ ppp->xmit_pending = NULL;
+ }
+ spin_unlock_bh(&pch->downl);
+ return;
+ }
+
+#ifdef CONFIG_PPP_MULTILINK
+ /* Multilink: fragment the packet over as many links
+ as can take the packet at the moment. */
+ if (!ppp_mp_explode(ppp, skb))
+ return;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ ppp->xmit_pending = NULL;
+ kfree_skb(skb);
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Divide a packet to be transmitted into fragments and
+ * send them out the individual links.
+ */
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
+{
+ int len, fragsize;
+ int i, bits, hdrlen, mtu;
+ int flen;
+ int navail, nfree;
+ int nbigger;
+ unsigned char *p, *q;
+ struct list_head *list;
+ struct channel *pch;
+ struct sk_buff *frag;
+ struct ppp_channel *chan;
+
+ nfree = 0; /* # channels which have no packet already queued */
+ navail = 0; /* total # of usable channels (not deregistered) */
+ hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+ i = 0;
+ list_for_each_entry(pch, &ppp->channels, clist) {
+ navail += pch->avail = (pch->chan != NULL);
+ if (pch->avail) {
+ if (skb_queue_empty(&pch->file.xq) ||
+ !pch->had_frag) {
+ pch->avail = 2;
+ ++nfree;
+ }
+ if (!pch->had_frag && i < ppp->nxchan)
+ ppp->nxchan = i;
+ }
+ ++i;
+ }
+
+ /*
+ * Don't start sending this packet unless at least half of
+ * the channels are free. This gives much better TCP
+ * performance if we have a lot of channels.
+ */
+ if (nfree == 0 || nfree < navail / 2)
+ return 0; /* can't take now, leave it in xmit_pending */
+
+ /* Do protocol field compression (XXX this should be optional) */
+ p = skb->data;
+ len = skb->len;
+ if (*p == 0) {
+ ++p;
+ --len;
+ }
+
+ /*
+ * Decide on fragment size.
+ * We create a fragment for each free channel regardless of
+ * how small they are (i.e. even 0 length) in order to minimize
+ * the time that it will take to detect when a channel drops
+ * a fragment.
+ */
+ fragsize = len;
+ if (nfree > 1)
+ fragsize = DIV_ROUND_UP(fragsize, nfree);
+ /* nbigger channels get fragsize bytes, the rest get fragsize-1,
+ except if nbigger==0, then they all get fragsize. */
+ nbigger = len % nfree;
+
+ /* skip to the channel after the one we last used
+ and start at that one */
+ list = &ppp->channels;
+ for (i = 0; i < ppp->nxchan; ++i) {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ break;
+ }
+ }
+
+ /* create a fragment for each channel */
+ bits = B;
+ while (nfree > 0 || len > 0) {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ continue;
+ }
+ pch = list_entry(list, struct channel, clist);
+ ++i;
+ if (!pch->avail)
+ continue;
+
+ /*
+ * Skip this channel if it has a fragment pending already and
+ * we haven't given a fragment to all of the free channels.
+ */
+ if (pch->avail == 1) {
+ if (nfree > 0)
+ continue;
+ } else {
+ --nfree;
+ pch->avail = 1;
+ }
+
+ /* check the channel's mtu and whether it is still attached. */
+ spin_lock_bh(&pch->downl);
+ if (pch->chan == NULL) {
+ /* can't use this channel, it's being deregistered */
+ spin_unlock_bh(&pch->downl);
+ pch->avail = 0;
+ if (--navail == 0)
+ break;
+ continue;
+ }
+
+ /*
+ * Create a fragment for this channel of
+ * min(max(mtu+2-hdrlen, 4), fragsize, len) bytes.
+ * If mtu+2-hdrlen < 4, that is a ridiculously small
+ * MTU, so we use mtu = 2 + hdrlen.
+ */
+ if (fragsize > len)
+ fragsize = len;
+ flen = fragsize;
+ mtu = pch->chan->mtu + 2 - hdrlen;
+ if (mtu < 4)
+ mtu = 4;
+ if (flen > mtu)
+ flen = mtu;
+ if (flen == len && nfree == 0)
+ bits |= E;
+ frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC);
+ if (!frag)
+ goto noskb;
+ q = skb_put(frag, flen + hdrlen);
+
+ /* make the MP header */
+ q[0] = PPP_MP >> 8;
+ q[1] = PPP_MP;
+ if (ppp->flags & SC_MP_XSHORTSEQ) {
+ q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
+ q[3] = ppp->nxseq;
+ } else {
+ q[2] = bits;
+ q[3] = ppp->nxseq >> 16;
+ q[4] = ppp->nxseq >> 8;
+ q[5] = ppp->nxseq;
+ }
+
+ /*
+ * Copy the data in.
+ * Unfortunately there is a bug in older versions of
+ * the Linux PPP multilink reconstruction code where it
+ * drops 0-length fragments. Therefore we make sure the
+ * fragment has at least one byte of data. Any bytes
+ * we add in this situation will end up as padding on the
+ * end of the reconstructed packet.
+ */
+ if (flen == 0)
+ *skb_put(frag, 1) = 0;
+ else
+ memcpy(q + hdrlen, p, flen);
+
+ /* try to send it down the channel */
+ chan = pch->chan;
+ if (!skb_queue_empty(&pch->file.xq) ||
+ !chan->ops->start_xmit(chan, frag))
+ skb_queue_tail(&pch->file.xq, frag);
+ pch->had_frag = 1;
+ p += flen;
+ len -= flen;
+ ++ppp->nxseq;
+ bits = 0;
+ spin_unlock_bh(&pch->downl);
+
+ if (--nbigger == 0 && fragsize > 0)
+ --fragsize;
+ }
+ ppp->nxchan = i;
+
+ return 1;
+
+ noskb:
+ spin_unlock_bh(&pch->downl);
+ if (ppp->debug & 1)
+ printk(KERN_ERR "PPP: no memory (fragment)\n");
+ ++ppp->dev->stats.tx_errors;
+ ++ppp->nxseq;
+ return 1; /* abandon the frame */
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Try to send data out on a channel.
+ */
+static void
+ppp_channel_push(struct channel *pch)
+{
+ struct sk_buff *skb;
+ struct ppp *ppp;
+
+ spin_lock_bh(&pch->downl);
+ if (pch->chan) {
+ while (!skb_queue_empty(&pch->file.xq)) {
+ skb = skb_dequeue(&pch->file.xq);
+ if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+ /* put the packet back and try again later */
+ skb_queue_head(&pch->file.xq, skb);
+ break;
+ }
+ }
+ } else {
+ /* channel got deregistered */
+ skb_queue_purge(&pch->file.xq);
+ }
+ spin_unlock_bh(&pch->downl);
+ /* see if there is anything from the attached unit to be sent */
+ if (skb_queue_empty(&pch->file.xq)) {
+ read_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp)
+ ppp_queue_work(&ppp->work);
+ read_unlock_bh(&pch->upl);
+ }
+}
+
+/*
+ * Receive-side routines.
+ */
+
+/* misuse a few fields of the skb for MP reconstruction */
+#define sequence priority
+#define BEbits cb[0]
+
+static inline void
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+ int wakeup=0;
+ spin_lock_bh(&ppp->work.lock);
+ /* ppp->dev == 0 means interface is closing down */
+ if (!ppp->closing) {
+ skb_queue_tail(&pch->rq, skb);
+ wakeup=__ppp_queue_work(&pch->work);
+ } else
+ kfree_skb(skb);
+ spin_unlock_bh(&ppp->work.lock);
+
+ if (wakeup) wake_up_interruptible(&ppp_wq_wait);
+}
+
+void
+ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct channel *pch = chan->ppp;
+ int proto;
+
+ if (!pch || skb->len == 0) {
+ kfree_skb(skb);
+ return;
+ }
+
+ proto = PPP_PROTO(skb);
+ read_lock_bh(&pch->upl);
+ if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
+ /* put it on the channel queue */
+ skb_queue_tail(&pch->file.rq, skb);
+ /* drop old frames if queue too long */
+ while (pch->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&pch->file.rq)))
+ kfree_skb(skb);
+ wake_up_interruptible(&pch->file.rwait);
+ } else {
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ read_unlock_bh(&pch->upl);
+}
+
+/* Put a 0-length skb in the receive queue as an error indication */
+void
+ppp_input_error(struct ppp_channel *chan, int code)
+{
+ struct channel *pch = chan->ppp;
+ struct sk_buff *skb;
+
+ if (!pch)
+ return;
+
+ read_lock_bh(&pch->upl);
+ if (pch->ppp) {
+ skb = alloc_skb(0, GFP_ATOMIC);
+ if (skb) {
+ skb->len = 0; /* probably unnecessary */
+ skb->cb[0] = code;
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ }
+ read_unlock_bh(&pch->upl);
+}
+
+/*
+ * We come in here to process a received frame.
+ * The receive side of the ppp unit is locked.
+ */
+static void
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+ if (pskb_may_pull(skb, 2)) {
+#ifdef CONFIG_PPP_MULTILINK
+ /* XXX do channel-level decompression here */
+ if (PPP_PROTO(skb) == PPP_MP)
+ ppp_receive_mp_frame(ppp, skb, pch);
+ else
+#endif /* CONFIG_PPP_MULTILINK */
+ ppp_receive_nonmp_frame(ppp, skb);
+ return;
+ }
+
+ if (skb->len > 0)
+ /* note: a 0-length skb is used as an error indication */
+ ++ppp->dev->stats.rx_length_errors;
+
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
+}
+
+static void
+ppp_receive_error(struct ppp *ppp)
+{
+ ++ppp->dev->stats.rx_errors;
+ if (ppp->vj)
+ slhc_toss(ppp->vj);
+}
+
+static void
+ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+ struct sk_buff *ns;
+ int proto, len, npi;
+
+ /*
+ * Decompress the frame, if compressed.
+ * Note that some decompressors need to see uncompressed frames
+ * that come in as well as compressed frames.
+ */
+ if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)
+ && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
+ skb = ppp_decompress_frame(ppp, skb);
+
+ if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
+ goto err;
+
+ proto = PPP_PROTO(skb);
+ switch (proto) {
+ case PPP_VJC_COMP:
+ /* decompress VJ compressed packets */
+ if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
+ goto err;
+
+ if (skb_tailroom(skb) < 124 || skb_cloned(skb)) {
+ /* copy to a new sk_buff with more tailroom */
+ ns = dev_alloc_skb(skb->len + 128);
+ if (!ns) {
+ printk(KERN_ERR"PPP: no memory (VJ decomp)\n");
+ goto err;
+ }
+ skb_reserve(ns, 2);
+ skb_copy_bits(skb, 0, skb_put(ns, skb->len), skb->len);
+ kfree_skb(skb);
+ skb = ns;
+ }
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+
+ len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
+ if (len <= 0) {
+ printk(KERN_DEBUG "PPP: VJ decompression error\n");
+ goto err;
+ }
+ len += 2;
+ if (len > skb->len)
+ skb_put(skb, len - skb->len);
+ else if (len < skb->len)
+ skb_trim(skb, len);
+ proto = PPP_IP;
+ break;
+
+ case PPP_VJC_UNCOMP:
+ if (!ppp->vj || (ppp->flags & SC_REJ_COMP_TCP))
+ goto err;
+
+ /* Until we fix the decompressor need to make sure
+ * data portion is linear.
+ */
+ if (!pskb_may_pull(skb, skb->len))
+ goto err;
+
+ if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) {
+ printk(KERN_ERR "PPP: VJ uncompressed error\n");
+ goto err;
+ }
+ proto = PPP_IP;
+ break;
+
+ case PPP_CCP:
+ ppp_ccp_peek(ppp, skb, 1);
+ break;
+ }
+
+ ++ppp->dev->stats.rx_packets;
+ ppp->dev->stats.rx_bytes += skb->len - 2;
+
+ npi = proto_to_npindex(proto);
+ if (npi < 0) {
+ /* control or unknown frame - pass it to pppd */
+ skb_queue_tail(&ppp->file.rq, skb);
+ /* limit queue length by dropping old frames */
+ while (ppp->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&ppp->file.rq)))
+ kfree_skb(skb);
+ /* wake up any process polling or blocking on read */
+ wake_up_interruptible(&ppp->file.rwait);
+
+ } else {
+ /* network protocol frame - give it to the kernel */
+
+#ifdef CONFIG_PPP_FILTER
+ /* check if the packet passes the pass and active filters */
+ /* the filter instructions are constructed assuming
+ a four-byte PPP header on each packet */
+ if (ppp->pass_filter || ppp->active_filter) {
+ if (skb_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+ goto err;
+
+ *skb_push(skb, 2) = 0;
+ if (ppp->pass_filter
+ && sk_run_filter(skb, ppp->pass_filter,
+ ppp->pass_len) == 0) {
+ if (ppp->debug & 1)
+ printk(KERN_DEBUG "PPP: inbound frame "
+ "not passed\n");
+ kfree_skb(skb);
+ return;
+ }
+ if (!(ppp->active_filter
+ && sk_run_filter(skb, ppp->active_filter,
+ ppp->active_len) == 0))
+ ppp->last_recv = jiffies;
+ __skb_pull(skb, 2);
+ } else
+#endif /* CONFIG_PPP_FILTER */
+ ppp->last_recv = jiffies;
+
+ if ((ppp->dev->flags & IFF_UP) == 0
+ || ppp->npmode[npi] != NPMODE_PASS) {
+ kfree_skb(skb);
+ } else {
+ /* chop off protocol */
+ skb_pull_rcsum(skb, 2);
+ skb->dev = ppp->dev;
+ skb->protocol = htons(npindex_to_ethertype[npi]);
+ skb_reset_mac_header(skb);
+ netif_rx(skb);
+ ppp->dev->last_rx = jiffies;
+ }
+ }
+ return;
+
+ err:
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
+}
+
+static struct sk_buff *
+ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+ int proto = PPP_PROTO(skb);
+ struct sk_buff *ns;
+ int len;
+
+ /* Until we fix all the decompressor's need to make sure
+ * data portion is linear.
+ */
+ if (!pskb_may_pull(skb, skb->len))
+ goto err;
+
+ if (proto == PPP_COMP) {
+ int obuff_size;
+
+ switch(ppp->rcomp->compress_proto) {
+ case CI_MPPE:
+ obuff_size = ppp->mru + PPP_HDRLEN + 1;
+ break;
+ default:
+ obuff_size = ppp->mru + PPP_HDRLEN;
+ break;
+ }
+
+ ns = dev_alloc_skb(obuff_size);
+ if (!ns) {
+ printk(KERN_ERR "ppp_decompress_frame: no memory\n");
+ goto err;
+ }
+ /* the decompressor still expects the A/C bytes in the hdr */
+ len = ppp->rcomp->decompress(ppp->rc_state, skb->data - 2,
+ skb->len + 2, ns->data, obuff_size);
+ if (len < 0) {
+ /* Pass the compressed frame to pppd as an
+ error indication. */
+ if (len == DECOMP_FATALERROR)
+ ppp->rstate |= SC_DC_FERROR;
+ kfree_skb(ns);
+ goto err;
+ }
+
+ kfree_skb(skb);
+ skb = ns;
+ skb_put(skb, len);
+ skb_pull(skb, 2); /* pull off the A/C bytes */
+
+ } else {
+ /* Uncompressed frame - pass to decompressor so it
+ can update its dictionary if necessary. */
+ if (ppp->rcomp->incomp)
+ ppp->rcomp->incomp(ppp->rc_state, skb->data - 2,
+ skb->len + 2);
+ }
+
+ return skb;
+
+ err:
+ ppp->rstate |= SC_DC_ERROR;
+ ppp_receive_error(ppp);
+ return skb;
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Receive a multilink frame.
+ * We put it on the reconstruction queue and then pull off
+ * as many completed frames as we can.
+ */
+static void
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+ u32 mask, seq;
+ struct channel *ch;
+ int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+
+ if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0)
+ goto err; /* no good, throw it away */
+
+ /* Decode sequence number and begin/end bits */
+ if (ppp->flags & SC_MP_SHORTSEQ) {
+ seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
+ mask = 0xfff;
+ } else {
+ seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
+ mask = 0xffffff;
+ }
+ skb->BEbits = skb->data[2];
+ skb_pull(skb, mphdrlen); /* pull off PPP and MP headers */
+
+ /*
+ * Do protocol ID decompression on the first fragment of each packet.
+ */
+ if ((skb->BEbits & B) && (skb->data[0] & 1))
+ *skb_push(skb, 1) = 0;
+
+ /*
+ * Expand sequence number to 32 bits, making it as close
+ * as possible to ppp->minseq.
+ */
+ seq |= ppp->minseq & ~mask;
+ if ((int)(ppp->minseq - seq) > (int)(mask >> 1))
+ seq += mask + 1;
+ else if ((int)(seq - ppp->minseq) > (int)(mask >> 1))
+ seq -= mask + 1; /* should never happen */
+ skb->sequence = seq;
+ pch->lastseq = seq;
+
+ /*
+ * If this packet comes before the next one we were expecting,
+ * drop it.
+ */
+ if (seq_before(seq, ppp->nextseq)) {
+ kfree_skb(skb);
+ ++ppp->dev->stats.rx_dropped;
+ ppp_receive_error(ppp);
+ return;
+ }
+
+ /*
+ * Reevaluate minseq, the minimum over all channels of the
+ * last sequence number received on each channel. Because of
+ * the increasing sequence number rule, we know that any fragment
+ * before `minseq' which hasn't arrived is never going to arrive.
+ * The list of channels can't change because we have the receive
+ * side of the ppp unit locked.
+ */
+ list_for_each_entry(ch, &ppp->channels, clist) {
+ if (seq_before(ch->lastseq, seq))
+ seq = ch->lastseq;
+ }
+ if (seq_before(ppp->minseq, seq))
+ ppp->minseq = seq;
+
+ /* Put the fragment on the reconstruction queue */
+ ppp_mp_insert(ppp, skb);
+
+ /* If the queue is getting long, don't wait any longer for packets
+ before the start of the queue. */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN
+ && seq_before(ppp->minseq, ppp->mrq.next->sequence))
+ ppp->minseq = ppp->mrq.next->sequence;
+ #else
+ if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN) {
+ struct sk_buff *skb = skb_peek(&ppp->mrq);
+ if (seq_before(ppp->minseq, skb->sequence))
+ ppp->minseq = skb->sequence;
+ }
+ #endif
+
+ /* Pull completed packets off the queue and receive them. */
+ while ((skb = ppp_mp_reconstruct(ppp)))
+ ppp_receive_nonmp_frame(ppp, skb);
+
+ return;
+
+ err:
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
+}
+
+/*
+ * Insert a fragment on the MP reconstruction queue.
+ * The queue is ordered by increasing sequence number.
+ */
+static void
+ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
+{
+ struct sk_buff *p;
+ struct sk_buff_head *list = &ppp->mrq;
+ u32 seq = skb->sequence;
+
+ /* N.B. we don't need to lock the list lock because we have the
+ ppp unit receive-side lock. */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ for (p = list->next; p != (struct sk_buff *)list; p = p->next) {
+ #else
+ skb_queue_walk(list, p) {
+ #endif
+ if (seq_before(seq, p->sequence))
+ break;
+ __skb_insert(skb, p->prev, p, list);
+ }
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+ __skb_queue_before(list, p, skb);
+ #endif
+}
+
+/*
+ * Reconstruct a packet from the MP fragment queue.
+ * We go through increasing sequence numbers until we find a
+ * complete packet, or we get to the sequence number for a fragment
+ * which hasn't arrived but might still do so.
+ */
+static struct sk_buff *
+ppp_mp_reconstruct(struct ppp *ppp)
+{
+ u32 seq = ppp->nextseq;
+ u32 minseq = ppp->minseq;
+ struct sk_buff_head *list = &ppp->mrq;
+ struct sk_buff *p, *next;
+ struct sk_buff *head, *tail;
+ struct sk_buff *skb = NULL;
+ int lost = 0, len = 0;
+
+ if (ppp->mrru == 0) /* do nothing until mrru is set */
+ return NULL;
+ head = list->next;
+ tail = NULL;
+ for (p = head; p != (struct sk_buff *) list; p = next) {
+ next = p->next;
+ if (seq_before(p->sequence, seq)) {
+ /* this can't happen, anyway ignore the skb */
+ printk(KERN_ERR "ppp_mp_reconstruct bad seq %u < %u\n",
+ p->sequence, seq);
+ head = next;
+ continue;
+ }
+ if (p->sequence != seq) {
+ /* Fragment `seq' is missing. If it is after
+ minseq, it might arrive later, so stop here. */
+ if (seq_after(seq, minseq))
+ break;
+ /* Fragment `seq' is lost, keep going. */
+ lost = 1;
+ seq = seq_before(minseq, p->sequence)?
+ minseq + 1: p->sequence;
+ next = p;
+ continue;
+ }
+
+ /*
+ * At this point we know that all the fragments from
+ * ppp->nextseq to seq are either present or lost.
+ * Also, there are no complete packets in the queue
+ * that have no missing fragments and end before this
+ * fragment.
+ */
+
+ /* B bit set indicates this fragment starts a packet */
+ if (p->BEbits & B) {
+ head = p;
+ lost = 0;
+ len = 0;
+ }
+
+ len += p->len;
+
+ /* Got a complete packet yet? */
+ if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
+ if (len > ppp->mrru + 2) {
+ ++ppp->dev->stats.rx_length_errors;
+ printk(KERN_DEBUG "PPP: reconstructed packet"
+ " is too long (%d)\n", len);
+ } else if (p == head) {
+ /* fragment is complete packet - reuse skb */
+ tail = p;
+ skb = skb_get(p);
+ break;
+ } else if ((skb = dev_alloc_skb(len)) == NULL) {
+ ++ppp->dev->stats.rx_missed_errors;
+ printk(KERN_DEBUG "PPP: no memory for "
+ "reconstructed packet");
+ } else {
+ tail = p;
+ break;
+ }
+ ppp->nextseq = seq + 1;
+ }
+
+ /*
+ * If this is the ending fragment of a packet,
+ * and we haven't found a complete valid packet yet,
+ * we can discard up to and including this fragment.
+ */
+ if (p->BEbits & E)
+ head = next;
+
+ ++seq;
+ }
+
+ /* If we have a complete packet, copy it all into one skb. */
+ if (tail != NULL) {
+ /* If we have discarded any fragments,
+ signal a receive error. */
+ if (head->sequence != ppp->nextseq) {
+ if (ppp->debug & 1)
+ printk(KERN_DEBUG " missed pkts %u..%u\n",
+ ppp->nextseq, head->sequence-1);
+ ++ppp->dev->stats.rx_dropped;
+ ppp_receive_error(ppp);
+ }
+
+ if (head != tail)
+ /* copy to a single skb */
+ for (p = head; p != tail->next; p = p->next)
+ skb_copy_bits(p, 0, skb_put(skb, p->len), p->len);
+ ppp->nextseq = tail->sequence + 1;
+ head = tail->next;
+ }
+
+ /* Discard all the skbuffs that we have copied the data out of
+ or that we can't use. */
+ while ((p = list->next) != head) {
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ }
+
+ return skb;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Channel interface.
+ */
+
+/*
+ * Create a new, unattached ppp channel.
+ */
+int
+ppp_register_channel(struct ppp_channel *chan)
+{
+ struct channel *pch;
+
+ pch = kzalloc(sizeof(struct channel), GFP_KERNEL);
+ if (!pch)
+ return -ENOMEM;
+ pch->ppp = NULL;
+ pch->chan = chan;
+ chan->ppp = pch;
+ init_ppp_file(&pch->file, CHANNEL);
+ pch->file.hdrlen = chan->hdrlen;
+ spin_lock_init(&pch->work.lock);
+ pch->work.pch=pch;
+ skb_queue_head_init(&pch->rq);
+#ifdef CONFIG_PPP_MULTILINK
+ pch->lastseq = -1;
+#endif /* CONFIG_PPP_MULTILINK */
+ init_rwsem(&pch->chan_sem);
+ spin_lock_init(&pch->downl);
+ rwlock_init(&pch->upl);
+ spin_lock_bh(&all_channels_lock);
+ pch->file.index = ++last_channel_index;
+ list_add(&pch->list, &new_channels);
+ atomic_inc(&channel_count);
+ spin_unlock_bh(&all_channels_lock);
+ return 0;
+}
+
+/*
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch)
+ return pch->file.index;
+ return -1;
+}
+
+/*
+ * Return the PPP unit number to which a channel is connected.
+ */
+int ppp_unit_number(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+ int unit = -1;
+
+ if (pch) {
+ read_lock_bh(&pch->upl);
+ if (pch->ppp)
+ unit = pch->ppp->file.index;
+ read_unlock_bh(&pch->upl);
+ }
+ return unit;
+}
+
+/*
+ * Disconnect a channel from the generic layer.
+ * This must be called in process context.
+ */
+void
+ppp_unregister_channel(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ if (!pch)
+ return; /* should never happen */
+ chan->ppp = NULL;
+
+ /*
+ * This ensures that we have returned from any calls into the
+ * the channel's start_xmit or ioctl routine before we proceed.
+ */
+ down_write(&pch->chan_sem);
+ spin_lock_bh(&pch->downl);
+ pch->chan = NULL;
+ spin_unlock_bh(&pch->downl);
+ up_write(&pch->chan_sem);
+ ppp_disconnect_channel(pch);
+ spin_lock_bh(&all_channels_lock);
+ list_del(&pch->list);
+ spin_unlock_bh(&all_channels_lock);
+ pch->file.dead = 1;
+ wake_up_interruptible(&pch->file.rwait);
+ if (atomic_dec_and_test(&pch->file.refcnt))
+ ppp_destroy_channel(pch);
+}
+
+/*
+ * Callback from a channel when it can accept more to transmit.
+ * This should be called at BH/softirq level, not interrupt level.
+ */
+void
+ppp_output_wakeup(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ if (!pch)
+ return;
+ ppp_channel_push(pch);
+}
+
+/*
+ * Compression control.
+ */
+
+/* Process the PPPIOCSCOMPRESS ioctl. */
+static int
+ppp_set_compress(struct ppp *ppp, unsigned long arg)
+{
+ int err;
+ struct compressor *cp, *ocomp;
+ struct ppp_option_data data;
+ void *state, *ostate;
+ unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
+
+ err = -EFAULT;
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))
+ || (data.length <= CCP_MAX_OPTION_LENGTH
+ && copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
+ goto out;
+ err = -EINVAL;
+ if (data.length > CCP_MAX_OPTION_LENGTH
+ || ccp_option[1] < 2 || ccp_option[1] > data.length)
+ goto out;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ cp = find_compressor(ccp_option[0]);
+#ifdef CONFIG_KMOD
+ if (!cp) {
+ request_module("ppp-compress-%d", ccp_option[0]);
+ cp = find_compressor(ccp_option[0]);
+ }
+#endif /* CONFIG_KMOD */
+ #else
+ cp = try_then_request_module(
+ find_compressor(ccp_option[0]),
+ "ppp-compress-%d", ccp_option[0]);
+ #endif
+ if (!cp)
+ goto out;
+
+ err = -ENOBUFS;
+ if (data.transmit) {
+ state = cp->comp_alloc(ccp_option, data.length);
+ if (state) {
+ ppp_xmit_lock(ppp);
+ ppp->xstate &= ~SC_COMP_RUN;
+ ocomp = ppp->xcomp;
+ ostate = ppp->xc_state;
+ ppp->xcomp = cp;
+ ppp->xc_state = state;
+ ppp_xmit_unlock(ppp);
+ if (ostate) {
+ ocomp->comp_free(ostate);
+ module_put(ocomp->owner);
+ }
+ err = 0;
+ } else
+ module_put(cp->owner);
+
+ } else {
+ state = cp->decomp_alloc(ccp_option, data.length);
+ if (state) {
+ ppp_recv_lock(ppp);
+ ppp->rstate &= ~SC_DECOMP_RUN;
+ ocomp = ppp->rcomp;
+ ostate = ppp->rc_state;
+ ppp->rcomp = cp;
+ ppp->rc_state = state;
+ ppp_recv_unlock(ppp);
+ if (ostate) {
+ ocomp->decomp_free(ostate);
+ module_put(ocomp->owner);
+ }
+ err = 0;
+ } else
+ module_put(cp->owner);
+ }
+
+ out:
+ return err;
+}
+
+/*
+ * Look at a CCP packet and update our state accordingly.
+ * We assume the caller has the xmit or recv path locked.
+ */
+static void
+ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
+{
+ unsigned char *dp;
+ int len;
+
+ if (!pskb_may_pull(skb, CCP_HDRLEN + 2))
+ return; /* no header */
+ dp = skb->data + 2;
+
+ switch (CCP_CODE(dp)) {
+ case CCP_CONFREQ:
+
+ /* A ConfReq starts negotiation of compression
+ * in one direction of transmission,
+ * and hence brings it down...but which way?
+ *
+ * Remember:
+ * A ConfReq indicates what the sender would like to receive
+ */
+ if(inbound)
+ /* He is proposing what I should send */
+ ppp->xstate &= ~SC_COMP_RUN;
+ else
+ /* I am proposing to what he should send */
+ ppp->rstate &= ~SC_DECOMP_RUN;
+
+ break;
+
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ /*
+ * CCP is going down, both directions of transmission
+ */
+ ppp->rstate &= ~SC_DECOMP_RUN;
+ ppp->xstate &= ~SC_COMP_RUN;
+ break;
+
+ case CCP_CONFACK:
+ if ((ppp->flags & (SC_CCP_OPEN | SC_CCP_UP)) != SC_CCP_OPEN)
+ break;
+ len = CCP_LENGTH(dp);
+ if (!pskb_may_pull(skb, len + 2))
+ return; /* too short */
+ dp += CCP_HDRLEN;
+ len -= CCP_HDRLEN;
+ if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp))
+ break;
+ if (inbound) {
+ /* we will start receiving compressed packets */
+ if (!ppp->rc_state)
+ break;
+ if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
+ ppp->file.index, 0, ppp->mru, ppp->debug)) {
+ ppp->rstate |= SC_DECOMP_RUN;
+ ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
+ }
+ } else {
+ /* we will soon start sending compressed packets */
+ if (!ppp->xc_state)
+ break;
+ if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
+ ppp->file.index, 0, ppp->debug))
+ ppp->xstate |= SC_COMP_RUN;
+ }
+ break;
+
+ case CCP_RESETACK:
+ /* reset the [de]compressor */
+ if ((ppp->flags & SC_CCP_UP) == 0)
+ break;
+ if (inbound) {
+ if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)) {
+ ppp->rcomp->decomp_reset(ppp->rc_state);
+ ppp->rstate &= ~SC_DC_ERROR;
+ }
+ } else {
+ if (ppp->xc_state && (ppp->xstate & SC_COMP_RUN))
+ ppp->xcomp->comp_reset(ppp->xc_state);
+ }
+ break;
+ }
+}
+
+/* Free up compression resources. */
+static void
+ppp_ccp_closed(struct ppp *ppp)
+{
+ void *xstate, *rstate;
+ struct compressor *xcomp, *rcomp;
+
+ ppp_lock(ppp);
+ ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
+ ppp->xstate = 0;
+ xcomp = ppp->xcomp;
+ xstate = ppp->xc_state;
+ ppp->xc_state = NULL;
+ ppp->rstate = 0;
+ rcomp = ppp->rcomp;
+ rstate = ppp->rc_state;
+ ppp->rc_state = NULL;
+ ppp_unlock(ppp);
+
+ if (xstate) {
+ xcomp->comp_free(xstate);
+ module_put(xcomp->owner);
+ }
+ if (rstate) {
+ rcomp->decomp_free(rstate);
+ module_put(rcomp->owner);
+ }
+}
+
+/* List of compressors. */
+static LIST_HEAD(compressor_list);
+static DEFINE_SPINLOCK(compressor_list_lock);
+
+struct compressor_entry {
+ struct list_head list;
+ struct compressor *comp;
+};
+
+static struct compressor_entry *
+find_comp_entry(int proto)
+{
+ struct compressor_entry *ce;
+
+ list_for_each_entry(ce, &compressor_list, list) {
+ if (ce->comp->compress_proto == proto)
+ return ce;
+ }
+ return NULL;
+}
+
+/* Register a compressor */
+int
+ppp_register_compressor(struct compressor *cp)
+{
+ struct compressor_entry *ce;
+ int ret;
+ spin_lock(&compressor_list_lock);
+ ret = -EEXIST;
+ if (find_comp_entry(cp->compress_proto))
+ goto out;
+ ret = -ENOMEM;
+ ce = kmalloc(sizeof(struct compressor_entry), GFP_ATOMIC);
+ if (!ce)
+ goto out;
+ ret = 0;
+ ce->comp = cp;
+ list_add(&ce->list, &compressor_list);
+ out:
+ spin_unlock(&compressor_list_lock);
+ return ret;
+}
+
+/* Unregister a compressor */
+void
+ppp_unregister_compressor(struct compressor *cp)
+{
+ struct compressor_entry *ce;
+
+ spin_lock(&compressor_list_lock);
+ ce = find_comp_entry(cp->compress_proto);
+ if (ce && ce->comp == cp) {
+ list_del(&ce->list);
+ kfree(ce);
+ }
+ spin_unlock(&compressor_list_lock);
+}
+
+/* Find a compressor. */
+static struct compressor *
+find_compressor(int type)
+{
+ struct compressor_entry *ce;
+ struct compressor *cp = NULL;
+
+ spin_lock(&compressor_list_lock);
+ ce = find_comp_entry(type);
+ if (ce) {
+ cp = ce->comp;
+ if (!try_module_get(cp->owner))
+ cp = NULL;
+ }
+ spin_unlock(&compressor_list_lock);
+ return cp;
+}
+
+/*
+ * Miscelleneous stuff.
+ */
+
+static void
+ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
+{
+ struct slcompress *vj = ppp->vj;
+
+ memset(st, 0, sizeof(*st));
+ st->p.ppp_ipackets = ppp->dev->stats.rx_packets;
+ st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
+ st->p.ppp_ibytes = ppp->dev->stats.rx_bytes;
+ st->p.ppp_opackets = ppp->dev->stats.tx_packets;
+ st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
+ st->p.ppp_obytes = ppp->dev->stats.tx_bytes;
+ if (!vj)
+ return;
+ st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
+ st->vj.vjs_compressed = vj->sls_o_compressed;
+ st->vj.vjs_searches = vj->sls_o_searches;
+ st->vj.vjs_misses = vj->sls_o_misses;
+ st->vj.vjs_errorin = vj->sls_i_error;
+ st->vj.vjs_tossed = vj->sls_i_tossed;
+ st->vj.vjs_uncompressedin = vj->sls_i_uncompressed;
+ st->vj.vjs_compressedin = vj->sls_i_compressed;
+}
+
+/*
+ * Stuff for handling the lists of ppp units and channels
+ * and for initialization.
+ */
+
+/*
+ * Create a new ppp interface unit. Fails if it can't allocate memory
+ * or if there is already a unit with the requested number.
+ * unit == -1 means allocate a new number.
+ */
+static struct ppp *
+ppp_create_interface(int unit, int *retp)
+{
+ struct ppp *ppp;
+ struct net_device *dev = NULL;
+ int ret = -ENOMEM;
+ int i;
+
+ ppp = kzalloc(sizeof(struct ppp), GFP_KERNEL);
+ if (!ppp)
+ goto out;
+ dev = alloc_netdev(0, "", ppp_setup);
+ if (!dev)
+ goto out1;
+
+ ppp->mru = PPP_MRU;
+ init_ppp_file(&ppp->file, INTERFACE);
+ ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */
+ for (i = 0; i < NUM_NP; ++i)
+ ppp->npmode[i] = NPMODE_PASS;
+ INIT_LIST_HEAD(&ppp->channels);
+ spin_lock_init(&ppp->rlock);
+ spin_lock_init(&ppp->wlock);
+ spin_lock_init(&ppp->work.lock);
+ ppp->work.ppp=ppp;
+#ifdef CONFIG_PPP_MULTILINK
+ ppp->minseq = -1;
+ skb_queue_head_init(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
+ ppp->dev = dev;
+ dev->priv = ppp;
+
+ dev->hard_start_xmit = ppp_start_xmit;
+ dev->do_ioctl = ppp_net_ioctl;
+
+ ret = -EEXIST;
+ mutex_lock(&all_ppp_mutex);
+ if (unit < 0)
+ unit = cardmap_find_first_free(all_ppp_units);
+ else if (cardmap_get(all_ppp_units, unit) != NULL)
+ goto out2; /* unit already exists */
+
+ /* Initialize the new ppp unit */
+ ppp->file.index = unit;
+ sprintf(dev->name, "ppp%d", unit);
+
+ ret = register_netdev(dev);
+ if (ret != 0) {
+ printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
+ dev->name, ret);
+ goto out2;
+ }
+
+ atomic_inc(&ppp_unit_count);
+ ret = cardmap_set(&all_ppp_units, unit, ppp);
+ if (ret != 0)
+ goto out3;
+
+ mutex_unlock(&all_ppp_mutex);
+ *retp = 0;
+ return ppp;
+
+out3:
+ atomic_dec(&ppp_unit_count);
+ unregister_netdev(dev);
+out2:
+ mutex_unlock(&all_ppp_mutex);
+ free_netdev(dev);
+out1:
+ kfree(ppp);
+out:
+ *retp = ret;
+ return NULL;
+}
+
+/*
+ * Initialize a ppp_file structure.
+ */
+static void
+init_ppp_file(struct ppp_file *pf, int kind)
+{
+ pf->kind = kind;
+ skb_queue_head_init(&pf->xq);
+ skb_queue_head_init(&pf->rq);
+ atomic_set(&pf->refcnt, 1);
+ init_waitqueue_head(&pf->rwait);
+}
+
+/*
+ * Take down a ppp interface unit - called when the owning file
+ * (the one that created the unit) is closed or detached.
+ */
+static void ppp_shutdown_interface(struct ppp *ppp)
+{
+ mutex_lock(&all_ppp_mutex);
+ ppp_lock(ppp);
+ if (!ppp->closing) {
+ ppp->closing = 1;
+ ppp_unlock(ppp);
+ unregister_netdev(ppp->dev);
+ } else
+ ppp_unlock(ppp);
+ /* This will call dev_close() for us. */
+ cardmap_set(&all_ppp_units, ppp->file.index, NULL);
+ ppp->file.dead = 1;
+ ppp->owner = NULL;
+ wake_up_interruptible(&ppp->file.rwait);
+ mutex_unlock(&all_ppp_mutex);
+}
+
+/*
+ * Free the memory used by a ppp unit. This is only called once
+ * there are no channels connected to the unit and no file structs
+ * that reference the unit.
+ */
+static void ppp_destroy_interface(struct ppp *ppp)
+{
+ atomic_dec(&ppp_unit_count);
+
+ if (!ppp->file.dead || ppp->n_channels) {
+ /* "can't happen" */
+ printk(KERN_ERR "ppp: destroying ppp struct %p but dead=%d "
+ "n_channels=%d !\n", ppp, ppp->file.dead,
+ ppp->n_channels);
+ return;
+ }
+
+ ppp_ccp_closed(ppp);
+ if (ppp->vj) {
+ slhc_free(ppp->vj);
+ ppp->vj = NULL;
+ }
+ spin_lock_bh(&ppp->work.lock);
+ if (ppp->work.queued) {
+ spin_lock_bh(&ppp_wq_lock);
+ list_del(&ppp->work.entry);
+ spin_unlock_bh(&ppp_wq_lock);
+ }
+ spin_unlock_bh(&ppp->work.lock);
+ skb_queue_purge(&ppp->file.xq);
+ skb_queue_purge(&ppp->file.rq);
+#ifdef CONFIG_PPP_MULTILINK
+ skb_queue_purge(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+ kfree(ppp->pass_filter);
+ ppp->pass_filter = NULL;
+ kfree(ppp->active_filter);
+ ppp->active_filter = NULL;
+#endif /* CONFIG_PPP_FILTER */
+
+ if (ppp->xmit_pending)
+ kfree_skb(ppp->xmit_pending);
+
+ kfree(ppp);
+ free_netdev(ppp->dev);
+}
+
+/*
+ * Locate an existing ppp unit.
+ * The caller should have locked the all_ppp_mutex.
+ */
+static struct ppp *
+ppp_find_unit(int unit)
+{
+ return cardmap_get(all_ppp_units, unit);
+}
+
+/*
+ * Locate an existing ppp channel.
+ * The caller should have locked the all_channels_lock.
+ * First we look in the new_channels list, then in the
+ * all_channels list. If found in the new_channels list,
+ * we move it to the all_channels list. This is for speed
+ * when we have a lot of channels in use.
+ */
+static struct channel *
+ppp_find_channel(int unit)
+{
+ struct channel *pch;
+
+ list_for_each_entry(pch, &new_channels, list) {
+ if (pch->file.index == unit) {
+ list_move(&pch->list, &all_channels);
+ return pch;
+ }
+ }
+ list_for_each_entry(pch, &all_channels, list) {
+ if (pch->file.index == unit)
+ return pch;
+ }
+ return NULL;
+}
+
+/*
+ * Connect a PPP channel to a PPP interface unit.
+ */
+static int
+ppp_connect_channel(struct channel *pch, int unit)
+{
+ struct ppp *ppp;
+ int ret = -ENXIO;
+ int hdrlen;
+
+ mutex_lock(&all_ppp_mutex);
+ ppp = ppp_find_unit(unit);
+ if (!ppp)
+ goto out;
+ write_lock_bh(&pch->upl);
+ ret = -EINVAL;
+ if (pch->ppp)
+ goto outl;
+
+ ppp_lock(ppp);
+ if (pch->file.hdrlen > ppp->file.hdrlen)
+ ppp->file.hdrlen = pch->file.hdrlen;
+ hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */
+ if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+ ppp->dev->hard_header_len = hdrlen;
+ list_add_tail(&pch->clist, &ppp->channels);
+ ++ppp->n_channels;
+ pch->ppp = ppp;
+ atomic_inc(&ppp->file.refcnt);
+ ppp_unlock(ppp);
+ ret = 0;
+
+ outl:
+ write_unlock_bh(&pch->upl);
+ out:
+ mutex_unlock(&all_ppp_mutex);
+ return ret;
+}
+
+/*
+ * Disconnect a channel from its ppp unit.
+ */
+static int
+ppp_disconnect_channel(struct channel *pch)
+{
+ struct ppp *ppp;
+ int err = -EINVAL;
+
+ write_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ pch->ppp = NULL;
+ write_unlock_bh(&pch->upl);
+ if (ppp) {
+ /* remove it from the ppp unit's list */
+ ppp_lock(ppp);
+ list_del(&pch->clist);
+ if (--ppp->n_channels == 0)
+ wake_up_interruptible(&ppp->file.rwait);
+ ppp_unlock(ppp);
+ if (atomic_dec_and_test(&ppp->file.refcnt))
+ ppp_destroy_interface(ppp);
+ err = 0;
+ }
+ return err;
+}
+
+/*
+ * Free up the resources used by a ppp channel.
+ */
+static void ppp_destroy_channel(struct channel *pch)
+{
+ atomic_dec(&channel_count);
+
+ if (!pch->file.dead) {
+ /* "can't happen" */
+ printk(KERN_ERR "ppp: destroying undead channel %p !\n",
+ pch);
+ return;
+ }
+ spin_lock_bh(&pch->work.lock);
+ if (pch->work.queued) {
+ spin_lock_bh(&ppp_wq_lock);
+ list_del(&pch->work.entry);
+ spin_unlock_bh(&ppp_wq_lock);
+ }
+ spin_unlock_bh(&pch->work.lock);
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
+ skb_queue_purge(&pch->rq);
+ kfree(pch);
+}
+
+static void __exit ppp_cleanup(void)
+{
+ /* should never happen */
+ if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
+ printk(KERN_ERR "PPP: removing module but units remain!\n");
+ stop_worker_threads();
+ cardmap_destroy(&all_ppp_units);
+ unregister_chrdev(PPP_MAJOR, "ppp");
+ device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
+ class_destroy(ppp_class);
+}
+
+/*
+ * Cardmap implementation.
+ */
+static void *cardmap_get(struct cardmap *map, unsigned int nr)
+{
+ struct cardmap *p;
+ int i;
+
+ for (p = map; p != NULL; ) {
+ if ((i = nr >> p->shift) >= CARDMAP_WIDTH)
+ return NULL;
+ if (p->shift == 0)
+ return p->ptr[i];
+ nr &= ~(CARDMAP_MASK << p->shift);
+ p = p->ptr[i];
+ }
+ return NULL;
+}
+
+static int cardmap_set(struct cardmap **pmap, unsigned int nr, void *ptr)
+{
+ struct cardmap *p;
+ int i;
+
+ p = *pmap;
+ if (p == NULL || (nr >> p->shift) >= CARDMAP_WIDTH) {
+ do {
+ /* need a new top level */
+ struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
+ if (!np)
+ goto enomem;
+ np->ptr[0] = p;
+ if (p != NULL) {
+ np->shift = p->shift + CARDMAP_ORDER;
+ p->parent = np;
+ } else
+ np->shift = 0;
+ p = np;
+ } while ((nr >> p->shift) >= CARDMAP_WIDTH);
+ *pmap = p;
+ }
+ while (p->shift > 0) {
+ i = (nr >> p->shift) & CARDMAP_MASK;
+ if (p->ptr[i] == NULL) {
+ struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
+ if (!np)
+ goto enomem;
+ np->shift = p->shift - CARDMAP_ORDER;
+ np->parent = p;
+ p->ptr[i] = np;
+ }
+ if (ptr == NULL)
+ clear_bit(i, &p->inuse);
+ p = p->ptr[i];
+ }
+ i = nr & CARDMAP_MASK;
+ p->ptr[i] = ptr;
+ if (ptr != NULL)
+ set_bit(i, &p->inuse);
+ else
+ clear_bit(i, &p->inuse);
+ return 0;
+ enomem:
+ return -ENOMEM;
+}
+
+static unsigned int cardmap_find_first_free(struct cardmap *map)
+{
+ struct cardmap *p;
+ unsigned int nr = 0;
+ int i;
+
+ if ((p = map) == NULL)
+ return 0;
+ for (;;) {
+ i = find_first_zero_bit(&p->inuse, CARDMAP_WIDTH);
+ if (i >= CARDMAP_WIDTH) {
+ if (p->parent == NULL)
+ return CARDMAP_WIDTH << p->shift;
+ p = p->parent;
+ i = (nr >> p->shift) & CARDMAP_MASK;
+ set_bit(i, &p->inuse);
+ continue;
+ }
+ nr = (nr & (~CARDMAP_MASK << p->shift)) | (i << p->shift);
+ if (p->shift == 0 || p->ptr[i] == NULL)
+ return nr;
+ p = p->ptr[i];
+ }
+}
+
+static void cardmap_destroy(struct cardmap **pmap)
+{
+ struct cardmap *p, *np;
+ int i;
+
+ for (p = *pmap; p != NULL; p = np) {
+ if (p->shift != 0) {
+ for (i = 0; i < CARDMAP_WIDTH; ++i)
+ if (p->ptr[i] != NULL)
+ break;
+ if (i < CARDMAP_WIDTH) {
+ np = p->ptr[i];
+ p->ptr[i] = NULL;
+ continue;
+ }
+ }
+ np = p->parent;
+ kfree(p);
+ }
+ *pmap = NULL;
+}
+
+static int worker_thread(void*p)
+{
+ struct ppp_work_head_t *wrk;
+ struct sk_buff *skb;
+ DEFINE_WAIT(wait);
+
+ set_user_nice(current,-5);
+
+ while(1)
+ {
+ prepare_to_wait(&ppp_wq_wait,&wait,TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&ppp_wq_lock);
+ if (list_empty(&ppp_work_queue)) {
+ spin_unlock_bh(&ppp_wq_lock);
+ if (!kthread_should_stop())
+ schedule();
+ finish_wait(&ppp_wq_wait,&wait);
+ } else {
+ wrk=list_entry(ppp_work_queue.next,typeof(*wrk),entry);
+ list_del(&wrk->entry);
+ spin_unlock_bh(&ppp_wq_lock);
+
+ finish_wait(&ppp_wq_wait,&wait);
+
+ if (wrk->pch) {
+ ppp_recv_lock(wrk->pch->ppp);
+ spin_lock_bh(&wrk->lock);
+ skb=skb_dequeue(&wrk->pch->rq);
+ if (!skb) wrk->queued=0;
+ spin_unlock_bh(&wrk->lock);
+
+ if (skb) {
+ if (wrk->pch->ppp->dev) {
+ ppp_receive_frame(wrk->pch->ppp, skb, wrk->pch);
+ } else
+ kfree_skb(skb);
+
+ spin_lock_bh(&ppp_wq_lock);
+ list_add_tail(&wrk->entry,&ppp_work_queue);
+ spin_unlock_bh(&ppp_wq_lock);
+ }
+ ppp_recv_unlock(wrk->pch->ppp);
+ } else {
+ int cont=1;
+ ppp_xmit_lock(wrk->ppp);
+ if (wrk->ppp->dev) {
+ if (wrk->ppp->xmit_pending) ppp_push(wrk->ppp);
+ else {
+ spin_lock_bh(&wrk->lock);
+ skb = skb_dequeue(&wrk->ppp->file.xq);
+ if (!skb) wrk->queued=0;
+ spin_unlock_bh(&wrk->lock);
+
+ if (skb) ppp_send_frame(wrk->ppp,skb);
+ else {
+ cont=0;
+ netif_wake_queue(wrk->ppp->dev);
+ }
+ }
+ } else cont=0;
+ ppp_xmit_unlock(wrk->ppp);
+
+ if (cont) {
+ spin_lock_bh(&ppp_wq_lock);
+ list_add_tail(&wrk->entry,&ppp_work_queue);
+ spin_unlock_bh(&ppp_wq_lock);
+ }
+ }
+ }
+
+ if (kthread_should_stop())
+ break;
+ }
+ return 0;
+}
+
+static int __ppp_queue_work(struct ppp_work_head_t *h)
+{
+ int wakeup=0;
+
+ if (!h->queued) {
+ spin_lock_bh(&ppp_wq_lock);
+ list_add_tail(&h->entry,&ppp_work_queue);
+ h->queued=1;
+ spin_unlock_bh(&ppp_wq_lock);
+ wakeup=1;
+ }
+
+ return wakeup;
+}
+
+static void ppp_queue_work(struct ppp_work_head_t *h)
+{
+ int wakeup;
+ spin_lock_bh(&h->lock);
+ wakeup=__ppp_queue_work(h);
+ spin_unlock_bh(&h->lock);
+ if (wakeup) wake_up_interruptible(&ppp_wq_wait);
+}
+
+static int run_worker_threads(void)
+{
+ struct worker_thread_t *t;
+ struct task_struct *p;
+ int i;
+
+ for(i=0; i<num_present_cpus(); i++) {
+ if (smp_affinity&(1<<i)) {
+ t=kzalloc(sizeof(*t),GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ t->task=p=kthread_create(worker_thread,NULL,"%s/%d","kpppd",i);
+ if (IS_ERR(t->task)) {
+ kfree(t);
+ return PTR_ERR(p);
+ }
+
+ kthread_bind(t->task,i);
+ wake_up_process(t->task);
+ list_add_tail(&t->entry,&worker_thread_list);
+ }
+ }
+ return 0;
+}
+
+static void stop_worker_threads(void)
+{
+ struct worker_thread_t *t;
+
+ while(!list_empty(&worker_thread_list)) {
+ t=list_entry(worker_thread_list.next,typeof(*t),entry);
+ list_del(&t->entry);
+ kthread_stop(t->task);
+ kfree(t);
+ }
+}
+
+/* Module/initialization stuff */
+
+module_init(ppp_init);
+module_exit(ppp_cleanup);
+
+EXPORT_SYMBOL(ppp_register_channel);
+EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_unit_number);
+EXPORT_SYMBOL(ppp_input);
+EXPORT_SYMBOL(ppp_input_error);
+EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(PPP_MAJOR);
+MODULE_ALIAS("/dev/ppp");
+module_param(smp_affinity,int,0);
+MODULE_PARM_DESC(smp_affinity,"cpus allowed to run packet processing (default 0xff)");
diff --git a/kernel/driver/pptp.c b/kernel/driver/pptp.c
new file mode 100644
index 0000000..a12a106
--- /dev/null
+++ b/kernel/driver/pptp.c
@@ -0,0 +1,1070 @@
+/*
+ * Point-to-Point Tunneling Protocol for Linux
+ *
+ * Authors: Kozlov D. (xeb@mail.ru)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/ppp_channel.h>
+#include <linux/ppp_defs.h>
+#include "if_pppox.h"
+#include <linux/if_ppp.h>
+#include <linux/notifier.h>
+#include <linux/file.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/version.h>
+#include <linux/spinlock.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/tqueue.h>
+#include <linux/timer.h>
+#include <asm/bitops.h>
+#else
+#include <linux/workqueue.h>
+#endif
+
+#include <net/sock.h>
+#include <net/protocol.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/route.h>
+
+#include <asm/uaccess.h>
+
+#define DEBUG
+
+#define PPTP_DRIVER_VERSION "0.8.3"
+
+MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol for Linux");
+MODULE_AUTHOR("Kozlov D. (xeb@mail.ru)");
+MODULE_LICENSE("GPL");
+
+static int log_level=0;
+static int log_packets=10;
+
+#define MAX_CALLID 65535
+
+static unsigned long *callid_bitmap=NULL;
+static struct pppox_sock **callid_sock=NULL;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+MODULE_PARM(log_level,"i");
+MODULE_PARM(log_packets,"i");
+#else
+module_param(log_level,int,0);
+module_param(log_packets,int,0);
+#endif
+MODULE_PARM_DESC(log_level,"Logging level (default=0)");
+
+#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define INIT_TIMER(_timer,_routine,_data) \
+do { \
+ (_timer)->function=_routine; \
+ (_timer)->data=_data; \
+ init_timer(_timer); \
+} while (0);
+
+static inline void *kzalloc(size_t size,int gfp)
+{
+ void *p=kmalloc(size,gfp);
+ memset(p,0,size);
+ return p;
+}
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static rwlock_t chan_lock=RW_LOCK_UNLOCKED;
+#define SK_STATE(sk) (sk)->state
+#else
+static DEFINE_RWLOCK(chan_lock);
+#define SK_STATE(sk) (sk)->sk_state
+#endif
+
+static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb);
+static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg);
+static int pptp_rcv_core(struct sock *sk,struct sk_buff *skb);
+
+static struct ppp_channel_ops pptp_chan_ops= {
+ .start_xmit = pptp_xmit,
+ .ioctl=pptp_ppp_ioctl,
+};
+
+
+#define MISSING_WINDOW 20
+#define WRAPPED( curseq, lastseq) \
+ ((((curseq) & 0xffffff00) == 0) && \
+ (((lastseq) & 0xffffff00 ) == 0xffffff00))
+
+/* gre header structure: -------------------------------------------- */
+
+#define PPTP_GRE_PROTO 0x880B
+#define PPTP_GRE_VER 0x1
+
+#define PPTP_GRE_FLAG_C 0x80
+#define PPTP_GRE_FLAG_R 0x40
+#define PPTP_GRE_FLAG_K 0x20
+#define PPTP_GRE_FLAG_S 0x10
+#define PPTP_GRE_FLAG_A 0x80
+
+#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
+#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
+#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
+#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
+#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)
+
+struct pptp_gre_header {
+ u_int8_t flags; /* bitfield */
+ u_int8_t ver; /* should be PPTP_GRE_VER (enhanced GRE) */
+ u_int16_t protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */
+ u_int16_t payload_len; /* size of ppp payload, not inc. gre header */
+ u_int16_t call_id; /* peer's call_id for this session */
+ u_int32_t seq; /* sequence number. Present if S==1 */
+ u_int32_t ack; /* seq number of highest packet recieved by */
+ /* sender in this session */
+};
+#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static struct pppox_sock * lookup_chan(__u16 call_id, __u32 s_addr)
+#else
+static struct pppox_sock * lookup_chan(__u16 call_id, __be32 s_addr)
+#endif
+{
+ struct pppox_sock *sock;
+ struct pptp_opt *opt;
+
+ read_lock(&chan_lock);
+ sock=callid_sock[call_id];
+ if (sock) {
+ opt=&sock->proto.pptp;
+ if (opt->dst_addr.sin_addr.s_addr!=s_addr) sock=NULL;
+ else sock_hold(sk_pppox(sock));
+ }
+ read_unlock(&chan_lock);
+
+ return sock;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static int lookup_chan_dst(__u16 call_id, __u32 d_addr)
+#else
+static int lookup_chan_dst(__u16 call_id, __be32 d_addr)
+#endif
+{
+ struct pppox_sock *sock;
+ struct pptp_opt *opt;
+ int i;
+
+ read_lock(&chan_lock);
+ for(i=find_next_bit(callid_bitmap,MAX_CALLID,1); i<MAX_CALLID; i=find_next_bit(callid_bitmap,MAX_CALLID,i+1)){
+ sock=callid_sock[i];
+ opt=&sock->proto.pptp;
+ if (opt->dst_addr.call_id==call_id && opt->dst_addr.sin_addr.s_addr==d_addr) break;
+ }
+ read_unlock(&chan_lock);
+
+ return i<MAX_CALLID;
+}
+
+static int add_chan(struct pppox_sock *sock)
+{
+ static int call_id=0;
+ int res=-1;
+
+ write_lock_bh(&chan_lock);
+
+ if (!sock->proto.pptp.src_addr.call_id)
+ {
+ call_id=find_next_zero_bit(callid_bitmap,MAX_CALLID,call_id+1);
+ if (call_id==MAX_CALLID)
+ call_id=find_next_zero_bit(callid_bitmap,MAX_CALLID,1);
+ sock->proto.pptp.src_addr.call_id=call_id;
+ }
+ else if (test_bit(sock->proto.pptp.src_addr.call_id,callid_bitmap))
+ goto exit;
+
+ set_bit(sock->proto.pptp.src_addr.call_id,callid_bitmap);
+ callid_sock[sock->proto.pptp.src_addr.call_id]=sock;
+ res=0;
+
+exit:
+ write_unlock_bh(&chan_lock);
+
+ return res;
+}
+
+static void del_chan(struct pppox_sock *sock)
+{
+ write_lock_bh(&chan_lock);
+ clear_bit(sock->proto.pptp.src_addr.call_id,callid_bitmap);
+ callid_sock[sock->proto.pptp.src_addr.call_id]=NULL;
+ write_unlock_bh(&chan_lock);
+}
+
+static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+{
+ struct sock *sk = (struct sock *) chan->private;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct pptp_opt *opt=&po->proto.pptp;
+ struct pptp_gre_header *hdr;
+ unsigned int header_len=sizeof(*hdr);
+ int err=0;
+ int islcp;
+ int len;
+ unsigned char *data;
+ __u32 seq_recv;
+
+
+ struct rtable *rt; /* Route to the other host */
+ struct net_device *tdev; /* Device to other host */
+ struct iphdr *iph; /* Our new IP header */
+ int max_headroom; /* The extra header space needed */
+
+ if (SK_STATE(sk_pppox(po)) & PPPOX_DEAD)
+ goto tx_error;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ {
+ struct rt_key key = {
+ .dst=opt->dst_addr.sin_addr.s_addr,
+ .src=opt->src_addr.sin_addr.s_addr,
+ .tos=RT_TOS(0),
+ };
+ if ((err=ip_route_output_key(&rt, &key))) {
+ goto tx_error;
+ }
+ }
+ #else
+ {
+ struct flowi fl = { .oif = 0,
+ .nl_u = { .ip4_u =
+ { .daddr = opt->dst_addr.sin_addr.s_addr,
+ .saddr = opt->src_addr.sin_addr.s_addr,
+ .tos = RT_TOS(0) } },
+ .proto = IPPROTO_GRE };
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ if ((err=ip_route_output_key(&rt, &fl))) {
+ #else
+ if ((err=ip_route_output_key(&init_net,&rt, &fl))) {
+ #endif
+ goto tx_error;
+ }
+ }
+ #endif
+ tdev = rt->u.dst.dev;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ max_headroom = ((tdev->hard_header_len+15)&~15) + sizeof(*iph)+sizeof(*hdr)+2;
+ #else
+ max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph)+sizeof(*hdr)+2;
+ #endif
+
+
+ if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+ if (!new_skb) {
+ ip_rt_put(rt);
+ goto tx_error;
+ }
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
+ kfree_skb(skb);
+ skb = new_skb;
+ }
+
+ data=skb->data;
+ islcp=((data[0] << 8) + data[1])== PPP_LCP && 1 <= data[2] && data[2] <= 7;
+
+ /* compress protocol field */
+ if ((opt->ppp_flags & SC_COMP_PROT) && data[0]==0 && !islcp)
+ skb_pull(skb,1);
+
+ /*
+ * Put in the address/control bytes if necessary
+ */
+ if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) {
+ data=skb_push(skb,2);
+ data[0]=0xff;
+ data[1]=0x03;
+ }
+
+ len=skb->len;
+
+ seq_recv = opt->seq_recv;
+
+ if (opt->ack_sent == seq_recv) header_len-=sizeof(hdr->ack);
+
+ // Push down and install GRE header
+ skb_push(skb,header_len);
+ hdr=(struct pptp_gre_header *)(skb->data);
+
+ hdr->flags = PPTP_GRE_FLAG_K;
+ hdr->ver = PPTP_GRE_VER;
+ hdr->protocol = htons(PPTP_GRE_PROTO);
+ hdr->call_id = htons(opt->dst_addr.call_id);
+
+ hdr->flags |= PPTP_GRE_FLAG_S;
+ hdr->seq = htonl(++opt->seq_sent);
+ #ifdef DEBUG
+ if (log_level>=3 && opt->seq_sent<=log_packets)
+ printk(KERN_INFO"PPTP[%i]: send packet: seq=%i",opt->src_addr.call_id,opt->seq_sent);
+ #endif
+ if (opt->ack_sent != seq_recv) {
+ /* send ack with this message */
+ hdr->ver |= PPTP_GRE_FLAG_A;
+ hdr->ack = htonl(seq_recv);
+ opt->ack_sent = seq_recv;
+ #ifdef DEBUG
+ if (log_level>=3 && opt->seq_sent<=log_packets)
+ printk(" ack=%i",seq_recv);
+ #endif
+ }
+ hdr->payload_len = htons(len);
+ #ifdef DEBUG
+ if (log_level>=3 && opt->seq_sent<=log_packets)
+ printk("\n");
+ #endif
+
+ /*
+ * Push down and install the IP header.
+ */
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ skb->transport_header = skb->network_header;
+ skb_push(skb, sizeof(*iph));
+ skb_reset_network_header(skb);
+ #else
+ skb->h.raw = skb->nh.raw;
+ skb->nh.raw = skb_push(skb, sizeof(*iph));
+ #endif
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+ IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
+ IPSKB_REROUTED);
+ #endif
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ iph = ip_hdr(skb);
+ #else
+ iph = skb->nh.iph;
+ #endif
+ iph->version = 4;
+ iph->ihl = sizeof(struct iphdr) >> 2;
+ iph->frag_off = 0;//df;
+ iph->protocol = IPPROTO_GRE;
+ iph->tos = 0;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ iph->ttl = sysctl_ip_default_ttl;
+ #else
+ iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
+ #endif
+ iph->tot_len = htons(skb->len);
+
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
+
+ nf_reset(skb);
+
+ skb->ip_summed = CHECKSUM_NONE;
+ ip_select_ident(iph, &rt->u.dst, NULL);
+ ip_send_check(iph);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, ip_send);
+ #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output);
+ #else
+ err = ip_local_out(skb);
+ #endif
+
+tx_error:
+ return 1;
+}
+
+static int pptp_rcv_core(struct sock *sk,struct sk_buff *skb)
+{
+ struct pppox_sock *po = pppox_sk(sk);
+ struct pptp_opt *opt=&po->proto.pptp;
+ int headersize,payload_len,seq;
+ __u8 *payload;
+ struct pptp_gre_header *header;
+
+ if (!(SK_STATE(sk) & PPPOX_CONNECTED)) {
+ if (sock_queue_rcv_skb(sk, skb))
+ goto drop;
+ return NET_RX_SUCCESS;
+ }
+
+ header = (struct pptp_gre_header *)(skb->data);
+
+ /* test if acknowledgement present */
+ if (PPTP_GRE_IS_A(header->ver)){
+ __u32 ack = (PPTP_GRE_IS_S(header->flags))?
+ header->ack:header->seq; /* ack in different place if S = 0 */
+
+ ack = ntohl( ack);
+
+ if (ack > opt->ack_recv) opt->ack_recv = ack;
+ /* also handle sequence number wrap-around */
+ if (WRAPPED(ack,opt->ack_recv)) opt->ack_recv = ack;
+ }
+
+ /* test if payload present */
+ if (!PPTP_GRE_IS_S(header->flags)){
+ goto drop;
+ }
+
+ headersize = sizeof(*header);
+ payload_len = ntohs(header->payload_len);
+ seq = ntohl(header->seq);
+
+ /* no ack present? */
+ if (!PPTP_GRE_IS_A(header->ver)) headersize -= sizeof(header->ack);
+ /* check for incomplete packet (length smaller than expected) */
+ if (skb->len - headersize < payload_len){
+ #ifdef DEBUG
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP: discarding truncated packet (expected %d, got %d bytes)\n",
+ payload_len, skb->len - headersize);
+ #endif
+ goto drop;
+ }
+
+ payload=skb->data+headersize;
+ /* check for expected sequence number */
+ if ( seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq) ){
+ #ifdef DEBUG
+ if ( log_level >= 1)
+ printk(KERN_INFO"PPTP[%i]: discarding duplicate or old packet %d (expecting %d)\n",opt->src_addr.call_id,
+ seq, opt->seq_recv + 1);
+ #endif
+ }else{
+ #ifdef DEBUG
+ if ( log_level >= 3 && opt->seq_sent<=log_packets)
+ printk(KERN_INFO"PPTP[%i]: accepting packet %d size=%i (%02x %02x %02x %02x %02x %02x)\n",opt->src_addr.call_id, seq,payload_len,
+ *(payload +0),
+ *(payload +1),
+ *(payload +2),
+ *(payload +3),
+ *(payload +4),
+ *(payload +5));
+ #endif
+ opt->seq_recv = seq;
+
+ skb_pull(skb,headersize);
+
+ if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI){
+ /* chop off address/control */
+ if (skb->len < 3)
+ goto drop;
+ skb_pull(skb,2);
+ }
+
+ if ((*skb->data) & 1){
+ /* protocol is compressed */
+ skb_push(skb, 1)[0] = 0;
+ }
+
+ skb->ip_summed=CHECKSUM_NONE;
+ skb_set_network_header(skb,skb->head-skb->data);
+ ppp_input(&po->chan,skb);
+
+ return NET_RX_SUCCESS;
+ }
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static int pptp_rcv(struct sk_buff *skb)
+{
+ struct pppox_sock *po;
+ struct pptp_gre_header *header;
+ struct iphdr *iph;
+ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0)
+ int ret;
+ struct sock *sk;
+ #endif
+
+ if (skb->pkt_type != PACKET_HOST)
+ goto drop;
+
+ if (!pskb_may_pull(skb, 12))
+ goto drop;
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ iph = ip_hdr(skb);
+ #else
+ iph = skb->nh.iph;
+ #endif
+
+ header = (struct pptp_gre_header *)skb->data;
+
+ if ( /* version should be 1 */
+ ((header->ver & 0x7F) != PPTP_GRE_VER) ||
+ /* PPTP-GRE protocol for PPTP */
+ (ntohs(header->protocol) != PPTP_GRE_PROTO)||
+ /* flag C should be clear */
+ PPTP_GRE_IS_C(header->flags) ||
+ /* flag R should be clear */
+ PPTP_GRE_IS_R(header->flags) ||
+ /* flag K should be set */
+ (!PPTP_GRE_IS_K(header->flags)) ||
+ /* routing and recursion ctrl = 0 */
+ ((header->flags&0xF) != 0)){
+ /* if invalid, discard this packet */
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP: Discarding GRE: %X %X %X %X %X %X\n",
+ header->ver&0x7F, ntohs(header->protocol),
+ PPTP_GRE_IS_C(header->flags),
+ PPTP_GRE_IS_R(header->flags),
+ PPTP_GRE_IS_K(header->flags),
+ header->flags & 0xF);
+ goto drop;
+ }
+
+
+ if ((po=lookup_chan(htons(header->call_id),iph->saddr))) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ nf_reset(skb);
+ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0)
+ sk=sk_pppox(po);
+ bh_lock_sock(sk);
+ /* Socket state is unknown, must put skb into backlog. */
+ if (sk->lock.users != 0) {
+ sk_add_backlog(sk, skb);
+ ret = NET_RX_SUCCESS;
+ } else {
+ ret = pptp_rcv_core(sk, skb);
+ }
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ return ret;
+
+ #else /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0) */
+
+ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
+ return sk_receive_skb(sk_pppox(po), skb);
+ #else
+ return sk_receive_skb(sk_pppox(po), skb, 0);
+ #endif
+
+ #endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0) */
+ }else {
+ #ifdef DEBUG
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP: Discarding packet from unknown call_id %i\n",htons(header->call_id));
+ #endif
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ }
+
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static int pptp_bind(struct socket *sock,struct sockaddr *uservaddr,int sockaddr_len)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct pptp_opt *opt=&po->proto.pptp;
+ int error=0;
+
+ #ifdef DEBUG
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP: bind: addr=%X call_id=%i\n",sp->sa_addr.pptp.sin_addr.s_addr,
+ sp->sa_addr.pptp.call_id);
+ #endif
+ lock_sock(sk);
+
+ opt->src_addr=sp->sa_addr.pptp;
+ if (add_chan(po))
+ {
+ release_sock(sk);
+ error=-EBUSY;
+ }
+ #ifdef DEBUG
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP: using call_id %i\n",opt->src_addr.call_id);
+ #endif
+
+ release_sock(sk);
+ return error;
+}
+
+static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct pptp_opt *opt = &po->proto.pptp;
+ struct rtable *rt; /* Route to the other host */
+ int error=0;
+
+ if (sp->sa_protocol != PX_PROTO_PPTP)
+ return -EINVAL;
+
+ #ifdef DEBUG
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP[%i]: connect: addr=%X call_id=%i\n",opt->src_addr.call_id,
+ sp->sa_addr.pptp.sin_addr.s_addr,sp->sa_addr.pptp.call_id);
+ #endif
+
+ if (lookup_chan_dst(sp->sa_addr.pptp.call_id,sp->sa_addr.pptp.sin_addr.s_addr))
+ return -EALREADY;
+
+ lock_sock(sk);
+ /* Check for already bound sockets */
+ if (SK_STATE(sk) & PPPOX_CONNECTED){
+ error = -EBUSY;
+ goto end;
+ }
+
+ /* Check for already disconnected sockets, on attempts to disconnect */
+ if (SK_STATE(sk) & PPPOX_DEAD){
+ error = -EALREADY;
+ goto end;
+ }
+
+ if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr){
+ error = -EINVAL;
+ goto end;
+ }
+
+ po->chan.private=sk;
+ po->chan.ops=&pptp_chan_ops;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ {
+ struct rt_key key = {
+ .dst=opt->dst_addr.sin_addr.s_addr,
+ .src=opt->src_addr.sin_addr.s_addr,
+ .tos=RT_TOS(0),
+ };
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ if (ip_route_output_key(&rt, &key)) {
+ #else
+ if (ip_route_output_key(&init_net, &rt, &key)) {
+ #endif
+ error = -EHOSTUNREACH;
+ goto end;
+ }
+ }
+ #else
+ {
+ struct flowi fl = {
+ .nl_u = { .ip4_u =
+ { .daddr = opt->dst_addr.sin_addr.s_addr,
+ .saddr = opt->src_addr.sin_addr.s_addr,
+ .tos = RT_CONN_FLAGS(sk) } },
+ .proto = IPPROTO_GRE };
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
+ security_sk_classify_flow(sk, &fl);
+ #endif
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+ if (ip_route_output_key(&rt, &fl)){
+ #else
+ if (ip_route_output_key(&init_net, &rt, &fl)){
+ #endif
+ error = -EHOSTUNREACH;
+ goto end;
+ }
+ sk_setup_caps(sk, &rt->u.dst);
+ }
+ #endif
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ po->chan.mtu=PPP_MTU;
+ #else
+ po->chan.mtu=dst_mtu(&rt->u.dst);
+ if (!po->chan.mtu) po->chan.mtu=PPP_MTU;
+ #endif
+ ip_rt_put(rt);
+ po->chan.mtu-=PPTP_HEADER_OVERHEAD;
+
+ po->chan.hdrlen=2+sizeof(struct pptp_gre_header);
+ error = ppp_register_channel(&po->chan);
+ if (error){
+ printk(KERN_ERR "PPTP: failed to register PPP channel (%d)\n",error);
+ goto end;
+ }
+
+ opt->dst_addr=sp->sa_addr.pptp;
+ SK_STATE(sk) = PPPOX_CONNECTED;
+
+ end:
+ release_sock(sk);
+ return error;
+}
+
+static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer)
+{
+ int len = sizeof(struct sockaddr_pppox);
+ struct sockaddr_pppox sp;
+
+ sp.sa_family = AF_PPPOX;
+ sp.sa_protocol = PX_PROTO_PPTP;
+ sp.sa_addr.pptp=pppox_sk(sock->sk)->proto.pptp.src_addr;
+
+ memcpy(uaddr, &sp, len);
+
+ *usockaddr_len = len;
+
+ return 0;
+}
+
+static int pptp_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct pppox_sock *po;
+ struct pptp_opt *opt;
+ int error = 0;
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ if (sk->dead)
+ #else
+ if (sock_flag(sk, SOCK_DEAD))
+ #endif
+ {
+ release_sock(sk);
+ return -EBADF;
+ }
+
+ po = pppox_sk(sk);
+ opt=&po->proto.pptp;
+ del_chan(po);
+
+ pppox_unbind_sock(sk);
+ SK_STATE(sk) = PPPOX_DEAD;
+
+ #ifdef DEBUG
+ if (log_level>=1)
+ printk(KERN_INFO"PPTP[%i]: release\n",opt->src_addr.call_id);
+ #endif
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return error;
+}
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+static struct proto pptp_sk_proto = {
+ .name = "PPTP",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct pppox_sock),
+};
+#endif
+
+static struct proto_ops pptp_ops = {
+ .family = AF_PPPOX,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+ .owner = THIS_MODULE,
+#endif
+ .release = pptp_release,
+ .bind = pptp_bind,
+ .connect = pptp_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = pptp_getname,
+ .poll = sock_no_poll,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+ .ioctl = pppox_ioctl,
+ #endif
+};
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static void pptp_sock_destruct(struct sock *sk)
+{
+ skb_queue_purge(&sk->receive_queue);
+ if (!(SK_STATE(sk) & PPPOX_DEAD)) {
+ del_chan(pppox_sk(sk));
+ pppox_unbind_sock(sk);
+ }
+ if (sk->protinfo.destruct_hook)
+ kfree(sk->protinfo.destruct_hook);
+
+ MOD_DEC_USE_COUNT;
+}
+
+static int pptp_create(struct socket *sock)
+{
+ int error = -ENOMEM;
+ struct sock *sk;
+ struct pppox_sock *po;
+ struct pptp_opt *opt;
+
+ MOD_INC_USE_COUNT;
+
+ sk = sk_alloc(PF_PPPOX, GFP_KERNEL, 1);
+ if (!sk)
+ goto out;
+
+ sock_init_data(sock, sk);
+
+ sock->state = SS_UNCONNECTED;
+ sock->ops = &pptp_ops;
+
+ //sk->sk_backlog_rcv = pppoe_rcv_core;
+ sk->state = PPPOX_NONE;
+ sk->type = SOCK_STREAM;
+ sk->family = PF_PPPOX;
+ sk->protocol = PX_PROTO_PPTP;
+
+ sk->protinfo.pppox=kzalloc(sizeof(struct pppox_sock),GFP_KERNEL);
+ sk->destruct=pptp_sock_destruct;
+ sk->protinfo.destruct_hook=sk->protinfo.pppox;
+
+ po = pppox_sk(sk);
+ po->sk=sk;
+ opt=&po->proto.pptp;
+
+ opt->seq_sent=0; opt->seq_recv=0;
+ opt->ack_recv=0; opt->ack_sent=0;
+
+ error = 0;
+out:
+ return error;
+}
+#else
+static void pptp_sock_destruct(struct sock *sk)
+{
+ if (!(SK_STATE(sk) & PPPOX_DEAD)){
+ del_chan(pppox_sk(sk));
+ pppox_unbind_sock(sk);
+ }
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+static int pptp_create(struct socket *sock)
+#else
+static int pptp_create(struct net *net, struct socket *sock)
+#endif
+{
+ int error = -ENOMEM;
+ struct sock *sk;
+ struct pppox_sock *po;
+ struct pptp_opt *opt;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ sk = sk_alloc(PF_PPPOX, GFP_KERNEL, &pptp_sk_proto, 1);
+ #else
+ sk = sk_alloc(net,PF_PPPOX, GFP_KERNEL, &pptp_sk_proto);
+ #endif
+ if (!sk)
+ goto out;
+
+ sock_init_data(sock, sk);
+
+ sock->state = SS_UNCONNECTED;
+ sock->ops = &pptp_ops;
+
+ sk->sk_backlog_rcv = pptp_rcv_core;
+ sk->sk_state = PPPOX_NONE;
+ sk->sk_type = SOCK_STREAM;
+ sk->sk_family = PF_PPPOX;
+ sk->sk_protocol = PX_PROTO_PPTP;
+ sk->sk_destruct = pptp_sock_destruct;
+
+ po = pppox_sk(sk);
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ po->sk=sk;
+ #endif
+ opt=&po->proto.pptp;
+
+ opt->seq_sent=0; opt->seq_recv=0;
+ opt->ack_recv=0; opt->ack_sent=0;
+
+ error = 0;
+out:
+ return error;
+}
+#endif
+
+
+static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sock *sk = (struct sock *) chan->private;
+ struct pppox_sock *po = pppox_sk(sk);
+ struct pptp_opt *opt=&po->proto.pptp;
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int err, val;
+
+ err = -EFAULT;
+ switch (cmd) {
+ case PPPIOCGFLAGS:
+ val = opt->ppp_flags;
+ if (put_user(val, p))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSFLAGS:
+ if (get_user(val, p))
+ break;
+ opt->ppp_flags = val & ~SC_RCV_BITS;
+ err = 0;
+ break;
+ default:
+ err = -ENOTTY;
+ }
+
+ return err;
+}
+
+
+static struct pppox_proto pppox_pptp_proto = {
+ .create = pptp_create,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ .owner = THIS_MODULE,
+ #endif
+};
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+static struct inet_protocol net_pptp_protocol = {
+ .handler = pptp_rcv,
+ //.err_handler = pptp_err,
+ .protocol = IPPROTO_GRE,
+ .name = "PPTP",
+};
+#else
+static struct net_protocol net_pptp_protocol = {
+ .handler = pptp_rcv,
+ //.err_handler = pptp_err,
+};
+#endif
+
+static int pptp_init_module(void)
+{
+ int err=0;
+ printk(KERN_INFO "PPTP driver version " PPTP_DRIVER_VERSION "\n");
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ inet_add_protocol(&net_pptp_protocol);
+ #else
+ if (inet_add_protocol(&net_pptp_protocol, IPPROTO_GRE) < 0) {
+ printk(KERN_INFO "PPTP: can't add protocol\n");
+ goto out;
+ }
+ #endif
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+ err = proto_register(&pptp_sk_proto, 0);
+ if (err){
+ printk(KERN_INFO "PPTP: can't register sk_proto\n");
+ goto out_inet_del_protocol;
+ }
+ #endif
+
+ err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
+ if (err){
+ printk(KERN_INFO "PPTP: can't register pppox_proto\n");
+ goto out_unregister_sk_proto;
+ }
+
+
+ //assuming PAGESIZE is 4096 bytes
+ callid_bitmap=(unsigned long*)__get_free_pages(GFP_KERNEL,1);
+ memset(callid_bitmap,0,PAGE_SIZE<<1);
+
+ #if (BITS_PER_LONG == 32)
+ callid_sock=(struct pppox_sock **)__get_free_pages(GFP_KERNEL,6);
+ memset(callid_sock,0,PAGE_SIZE<<6);
+ #elif (BITS_PER_LONG == 64)
+ callid_sock=(struct pppox_sock **)__get_free_pages(GFP_KERNEL,7);
+ memset(callid_sock,0,PAGE_SIZE<<7);
+ #else
+ #error unknown size of LONG
+ #endif
+
+out:
+ return err;
+out_unregister_sk_proto:
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+ proto_unregister(&pptp_sk_proto);
+ #endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+out_inet_del_protocol:
+#endif
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ inet_del_protocol(&net_pptp_protocol);
+ #else
+ inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE);
+ #endif
+ goto out;
+}
+
+static void pptp_exit_module(void)
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ flush_scheduled_tasks();
+ #else
+ flush_scheduled_work();
+ #endif
+
+ unregister_pppox_proto(PX_PROTO_PPTP);
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ inet_del_protocol(&net_pptp_protocol);
+ #else
+ proto_unregister(&pptp_sk_proto);
+ inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE);
+ #endif
+ if (callid_bitmap) free_pages((unsigned long)callid_bitmap,1);
+ if (callid_sock) {
+ #if (BITS_PER_LONG == 32)
+ free_pages((unsigned long)callid_sock,6);
+ #elif (BITS_PER_LONG == 64)
+ free_pages((unsigned long)callid_sock,7);
+ #endif
+ }
+}
+
+module_init(pptp_init_module);
+module_exit(pptp_exit_module);
diff --git a/kernel/patch/patch-2.6.18 b/kernel/patch/patch-2.6.18
new file mode 100644
index 0000000..d38121d
--- /dev/null
+++ b/kernel/patch/patch-2.6.18
@@ -0,0 +1,1110 @@
+diff -uprN linux-2.6.18.orig/drivers/net/Kconfig linux-2.6.18.pptp/drivers/net/Kconfig
+--- linux-2.6.18.orig/drivers/net/Kconfig 2006-10-30 13:20:24.000000000 +0300
++++ linux-2.6.18.pptp/drivers/net/Kconfig 2006-10-30 13:21:45.000000000 +0300
+@@ -2687,6 +2687,15 @@ config PPPOE
+ which contains instruction on how to use this driver (under
+ the heading "Kernel mode PPPoE").
+
++config PPTP
++ tristate "PPTP (Point-to-Point Tunneling Protocol) (EXPERIMENTAL)"
++ depends on EXPERIMENTAL && PPP
++ help
++ Support for Microsoft Point-to-Point Tunneling Protocol.
++
++ See http://accel-pptp.sourceforge.net/ for information on
++ configuring PPTP clients and servers to utilize this driver.
++
+ config PPPOATM
+ tristate "PPP over ATM"
+ depends on ATM && PPP
+diff -uprN linux-2.6.18.orig/drivers/net/Makefile linux-2.6.18.pptp/drivers/net/Makefile
+--- linux-2.6.18.orig/drivers/net/Makefile 2006-10-30 13:20:23.000000000 +0300
++++ linux-2.6.18.pptp/drivers/net/Makefile 2006-10-30 13:21:45.000000000 +0300
+@@ -121,6 +121,7 @@ obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate
+ obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
+ obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o
+ obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
++obj-$(CONFIG_PPTP) += pppox.o pptp.o
+
+ obj-$(CONFIG_SLIP) += slip.o
+ ifeq ($(CONFIG_SLIP_COMPRESSED),y)
+diff -uprN linux-2.6.18.orig/drivers/net/pptp.c linux-2.6.18.pptp/drivers/net/pptp.c
+--- linux-2.6.18.orig/drivers/net/pptp.c 1970-01-01 03:00:00.000000000 +0300
++++ linux-2.6.18.pptp/drivers/net/pptp.c 2006-10-30 13:31:37.000000000 +0300
+@@ -0,0 +1,941 @@
++/*
++ * Point-to-Point Tunneling Protocol for Linux
++ *
++ * Authors: Kozlov D. (xeb@mail.ru)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/string.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <linux/net.h>
++#include <linux/skbuff.h>
++#include <linux/init.h>
++#include <linux/ppp_channel.h>
++#include <linux/ppp_defs.h>
++#include <linux/if_ppp.h>
++#include <linux/if_pppox.h>
++#include <linux/notifier.h>
++#include <linux/file.h>
++#include <linux/proc_fs.h>
++#include <linux/in.h>
++#include <linux/ip.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/workqueue.h>
++#include <linux/version.h>
++
++#include <net/sock.h>
++#include <net/protocol.h>
++#include <net/ip.h>
++#include <net/icmp.h>
++#include <net/route.h>
++
++#include <asm/uaccess.h>
++
++#define PPTP_DRIVER_VERSION "0.7"
++
++MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol for Linux");
++MODULE_AUTHOR("Kozlov D. (xeb@mail.ru)");
++MODULE_LICENSE("GPL");
++
++static int log_level=0;
++static int min_window=5;
++static int max_window=100;
++module_param(min_window,int,5);
++MODULE_PARM_DESC(min_window,"Minimum sliding window size (default=3)");
++module_param(max_window,int,100);
++MODULE_PARM_DESC(max_window,"Maximum sliding window size (default=100)");
++module_param(log_level,int,0);
++
++static struct proc_dir_entry* proc_dir;
++
++#define HASH_SIZE 16
++#define HASH(addr) ((addr^(addr>>4))&0xF)
++static DEFINE_RWLOCK(chan_lock);
++static struct pppox_sock *chans[HASH_SIZE];
++
++static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb);
++static int read_proc(char *page, char **start, off_t off,int count,
++ int *eof, void *data);
++static int __pptp_rcv(struct pppox_sock *po,struct sk_buff *skb,int new);
++
++static struct ppp_channel_ops pptp_chan_ops= {
++ .start_xmit = pptp_xmit,
++};
++
++
++/* gre header structure: -------------------------------------------- */
++
++#define PPTP_GRE_PROTO 0x880B
++#define PPTP_GRE_VER 0x1
++
++#define PPTP_GRE_FLAG_C 0x80
++#define PPTP_GRE_FLAG_R 0x40
++#define PPTP_GRE_FLAG_K 0x20
++#define PPTP_GRE_FLAG_S 0x10
++#define PPTP_GRE_FLAG_A 0x80
++
++#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C)
++#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R)
++#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K)
++#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S)
++#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A)
++
++struct pptp_gre_header {
++ u_int8_t flags; /* bitfield */
++ u_int8_t ver; /* should be PPTP_GRE_VER (enhanced GRE) */
++ u_int16_t protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */
++ u_int16_t payload_len; /* size of ppp payload, not inc. gre header */
++ u_int16_t call_id; /* peer's call_id for this session */
++ u_int32_t seq; /* sequence number. Present if S==1 */
++ u_int32_t ack; /* seq number of highest packet recieved by */
++ /* sender in this session */
++};
++
++struct gre_statistics {
++ /* statistics for GRE receive */
++ unsigned int rx_accepted; // data packet accepted
++ unsigned int rx_lost; // data packet did not arrive before timeout
++ unsigned int rx_underwin; // data packet was under window (arrived too late
++ // or duplicate packet)
++ unsigned int rx_buffered; // data packet arrived earlier than expected,
++ // packet(s) before it were lost or reordered
++ unsigned int rx_errors; // OS error on receive
++ unsigned int rx_truncated; // truncated packet
++ unsigned int rx_invalid; // wrong protocol or invalid flags
++ unsigned int rx_acks; // acknowledgement only
++
++ /* statistics for GRE transmit */
++ unsigned int tx_sent; // data packet sent
++ unsigned int tx_failed; //
++ unsigned int tx_acks; // sent packet with just ACK
++
++ __u32 pt_seq;
++ struct timeval pt_time;
++ unsigned int rtt;
++};
++
++static struct pppox_sock * lookup_chan(__u16 call_id)
++{
++ struct pppox_sock *po;
++ read_lock_bh(&chan_lock);
++ for(po=chans[HASH(call_id)]; po; po=po->next)
++ if (po->proto.pptp.src_addr.call_id==call_id)
++ break;
++ read_unlock_bh(&chan_lock);
++ return po;
++}
++
++static void add_chan(struct pppox_sock *po)
++{
++ write_lock_bh(&chan_lock);
++ po->next=chans[HASH(po->proto.pptp.src_addr.call_id)];
++ chans[HASH(po->proto.pptp.src_addr.call_id)]=po;
++ write_unlock_bh(&chan_lock);
++}
++
++static void add_free_chan(struct pppox_sock *po)
++{
++ __u16 call_id=1;
++ struct pppox_sock *p;
++
++ write_lock_bh(&chan_lock);
++ for(call_id=1; call_id<65535; call_id++) {
++ for(p=chans[HASH(call_id)]; p; p=p->next)
++ if (p->proto.pptp.src_addr.call_id==call_id)
++ break;
++ if (!p){
++ po->proto.pptp.src_addr.call_id=call_id;
++ po->next=chans[HASH(call_id)];
++ chans[HASH(call_id)]=po;
++ break;
++ }
++ }
++ write_unlock_bh(&chan_lock);
++}
++
++static void del_chan(struct pppox_sock *po)
++{
++ struct pppox_sock *p1,*p2;
++ write_lock_bh(&chan_lock);
++ for(p2=NULL,p1=chans[HASH(po->proto.pptp.src_addr.call_id)]; p1 && p1!=po;
++ p2=p1,p1=p1->next);
++ if (p2) p2->next=p1->next;
++ else chans[HASH(po->proto.pptp.src_addr.call_id)]=p1->next;
++ write_unlock_bh(&chan_lock);
++}
++
++static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
++{
++ struct sock *sk = (struct sock *) chan->private;
++ struct pppox_sock *po = pppox_sk(sk);
++ struct pptp_opt *opt=&po->proto.pptp;
++ struct pptp_gre_header *hdr;
++ unsigned int header_len=sizeof(*hdr);
++ int len=skb?skb->len:0;
++ int err=0;
++
++ struct rtable *rt; /* Route to the other host */
++ struct net_device *tdev; /* Device to other host */
++ struct iphdr *iph; /* Our new IP header */
++ int max_headroom; /* The extra header space needed */
++
++
++ if (skb && opt->seq_sent-opt->ack_recv>opt->window){
++ opt->pause=1;
++ return 0;
++ }
++
++ {
++ struct flowi fl = { .oif = 0,
++ .nl_u = { .ip4_u =
++ { .daddr = opt->dst_addr.sin_addr.s_addr,
++ .saddr = opt->src_addr.sin_addr.s_addr,
++ .tos = RT_TOS(0) } },
++ .proto = IPPROTO_GRE };
++ if ((err=ip_route_output_key(&rt, &fl))) {
++ goto tx_error;
++ }
++ }
++ tdev = rt->u.dst.dev;
++
++ max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph)+sizeof(*hdr)+2;
++
++ if (!skb){
++ skb=dev_alloc_skb(max_headroom);
++ skb_reserve(skb,max_headroom-skb_headroom(skb));
++ }else if (skb_headroom(skb) < max_headroom ||
++ skb_cloned(skb) || skb_shared(skb)) {
++ struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
++ if (!new_skb) {
++ ip_rt_put(rt);
++ goto tx_error;
++ }
++ if (skb->sk)
++ skb_set_owner_w(new_skb, skb->sk);
++ dev_kfree_skb(skb);
++ skb = new_skb;
++ }
++
++ if (skb->len){
++ int islcp;
++ unsigned char *data=skb->data;
++ islcp=((data[0] << 8) + data[1])== PPP_LCP && 1 <= data[2] && data[2] <= 7;
++ if (islcp) {
++ data=skb_push(skb,2);
++ data[0]=0xff;
++ data[1]=0x03;
++ }
++ }
++ len=skb->len;
++
++ if (len==0) header_len-=sizeof(hdr->seq);
++ if (opt->ack_sent == opt->seq_recv) header_len-=sizeof(hdr->ack);
++
++ skb->nh.raw = skb_push(skb, sizeof(*iph)+header_len);
++ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
++ IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
++ IPSKB_REROUTED);
++ #endif
++ dst_release(skb->dst);
++ skb->dst = &rt->u.dst;
++
++ /*
++ * Push down and install the IP header.
++ */
++
++ iph = skb->nh.iph;
++ iph->version = 4;
++ iph->ihl = sizeof(struct iphdr) >> 2;
++ iph->frag_off = 0;//df;
++ iph->protocol = IPPROTO_GRE;
++ iph->tos = 0;
++ iph->daddr = rt->rt_dst;
++ iph->saddr = rt->rt_src;
++ iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
++
++ hdr=(struct pptp_gre_header *)(iph+1);
++ skb->h.raw = (char*)hdr;
++
++ hdr->flags = PPTP_GRE_FLAG_K;
++ hdr->ver = PPTP_GRE_VER;
++ hdr->protocol = htons(PPTP_GRE_PROTO);
++ hdr->call_id = htons(opt->dst_addr.call_id);
++
++ if (!len){
++ hdr->payload_len = 0;
++ hdr->ver |= PPTP_GRE_FLAG_A;
++ /* ack is in odd place because S == 0 */
++ hdr->seq = htonl(opt->seq_recv);
++ opt->ack_sent = opt->seq_recv;
++ opt->stat->tx_acks++;
++ }else {
++ //if (!opt->seq_sent){
++ //}
++
++ hdr->flags |= PPTP_GRE_FLAG_S;
++ hdr->seq = htonl(opt->seq_sent++);
++ if (log_level>=2)
++ printk("PPTP: send packet: seq=%i",opt->seq_sent);
++ if (opt->ack_sent != opt->seq_recv) {
++ /* send ack with this message */
++ hdr->ver |= PPTP_GRE_FLAG_A;
++ hdr->ack = htonl(opt->seq_recv);
++ opt->ack_sent = opt->seq_recv;
++ if (log_level>=2)
++ printk(" ack=%i",opt->seq_recv);
++ }
++ hdr->payload_len = htons(len);
++ if (log_level>=2)
++ printk("\n");
++ }
++
++ nf_reset(skb);
++
++ skb->ip_summed = CHECKSUM_NONE;
++ iph->tot_len = htons(skb->len);
++ ip_select_ident(iph, &rt->u.dst, NULL);
++ ip_send_check(iph);
++
++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output);
++ if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) {
++ opt->stat->tx_sent++;
++ if (!opt->stat->pt_seq){
++ opt->stat->pt_seq = opt->seq_sent;
++ do_gettimeofday(&opt->stat->pt_time);
++ }
++ }else goto tx_error;
++
++ return 1;
++
++tx_error:
++ opt->stat->tx_failed++;
++ if (!len) dev_kfree_skb(skb);
++ return 1;
++}
++
++static void ack_work(struct pppox_sock *po)
++{
++ struct pptp_opt *opt=&po->proto.pptp;
++ if (opt->ack_sent != opt->seq_recv)
++ pptp_xmit(&po->chan,0);
++
++ if (!opt->proc){
++ char unit[10];
++ opt->proc=1;
++ sprintf(unit,"ppp%i",ppp_unit_number(&po->chan));
++ create_proc_read_entry(unit,0,proc_dir,read_proc,po);
++ }
++}
++
++static int get_seq(struct sk_buff *skb)
++{
++ struct iphdr *iph;
++ u8 *payload;
++ struct pptp_gre_header *header;
++
++ iph = (struct iphdr*)skb->data;
++ payload = skb->data + (iph->ihl << 2);
++ header = (struct pptp_gre_header *)(payload);
++
++ return ntohl(header->seq);
++}
++static void buf_work(struct pppox_sock *po)
++{
++ struct timeval tv1,tv2;
++ struct sk_buff *skb;
++ struct pptp_opt *opt=&po->proto.pptp;
++ unsigned int t;
++
++ do_gettimeofday(&tv1);
++ spin_lock_bh(&opt->skb_buf_lock);
++ while((skb=skb_dequeue(&opt->skb_buf))){
++ if (!__pptp_rcv(po,skb,0)){
++ skb_get_timestamp(skb,&tv2);
++ t=(tv1.tv_sec-tv2.tv_sec)*1000000+(tv1.tv_usec-tv2.tv_usec);
++ if (t<opt->stat->rtt){
++ skb_queue_head(&opt->skb_buf,skb);
++ schedule_delayed_work(&opt->buf_work,t/100*HZ/10000);
++ goto exit;
++ }
++ t=get_seq(skb)-1;
++ opt->stat->rx_lost+=t-opt->seq_recv;
++ opt->seq_recv=t;
++ __pptp_rcv(po,skb,0);
++ }
++ }
++exit:
++ spin_unlock_bh(&opt->skb_buf_lock);
++}
++
++
++#define MISSING_WINDOW 20
++#define WRAPPED( curseq, lastseq) \
++ ((((curseq) & 0xffffff00) == 0) && \
++ (((lastseq) & 0xffffff00 ) == 0xffffff00))
++static int __pptp_rcv(struct pppox_sock *po,struct sk_buff *skb,int new)
++{
++ struct pptp_opt *opt=&po->proto.pptp;
++ int headersize,payload_len,seq;
++ __u8 *payload;
++ struct pptp_gre_header *header;
++
++ header = (struct pptp_gre_header *)(skb->data);
++
++ if (new){
++ /* test if acknowledgement present */
++ if (PPTP_GRE_IS_A(header->ver)){
++ __u32 ack = (PPTP_GRE_IS_S(header->flags))?
++ header->ack:header->seq; /* ack in different place if S = 0 */
++ ack = ntohl( ack);
++ if (ack > opt->ack_recv) opt->ack_recv = ack;
++ /* also handle sequence number wrap-around */
++ if (WRAPPED(ack,opt->ack_recv)) opt->ack_recv = ack;
++ if (opt->stat->pt_seq && opt->ack_recv > opt->stat->pt_seq){
++ struct timeval tv;
++ unsigned int rtt;
++ do_gettimeofday(&tv);
++ rtt = (tv.tv_sec - opt->stat->pt_time.tv_sec)*1000000+
++ tv.tv_usec-opt->stat->pt_time.tv_usec;
++ opt->stat->rtt = (opt->stat->rtt + rtt) / 2;
++ if (opt->stat->rtt>opt->timeout) opt->stat->rtt=opt->timeout;
++ opt->stat->pt_seq=0;
++ }
++ if (opt->pause){
++ opt->pause=0;
++ ppp_output_wakeup(&po->chan);
++ }
++ }
++
++ /* test if payload present */
++ if (!PPTP_GRE_IS_S(header->flags)){
++ opt->stat->rx_acks++;
++ goto drop;
++ }
++ }
++
++ headersize = sizeof(*header);
++ payload_len = ntohs(header->payload_len);
++ seq = ntohl(header->seq);
++
++ /* no ack present? */
++ if (!PPTP_GRE_IS_A(header->ver)) headersize -= sizeof(header->ack);
++ /* check for incomplete packet (length smaller than expected) */
++ if (skb->len- headersize < payload_len){
++ if (log_level>=1)
++ printk("PPTP: discarding truncated packet (expected %d, got %d bytes)\n",
++ payload_len, skb->len- headersize);
++ opt->stat->rx_truncated++;
++ goto drop;
++ }
++
++ payload=skb->data+headersize;
++ /* check for expected sequence number */
++ if ((seq == opt->seq_recv + 1) || (!opt->timeout &&
++ (seq > opt->seq_recv + 1 || WRAPPED(seq, opt->seq_recv)))){
++ if ( log_level >= 2 )
++ printk("PPTP: accepting packet %d size=%i (%02x %02x %02x %02x %02x %02x)\n", seq,payload_len,
++ *(payload +0),
++ *(payload +1),
++ *(payload +2),
++ *(payload +3),
++ *(payload +4),
++ *(payload +5));
++ opt->stat->rx_accepted++;
++ opt->stat->rx_lost+=seq-(opt->seq_recv + 1);
++ opt->seq_recv = seq;
++ schedule_work(&opt->ack_work);
++
++ skb_pull(skb,headersize);
++
++ if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI){
++ /* chop off address/control */
++ if (skb->len < 3)
++ goto drop;
++ skb_pull(skb,2);
++ }
++
++ if ((*skb->data) & 1){
++ /* protocol is compressed */
++ skb_push(skb, 1)[0] = 0;
++ }
++
++ ppp_input(&po->chan,skb);
++
++ return 1;
++ /* out of order, check if the number is too low and discard the packet.
++ * (handle sequence number wrap-around, and try to do it right) */
++ }else if ( seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq) ){
++ if ( log_level >= 1)
++ printk("PPTP: discarding duplicate or old packet %d (expecting %d)\n",
++ seq, opt->seq_recv + 1);
++ opt->stat->rx_underwin++;
++ /* sequence number too high, is it reasonably close? */
++ }else if ( seq < opt->seq_recv + MISSING_WINDOW ||
++ WRAPPED(seq, opt->seq_recv + MISSING_WINDOW) ){
++ opt->stat->rx_buffered++;
++ if ( log_level >= 1 && new )
++ printk("PPTP: buffering packet %d (expecting %d, lost or reordered)\n",
++ seq, opt->seq_recv+1);
++ return 0;
++ /* no, packet must be discarded */
++ }else{
++ if ( log_level >= 1 )
++ printk("PPTP: discarding bogus packet %d (expecting %d)\n",
++ seq, opt->seq_recv + 1);
++ }
++drop:
++ kfree_skb(skb);
++ return -1;
++}
++
++
++static int pptp_rcv(struct sk_buff *skb)
++{
++ struct pptp_gre_header *header;
++ struct pppox_sock *po;
++ struct pptp_opt *opt;
++
++ if (!pskb_may_pull(skb, 12))
++ goto drop;
++
++ header = (struct pptp_gre_header *)skb->data;
++
++ if ( /* version should be 1 */
++ ((header->ver & 0x7F) != PPTP_GRE_VER) ||
++ /* PPTP-GRE protocol for PPTP */
++ (ntohs(header->protocol) != PPTP_GRE_PROTO)||
++ /* flag C should be clear */
++ PPTP_GRE_IS_C(header->flags) ||
++ /* flag R should be clear */
++ PPTP_GRE_IS_R(header->flags) ||
++ /* flag K should be set */
++ (!PPTP_GRE_IS_K(header->flags)) ||
++ /* routing and recursion ctrl = 0 */
++ ((header->flags&0xF) != 0)){
++ /* if invalid, discard this packet */
++ if (log_level>=1)
++ printk("PPTP: Discarding GRE: %X %X %X %X %X %X\n",
++ header->ver&0x7F, ntohs(header->protocol),
++ PPTP_GRE_IS_C(header->flags),
++ PPTP_GRE_IS_R(header->flags),
++ PPTP_GRE_IS_K(header->flags),
++ header->flags & 0xF);
++ goto drop;
++ }
++
++ dst_release(skb->dst);
++ skb->dst = NULL;
++ nf_reset(skb);
++
++ if ((po=lookup_chan(htons(header->call_id)))) {
++ if (!(sk_pppox(po)->sk_state&PPPOX_BOUND))
++ goto drop;
++ if (__pptp_rcv(po,skb,1))
++ buf_work(po);
++ else{
++ struct timeval tv;
++ do_gettimeofday(&tv);
++ skb_set_timestamp(skb,&tv);
++ opt=&po->proto.pptp;
++ spin_lock(&opt->skb_buf_lock);
++ skb_queue_tail(&opt->skb_buf, skb);
++ spin_unlock(&opt->skb_buf_lock);
++ schedule_delayed_work(&opt->buf_work,opt->stat->rtt/100*HZ/10000);
++ }
++ goto out;
++ }else{
++ if (log_level>=1)
++ printk("PPTP: Discarding packet from unknown call_id %i\n",header->call_id);
++ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
++ }
++
++drop:
++ kfree_skb(skb);
++out:
++ return 0;
++}
++
++
++
++static int proc_output (struct pppox_sock *po,char *buf)
++{
++ struct gre_statistics *stat=po->proto.pptp.stat;
++ char *p=buf;
++ p+=sprintf(p,"rx accepted = %d\n",stat->rx_accepted);
++ p+=sprintf(p,"rx lost = %d\n",stat->rx_lost);
++ p+=sprintf(p,"rx under win = %d\n",stat->rx_underwin);
++ p+=sprintf(p,"rx buffered = %d\n",stat->rx_buffered);
++ p+=sprintf(p,"rx invalid = %d\n",stat->rx_invalid);
++ p+=sprintf(p,"rx acks = %d\n",stat->rx_acks);
++ p+=sprintf(p,"tx sent = %d\n",stat->tx_sent);
++ p+=sprintf(p,"tx failed = %d\n",stat->tx_failed);
++ p+=sprintf(p,"tx acks = %d\n",stat->tx_acks);
++
++ return p-buf;
++}
++static int read_proc(char *page, char **start, off_t off,int count, int *eof, void *data)
++{
++ struct pppox_sock *po = data;
++ int len = proc_output (po,page);
++ if (len <= off+count) *eof = 1;
++ *start = page + off;
++ len -= off;
++ if (len>count) len = count;
++ if (len<0) len = 0;
++ return len;
++}
++
++static int pptp_bind(struct socket *sock,struct sockaddr *uservaddr,int sockaddr_len)
++{
++ struct sock *sk = sock->sk;
++ struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
++ struct pppox_sock *po = pppox_sk(sk);
++ struct pptp_opt *opt=&po->proto.pptp;
++ int error=0;
++
++ if (log_level>=1)
++ printk("PPTP: bind: addr=%X call_id=%i\n",sp->sa_addr.pptp.sin_addr.s_addr,
++ sp->sa_addr.pptp.call_id);
++ lock_sock(sk);
++
++ opt->src_addr=sp->sa_addr.pptp;
++ if (sp->sa_addr.pptp.call_id){
++ if (lookup_chan(sp->sa_addr.pptp.call_id)){
++ error=-EBUSY;
++ goto end;
++ }
++ add_chan(po);
++ }else{
++ add_free_chan(po);
++ if (!opt->src_addr.call_id)
++ error=-EBUSY;
++ if (log_level>=1)
++ printk("PPTP: using call_id %i\n",opt->src_addr.call_id);
++ }
++
++ end:
++ release_sock(sk);
++ return error;
++}
++
++static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
++ int sockaddr_len, int flags)
++{
++ struct sock *sk = sock->sk;
++ struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
++ struct pppox_sock *po = pppox_sk(sk);
++ struct pptp_opt *opt=&po->proto.pptp;
++ int error=0;
++
++ if (log_level>=1)
++ printk("PPTP: connect: addr=%X call_id=%i\n",
++ sp->sa_addr.pptp.sin_addr.s_addr,sp->sa_addr.pptp.call_id);
++
++ lock_sock(sk);
++
++ if (sp->sa_protocol != PX_PROTO_PPTP){
++ error = -EINVAL;
++ goto end;
++ }
++
++ /* Check for already bound sockets */
++ if (sk->sk_state & PPPOX_CONNECTED){
++ error = -EBUSY;
++ goto end;
++ }
++
++ /* Check for already disconnected sockets, on attempts to disconnect */
++ if (sk->sk_state & PPPOX_DEAD){
++ error = -EALREADY;
++ goto end;
++ }
++
++ if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr){
++ error = -EINVAL;
++ goto end;
++ }
++
++ po->chan.private=sk;
++ po->chan.ops=&pptp_chan_ops;
++ po->chan.mtu=PPP_MTU;
++ po->chan.hdrlen=2+sizeof(struct pptp_gre_header);
++ error = ppp_register_channel(&po->chan);
++ if (error){
++ printk(KERN_ERR "PPTP: failed to register PPP channel (%d)\n",error);
++ goto end;
++ }
++
++ opt->dst_addr=sp->sa_addr.pptp;
++ sk->sk_state = PPPOX_CONNECTED;
++
++ end:
++ release_sock(sk);
++ return error;
++}
++
++static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
++ int *usockaddr_len, int peer)
++{
++ int len = sizeof(struct sockaddr_pppox);
++ struct sockaddr_pppox sp;
++
++ sp.sa_family = AF_PPPOX;
++ sp.sa_protocol = PX_PROTO_PPTP;
++ sp.sa_addr.pptp=pppox_sk(sock->sk)->proto.pptp.src_addr;
++
++ memcpy(uaddr, &sp, len);
++
++ *usockaddr_len = len;
++
++ return 0;
++}
++
++static int pptp_setsockopt(struct socket *sock, int level, int optname,
++ char* optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ struct pppox_sock *po = pppox_sk(sk);
++ struct pptp_opt *opt=&po->proto.pptp;
++ int val;
++
++ if (optlen!=sizeof(int))
++ return -EINVAL;
++
++ if (get_user(val,(int __user*)optval))
++ return -EFAULT;
++
++ switch(optname) {
++ case PPTP_SO_TIMEOUT:
++ opt->timeout=val;
++ break;
++ case PPTP_SO_WINDOW:
++ opt->window=val;
++ break;
++ default:
++ return -ENOPROTOOPT;
++ }
++
++ return 0;
++}
++
++static int pptp_getsockopt(struct socket *sock, int level, int optname,
++ char* optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct pppox_sock *po = pppox_sk(sk);
++ struct pptp_opt *opt=&po->proto.pptp;
++ int len,val;
++
++ if (get_user(len,(int __user*)optlen))
++ return -EFAULT;
++
++ if (len<sizeof(int))
++ return -EINVAL;
++
++ switch(optname) {
++ case PPTP_SO_TIMEOUT:
++ val=opt->timeout;
++ break;
++ case PPTP_SO_WINDOW:
++ val=opt->window;
++ break;
++ default:
++ return -ENOPROTOOPT;
++ }
++
++ if (put_user(sizeof(int),(int __user*)optlen))
++ return -EFAULT;
++
++ if (put_user(val,(int __user*)optval))
++ return -EFAULT;
++
++ return 0;
++}
++
++static int pptp_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ struct pppox_sock *po;
++ struct pptp_opt *opt;
++ int error = 0;
++
++ if (!sk)
++ return 0;
++
++ if (sock_flag(sk, SOCK_DEAD))
++ return -EBADF;
++
++ po = pppox_sk(sk);
++ opt=&po->proto.pptp;
++ if (opt->src_addr.sin_addr.s_addr) {
++ cancel_delayed_work(&opt->buf_work);
++ flush_scheduled_work();
++ del_chan(po);
++ skb_queue_purge(&opt->skb_buf);
++
++ if (opt->proc){
++ char unit[10];
++ sprintf(unit,"ppp%i",ppp_unit_number(&po->chan));
++ remove_proc_entry(unit,proc_dir);
++ }
++ }
++
++ pppox_unbind_sock(sk);
++
++ kfree(opt->stat);
++
++ /* Signal the death of the socket. */
++ sk->sk_state = PPPOX_DEAD;
++
++ sock_orphan(sk);
++ sock->sk = NULL;
++
++ skb_queue_purge(&sk->sk_receive_queue);
++ sock_put(sk);
++
++ return error;
++}
++
++
++static struct proto pptp_sk_proto = {
++ .name = "PPTP",
++ .owner = THIS_MODULE,
++ .obj_size = sizeof(struct pppox_sock),
++};
++
++static const struct proto_ops pptp_ops = {
++ .family = AF_PPPOX,
++ .owner = THIS_MODULE,
++ .release = pptp_release,
++ .bind = pptp_bind,
++ .connect = pptp_connect,
++ .socketpair = sock_no_socketpair,
++ .accept = sock_no_accept,
++ .getname = pptp_getname,
++ .poll = sock_no_poll,
++ .listen = sock_no_listen,
++ .shutdown = sock_no_shutdown,
++ .setsockopt = pptp_setsockopt,
++ .getsockopt = pptp_getsockopt,
++ .sendmsg = sock_no_sendmsg,
++ .recvmsg = sock_no_recvmsg,
++ .mmap = sock_no_mmap,
++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
++ .ioctl = pppox_ioctl,
++ #endif
++};
++
++static int pptp_create(struct socket *sock)
++{
++ int error = -ENOMEM;
++ struct sock *sk;
++ struct pppox_sock *po;
++ struct pptp_opt *opt;
++
++ sk = sk_alloc(PF_PPPOX, GFP_KERNEL, &pptp_sk_proto, 1);
++ if (!sk)
++ goto out;
++
++ sock_init_data(sock, sk);
++
++ sock->state = SS_UNCONNECTED;
++ sock->ops = &pptp_ops;
++
++ //sk->sk_backlog_rcv = pppoe_rcv_core;
++ sk->sk_state = PPPOX_NONE;
++ sk->sk_type = SOCK_STREAM;
++ sk->sk_family = PF_PPPOX;
++ sk->sk_protocol = PX_PROTO_PPTP;
++
++ po = pppox_sk(sk);
++ opt=&po->proto.pptp;
++
++ opt->window=min_window;
++ opt->timeout=0;
++ opt->seq_sent=0; opt->seq_recv=-1;
++ opt->ack_recv=0; opt->ack_sent=-1;
++ skb_queue_head_init(&opt->skb_buf);
++ opt->skb_buf_lock=SPIN_LOCK_UNLOCKED;
++ INIT_WORK(&opt->ack_work,(void(*)(void*))ack_work,sk);
++ INIT_WORK(&opt->buf_work,(void(*)(void*))buf_work,sk);
++ opt->stat=kzalloc(sizeof(*opt->stat),GFP_KERNEL);
++
++ error = 0;
++out:
++ return error;
++}
++
++
++static struct pppox_proto pppox_pptp_proto = {
++ .create = pptp_create,
++ //.ioctl = pptp_ioctl,
++ .owner = THIS_MODULE,
++};
++
++static struct net_protocol net_pptp_protocol = {
++ .handler = pptp_rcv,
++ //.err_handler = ipgre_err,
++};
++
++
++static int pptp_init_module(void)
++{
++ int err=0;
++ printk(KERN_INFO "PPTP driver version " PPTP_DRIVER_VERSION "\n");
++
++ if (inet_add_protocol(&net_pptp_protocol, IPPROTO_GRE) < 0) {
++ printk(KERN_INFO "PPTP: can't add protocol\n");
++ goto out;
++ }
++
++ err = proto_register(&pptp_sk_proto, 0);
++ if (err){
++ printk(KERN_INFO "PPTP: can't register sk_proto\n");
++ goto out_inet_del_protocol;
++ }
++
++ err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto);
++ if (err){
++ printk(KERN_INFO "PPTP: can't register pppox_proto\n");
++ goto out_unregister_sk_proto;
++ }
++
++ proc_dir=proc_mkdir("pptp",NULL);
++ if (!proc_dir){
++ printk(KERN_ERR "PPTP: failed to create proc dir\n");
++ }
++ //console_verbose();
++
++out:
++ return err;
++out_unregister_sk_proto:
++ proto_unregister(&pptp_sk_proto);
++out_inet_del_protocol:
++ inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE);
++ goto out;
++}
++
++static void pptp_exit_module(void)
++{
++ unregister_pppox_proto(PX_PROTO_PPTP);
++ proto_unregister(&pptp_sk_proto);
++ inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE);
++
++ if (proc_dir)
++ remove_proc_entry("pptp",NULL);
++}
++
++
++module_init(pptp_init_module);
++module_exit(pptp_exit_module);
+diff -uprN linux-2.6.18.orig/include/linux/if_pppox.h linux-2.6.18.pptp/include/linux/if_pppox.h
+--- linux-2.6.18.orig/include/linux/if_pppox.h 2006-10-30 13:20:48.000000000 +0300
++++ linux-2.6.18.pptp/include/linux/if_pppox.h 2006-10-30 11:35:35.000000000 +0300
+@@ -1,6 +1,6 @@
+ /***************************************************************************
+ * Linux PPP over X - Generic PPP transport layer sockets
+- * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516)
++ * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516)
+ *
+ * This file supplies definitions required by the PPP over Ethernet driver
+ * (pppox.c). All version information wrt this file is located in pppox.c
+@@ -19,6 +19,7 @@
+
+ #include <asm/types.h>
+ #include <asm/byteorder.h>
++#include <linux/in.h>
+
+ #ifdef __KERNEL__
+ #include <linux/if_ether.h>
+@@ -36,29 +37,35 @@
+ #define PF_PPPOX AF_PPPOX
+ #endif /* !(AF_PPPOX) */
+
+-/************************************************************************
+- * PPPoE addressing definition
+- */
+-typedef __u16 sid_t;
+-struct pppoe_addr{
+- sid_t sid; /* Session identifier */
+- unsigned char remote[ETH_ALEN]; /* Remote address */
+- char dev[IFNAMSIZ]; /* Local device to use */
+-};
+-
+-/************************************************************************
+- * Protocols supported by AF_PPPOX
+- */
++/************************************************************************
++ * PPPoE addressing definition
++ */
++typedef __u16 sid_t;
++struct pppoe_addr{
++ sid_t sid; /* Session identifier */
++ unsigned char remote[ETH_ALEN]; /* Remote address */
++ char dev[IFNAMSIZ]; /* Local device to use */
++};
++
++struct pptp_addr{
++ __u16 call_id;
++ struct in_addr sin_addr;
++};
++/************************************************************************
++ * Protocols supported by AF_PPPOX
++ */
+ #define PX_PROTO_OE 0 /* Currently just PPPoE */
+-#define PX_MAX_PROTO 1
+-
+-struct sockaddr_pppox {
+- sa_family_t sa_family; /* address family, AF_PPPOX */
+- unsigned int sa_protocol; /* protocol identifier */
+- union{
+- struct pppoe_addr pppoe;
+- }sa_addr;
+-}__attribute__ ((packed));
++#define PX_PROTO_PPTP 1
++#define PX_MAX_PROTO 2
++
++struct sockaddr_pppox {
++ sa_family_t sa_family; /* address family, AF_PPPOX */
++ unsigned int sa_protocol; /* protocol identifier */
++ union{
++ struct pppoe_addr pppoe;
++ struct pptp_addr pptp;
++ }sa_addr;
++}__attribute__ ((packed));
+
+
+ /*********************************************************************
+@@ -111,6 +118,12 @@ struct pppoe_hdr {
+ struct pppoe_tag tag[0];
+ } __attribute__ ((packed));
+
++
++/* Socket options */
++#define PPTP_SO_TIMEOUT 1
++#define PPTP_SO_WINDOW 2
++
++
+ #ifdef __KERNEL__
+ struct pppoe_opt {
+ struct net_device *dev; /* device associated with socket*/
+@@ -118,6 +131,21 @@ struct pppoe_opt {
+ struct sockaddr_pppox relay; /* what socket data will be
+ relayed to (PPPoE relaying) */
+ };
++struct pptp_opt {
++ struct pptp_addr src_addr;
++ struct pptp_addr dst_addr;
++ int timeout;
++ int window;
++ __u32 ack_sent, ack_recv;
++ __u32 seq_sent, seq_recv;
++ int pause:1;
++ int proc:1;
++ spinlock_t skb_buf_lock;
++ struct sk_buff_head skb_buf;
++ struct work_struct ack_work; //send ack work
++ struct work_struct buf_work; //check bufferd packets work
++ struct gre_statistics *stat;
++};
+
+ #include <net/sock.h>
+
+@@ -128,6 +156,7 @@ struct pppox_sock {
+ struct pppox_sock *next; /* for hash table */
+ union {
+ struct pppoe_opt pppoe;
++ struct pptp_opt pptp;
+ } proto;
+ unsigned short num;
+ };
+diff -uprN linux-2.6.18.orig/MAINTAINERS linux-2.6.18.pptp/MAINTAINERS
+--- linux-2.6.18.orig/MAINTAINERS 2006-10-30 13:20:47.000000000 +0300
++++ linux-2.6.18.pptp/MAINTAINERS 2006-10-30 13:21:45.000000000 +0300
+@@ -2325,6 +2325,11 @@ P: Michal Ostrowski
+ M: mostrows@speakeasy.net
+ S: Maintained
+
++PPTP
++P: Dmitry Kozlov
++M: xeb@mail.ru
++S: Maintained
++
+ PREEMPTIBLE KERNEL
+ P: Robert Love
+ M: rml@tech9.net
diff --git a/kernel/patch/ppp-generic-smp-2.6.26.patch b/kernel/patch/ppp-generic-smp-2.6.26.patch
new file mode 100644
index 0000000..0354914
--- /dev/null
+++ b/kernel/patch/ppp-generic-smp-2.6.26.patch
@@ -0,0 +1,165 @@
+--- a/drivers/net/ppp_generic.c 2008-07-14 01:51:29.000000000 +0400
++++ b/drivers/net/ppp_generic.c 2009-03-01 00:17:03.000000000 +0300
+@@ -44,6 +44,7 @@
+ #include <linux/stddef.h>
+ #include <linux/device.h>
+ #include <linux/mutex.h>
++#include <linux/workqueue.h>
+ #include <net/slhc_vj.h>
+ #include <asm/atomic.h>
+
+@@ -114,7 +115,8 @@
+ void *rc_state; /* its internal state 98 */
+ unsigned long last_xmit; /* jiffies when last pkt sent 9c */
+ unsigned long last_recv; /* jiffies when last pkt rcvd a0 */
+- struct net_device *dev; /* network interface device a4 */
++ struct net_device *dev; /* network interface device a4 */
++ struct work_struct xmit_work;
+ #ifdef CONFIG_PPP_MULTILINK
+ int nxchan; /* next channel to send something on */
+ u32 nxseq; /* next sequence number to send */
+@@ -154,6 +156,10 @@
+ struct ppp *ppp; /* ppp unit we're connected to */
+ struct list_head clist; /* link in list of channels per unit */
+ rwlock_t upl; /* protects `ppp' */
++
++ struct work_struct recv_work;
++ struct sk_buff_head rq; /* receive queue for pppd */
++
+ #ifdef CONFIG_PPP_MULTILINK
+ u8 avail; /* flag used in multilink stuff */
+ u8 had_frag; /* >= 1 fragments have been sent */
+@@ -270,6 +276,7 @@
+ static void ppp_destroy_channel(struct channel *pch);
+
+ static struct class *ppp_class;
++static struct workqueue_struct *kpppd_workqueue;
+
+ /* Translates a PPP protocol number to a NP index (NP == network protocol) */
+ static inline int proto_to_npindex(int proto)
+@@ -849,6 +856,13 @@
+ int err;
+
+ printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
++
++ kpppd_workqueue = create_workqueue("kpppd");
++ if (!kpppd_workqueue){
++ printk(KERN_ERR "failed to create workqueue\n");
++ return -1;
++ }
++
+ err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
+ if (!err) {
+ ppp_class = class_create(THIS_MODULE, "ppp");
+@@ -858,10 +872,12 @@
+ }
+ device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), "ppp");
+ }
+-
++
+ out:
+- if (err)
++ if (err) {
++ destroy_workqueue(kpppd_workqueue);
+ printk(KERN_ERR "failed to register PPP device (%d)\n", err);
++ }
+ return err;
+
+ out_chrdev:
+@@ -869,6 +885,12 @@
+ goto out;
+ }
+
++static void ppp_xmit_work(struct work_struct *work)
++{
++ struct ppp *ppp=container_of(work,typeof(*ppp),xmit_work);
++ ppp_xmit_process(ppp);
++}
++
+ /*
+ * Network interface unit routines.
+ */
+@@ -908,7 +930,7 @@
+
+ netif_stop_queue(dev);
+ skb_queue_tail(&ppp->file.xq, skb);
+- ppp_xmit_process(ppp);
++ queue_work(kpppd_workqueue,&ppp->xmit_work);
+ return 0;
+
+ outf:
+@@ -1453,13 +1475,29 @@
+ {
+ ppp_recv_lock(ppp);
+ /* ppp->dev == 0 means interface is closing down */
+- if (ppp->dev)
+- ppp_receive_frame(ppp, skb, pch);
+- else
++ if (ppp->dev) {
++ skb_queue_tail(&pch->rq, skb);
++ queue_work(kpppd_workqueue,&pch->recv_work);
++ } else
+ kfree_skb(skb);
+ ppp_recv_unlock(ppp);
+ }
+
++static void ppp_recv_work(struct work_struct *work)
++{
++ struct channel *pch=container_of(work,typeof(*pch),recv_work);
++ struct sk_buff *skb;
++
++ ppp_recv_lock(pch->ppp);
++
++ while((skb=skb_dequeue(&pch->rq))){
++ if (pch->ppp->dev)
++ ppp_receive_frame(pch->ppp, skb, pch);
++ }
++
++ ppp_recv_unlock(pch->ppp);
++}
++
+ void
+ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
+ {
+@@ -2000,6 +2038,8 @@
+ chan->ppp = pch;
+ init_ppp_file(&pch->file, CHANNEL);
+ pch->file.hdrlen = chan->hdrlen;
++ INIT_WORK(&pch->recv_work,ppp_recv_work);
++ skb_queue_head_init(&pch->rq);
+ #ifdef CONFIG_PPP_MULTILINK
+ pch->lastseq = -1;
+ #endif /* CONFIG_PPP_MULTILINK */
+@@ -2419,6 +2459,7 @@
+ INIT_LIST_HEAD(&ppp->channels);
+ spin_lock_init(&ppp->rlock);
+ spin_lock_init(&ppp->wlock);
++ INIT_WORK(&ppp->xmit_work,ppp_xmit_work);
+ #ifdef CONFIG_PPP_MULTILINK
+ ppp->minseq = -1;
+ skb_queue_head_init(&ppp->mrq);
+@@ -2529,6 +2570,7 @@
+ slhc_free(ppp->vj);
+ ppp->vj = NULL;
+ }
++ cancel_work_sync(&ppp->xmit_work);
+ skb_queue_purge(&ppp->file.xq);
+ skb_queue_purge(&ppp->file.rq);
+ #ifdef CONFIG_PPP_MULTILINK
+@@ -2664,6 +2706,8 @@
+ }
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
++ cancel_work_sync(&pch->recv_work);
++ skb_queue_purge(&pch->rq);
+ kfree(pch);
+ }
+
+@@ -2676,6 +2720,7 @@
+ unregister_chrdev(PPP_MAJOR, "ppp");
+ device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
+ class_destroy(ppp_class);
++ destroy_workqueue(kpppd_workqueue);
+ }
+
+ /*
diff --git a/kernel/patch/ppp-generic-smp-2.6.28.patch b/kernel/patch/ppp-generic-smp-2.6.28.patch
new file mode 100644
index 0000000..65a218a
--- /dev/null
+++ b/kernel/patch/ppp-generic-smp-2.6.28.patch
@@ -0,0 +1,163 @@
+--- a/drivers/net/ppp_generic.c 2008-12-25 02:26:37.000000000 +0300
++++ b/drivers/net/ppp_generic.c 2009-03-01 14:54:45.000000000 +0300
+@@ -45,6 +45,7 @@
+ #include <linux/stddef.h>
+ #include <linux/device.h>
+ #include <linux/mutex.h>
++#include <linux/workqueue.h>
+ #include <net/slhc_vj.h>
+ #include <asm/atomic.h>
+
+@@ -117,6 +118,7 @@
+ unsigned long last_recv; /* jiffies when last pkt rcvd a0 */
+ struct net_device *dev; /* network interface device a4 */
+ int closing; /* is device closing down? a8 */
++ struct work_struct xmit_work;
+ #ifdef CONFIG_PPP_MULTILINK
+ int nxchan; /* next channel to send something on */
+ u32 nxseq; /* next sequence number to send */
+@@ -156,6 +158,10 @@
+ struct ppp *ppp; /* ppp unit we're connected to */
+ struct list_head clist; /* link in list of channels per unit */
+ rwlock_t upl; /* protects `ppp' */
++
++ struct work_struct recv_work;
++ struct sk_buff_head rq; /* receive queue for pppd */
++
+ #ifdef CONFIG_PPP_MULTILINK
+ u8 avail; /* flag used in multilink stuff */
+ u8 had_frag; /* >= 1 fragments have been sent */
+@@ -272,6 +278,7 @@
+ static void ppp_destroy_channel(struct channel *pch);
+
+ static struct class *ppp_class;
++static struct workqueue_struct *kpppd_workqueue;
+
+ /* Translates a PPP protocol number to a NP index (NP == network protocol) */
+ static inline int proto_to_npindex(int proto)
+@@ -860,6 +867,13 @@
+ int err;
+
+ printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
++
++ kpppd_workqueue = create_workqueue("kpppd");
++ if (!kpppd_workqueue){
++ printk(KERN_ERR "failed to create workqueue\n");
++ return -1;
++ }
++
+ err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
+ if (!err) {
+ ppp_class = class_create(THIS_MODULE, "ppp");
+@@ -870,10 +884,12 @@
+ device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL,
+ "ppp");
+ }
+-
++
+ out:
+- if (err)
++ if (err) {
++ destroy_workqueue(kpppd_workqueue);
+ printk(KERN_ERR "failed to register PPP device (%d)\n", err);
++ }
+ return err;
+
+ out_chrdev:
+@@ -881,6 +897,12 @@
+ goto out;
+ }
+
++static void ppp_xmit_work(struct work_struct *work)
++{
++ struct ppp *ppp=container_of(work,typeof(*ppp),xmit_work);
++ ppp_xmit_process(ppp);
++}
++
+ /*
+ * Network interface unit routines.
+ */
+@@ -920,7 +942,7 @@
+
+ netif_stop_queue(dev);
+ skb_queue_tail(&ppp->file.xq, skb);
+- ppp_xmit_process(ppp);
++ queue_work(kpppd_workqueue,&ppp->xmit_work);
+ return 0;
+
+ outf:
+@@ -1464,13 +1486,29 @@
+ ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+ {
+ ppp_recv_lock(ppp);
+- if (!ppp->closing)
+- ppp_receive_frame(ppp, skb, pch);
+- else
++ if (!ppp->closing){
++ skb_queue_tail(&pch->rq, skb);
++ queue_work(kpppd_workqueue,&pch->recv_work);
++ }else
+ kfree_skb(skb);
+ ppp_recv_unlock(ppp);
+ }
+
++static void ppp_recv_work(struct work_struct *work)
++{
++ struct channel *pch=container_of(work,typeof(*pch),recv_work);
++ struct sk_buff *skb;
++
++ ppp_recv_lock(pch->ppp);
++
++ while((skb=skb_dequeue(&pch->rq))){
++ if (pch->ppp->dev)
++ ppp_receive_frame(pch->ppp, skb, pch);
++ }
++
++ ppp_recv_unlock(pch->ppp);
++}
++
+ void
+ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
+ {
+@@ -2014,6 +2052,8 @@
+ chan->ppp = pch;
+ init_ppp_file(&pch->file, CHANNEL);
+ pch->file.hdrlen = chan->hdrlen;
++ INIT_WORK(&pch->recv_work,ppp_recv_work);
++ skb_queue_head_init(&pch->rq);
+ #ifdef CONFIG_PPP_MULTILINK
+ pch->lastseq = -1;
+ #endif /* CONFIG_PPP_MULTILINK */
+@@ -2429,6 +2469,7 @@
+ INIT_LIST_HEAD(&ppp->channels);
+ spin_lock_init(&ppp->rlock);
+ spin_lock_init(&ppp->wlock);
++ INIT_WORK(&ppp->xmit_work,ppp_xmit_work);
+ #ifdef CONFIG_PPP_MULTILINK
+ ppp->minseq = -1;
+ skb_queue_head_init(&ppp->mrq);
+@@ -2537,6 +2578,7 @@
+ slhc_free(ppp->vj);
+ ppp->vj = NULL;
+ }
++ cancel_work_sync(&ppp->xmit_work);
+ skb_queue_purge(&ppp->file.xq);
+ skb_queue_purge(&ppp->file.rq);
+ #ifdef CONFIG_PPP_MULTILINK
+@@ -2672,6 +2714,8 @@
+ }
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
++ cancel_work_sync(&pch->recv_work);
++ skb_queue_purge(&pch->rq);
+ kfree(pch);
+ }
+
+@@ -2684,6 +2728,7 @@
+ unregister_chrdev(PPP_MAJOR, "ppp");
+ device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
+ class_destroy(ppp_class);
++ destroy_workqueue(kpppd_workqueue);
+ }
+
+ /*