Imported Upstream version 2.1.21
This commit is contained in:
commit
f2a3180cc0
57 changed files with 24656 additions and 0 deletions
340
COPYING
Normal file
340
COPYING
Normal file
|
|
@ -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.
|
||||
479
Changes
Normal file
479
Changes
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
* Fri Dec 1 2006 Brendan O'Dea <bod@optus.net> 2.1.21
|
||||
- Security: Rhys Kidd identified a vulnerability in the handling of
|
||||
heartbeat packets. Drop oversize heartbeat packets.
|
||||
|
||||
* Thu Aug 3 2006 Brendan O'Dea <bod@optus.net> 2.1.20
|
||||
- Fix sign problem with reporting of unknown VSAs.
|
||||
- Allow DNS servers to be specified either using the old or new
|
||||
vendor-specific Ascend formats.
|
||||
|
||||
* Fri Jun 23 2006 Brendan O'Dea <bod@optus.net> 2.1.19
|
||||
- Kludge around problem with Netgear DM602 authentication.
|
||||
- Use result code AVP to set Acct-Terminate-Cause is disconnect cause
|
||||
AVP is not present.
|
||||
|
||||
* Tue Apr 18 2006 Brendan O'Dea <bod@optus.net> 2.1.18
|
||||
- Don't shutdown on TerminateReq, wait for CDN.
|
||||
- Interpret "local" direction correctly (as LAC) in disconnect AVPs.
|
||||
|
||||
* Thu Apr 13 2006 Brendan O'Dea <bod@optus.net> 2.1.17
|
||||
- Fix IPCP length test to allow Terminate-Request (4 bytes).
|
||||
- Send nsctl responses back using the correct source address (thanks ltd).
|
||||
- Similarly set the source for DAE responses; use bind_address when
|
||||
handling forwarded packets on the master.
|
||||
- Add Acct-Terminate-Cause to RADIUS stop records.
|
||||
|
||||
* Thu Feb 23 2006 Brendan O'Dea <bod@optus.net> 2.1.16
|
||||
- Send configured magic-no in LCP EchoReq when LCP is opened.
|
||||
- Correct addition of single IP to pool (Jonathan Yarden).
|
||||
- Ensure session changes from LCP ConfigReq/ConfigNak are sent to cluster.
|
||||
- Verify that RADIUS packets come from a configured server (Jonathan Yarden).
|
||||
- Avoid endless loop in processipcp, processipv6cp.
|
||||
- Additional length checks in processlcp.
|
||||
- Allow peer to request a new magic-number, or to disable magic-numbers.
|
||||
- Decrease ip_conntrack_tcp_timeout_established to 5hrs (table filling).
|
||||
|
||||
* Mon Dec 19 2005 Brendan O'Dea <bod@optus.net> 2.1.15
|
||||
- Drop backtrace.
|
||||
- Reduce logging of LCP EchoReply packets.
|
||||
- Break LCP configure loop with shutdown.
|
||||
- Limit value of MRU of 1492 (rfc2516).
|
||||
- Tun MTU should be MRU (not MRU+4).
|
||||
- Add Service-Type/Framed-Protocol to RADIUS records (Paul Martin).
|
||||
|
||||
* Fri Dec 9 2005 Brendan O'Dea <bod@optus.net> 2.1.14
|
||||
- Run PLUGIN_RADIUS_ACCOUNT for Start records.
|
||||
|
||||
* Wed Dec 7 2005 Brendan O'Dea <bod@optus.net> 2.1.13
|
||||
- Add test/ping-sweep.
|
||||
- Apply spec changes from Charlie Brady: use License header, change
|
||||
BuildRoot to include username.
|
||||
- Fix IPCP negotiation of secondary DNS server, reported by Jon Morby.
|
||||
- Clean up sessiont, removing some unused fields.
|
||||
- Remove unused "MAC" config type.
|
||||
- Reject unknown/unconfigured protocols on the master.
|
||||
- Sanity check MRU before using in ppp_code_rej, protoreject.
|
||||
|
||||
* Thu Nov 17 2005 Brendan O'Dea <bod@optus.net> 2.1.12
|
||||
- Set MTU on tunnel interface so the kernel will re-fragment large
|
||||
packets to within MRU.
|
||||
- Fix TCP checksum recalc.
|
||||
- NAK silly MRU values from peer.
|
||||
|
||||
* Mon Nov 14 2005 Brendan O'Dea <bod@optus.net> 2.1.11
|
||||
- Fix fragment handling in ip_filter.
|
||||
- Exclude counter when comparing filter rules.
|
||||
|
||||
* Sat Nov 5 2005 Brendan O'Dea <bod@optus.net> 2.1.10
|
||||
- Add scripts/l2tpns-capture.
|
||||
- Fix LCP Echo frequency.
|
||||
- Add Framed-Route entries to RADIUS records.
|
||||
- Reset restart counters correctly.
|
||||
- Reset timers on sending ConfigReq.
|
||||
- Only send one RADIUS Start record, even if IPCP is restarted.
|
||||
|
||||
* Tue Oct 11 2005 Brendan O'Dea <bod@optus.net> 2.1.9
|
||||
- Fix Calling-Station-Id in RADIUS accounting records (Slobodan Tomic).
|
||||
- Fix RADIUS authentication on DAE responses.
|
||||
- Don't send tunnel HELLO when there are pending control messages.
|
||||
- Move plugin_radius_reset from *ctl to auto* plugins.
|
||||
- Add Cisco-AVPairs to RADIUS accounting records via plugin_radius_account.
|
||||
|
||||
* Mon Sep 19 2005 Brendan O'Dea <bod@optus.net> 2.1.8
|
||||
- Move code from signal handlers into mainloop, avoiding a race
|
||||
condition when forking CLI.
|
||||
|
||||
* Fri Sep 16 2005 Brendan O'Dea <bod@optus.net> 2.1.7
|
||||
- This time, for sure: really fix Protocol-Reject.
|
||||
|
||||
* Fri Sep 16 2005 Brendan O'Dea <bod@optus.net> 2.1.6
|
||||
- Any traffic on a tunnel resets lastrec, not just control messages.
|
||||
- Use a unique identifier for LCP.
|
||||
- Fix Code-Reject/Protocol-Reject.
|
||||
- Add l2tp_mtu configuration option, used to define MRU, MSS.
|
||||
- Adjust TCP MSS options in SYN and SYN,ACK packets to avoid
|
||||
fragmentation of tcp packets.
|
||||
|
||||
* Sat Sep 3 2005 Brendan O'Dea <bod@optus.net> 2.1.5
|
||||
- Avoid Code-Reject loop.
|
||||
- Increase size of PPP buffers to MAXETHER.
|
||||
- Bug fixes for CLI ringbuffer and tunnel HELLO from Yuri.
|
||||
- Restart rather than halt BGP on receipt of CEASE (Dominique Rousseau).
|
||||
- Add cluster_mcast_ttl option to allow a cluster to span multiple
|
||||
subnets (suggested by Tim Devries).
|
||||
|
||||
* Mon Aug 29 2005 Brendan O'Dea <bod@optus.net> 2.1.4
|
||||
- Drop level of "Unexpected CHAP message" log.
|
||||
- Fix parsing of ProtocolRej (allow 1 or two byte protocols).
|
||||
- Handle rejection of MRU negotiation by peer.
|
||||
- Use local hostname for tunnel in SCCRP (Alex Kiernan).
|
||||
|
||||
* Wed Aug 17 2005 Brendan O'Dea <bod@optus.net> 2.1.3
|
||||
- Fail IPCP negotiation only on ConfigRej of IP-Address.
|
||||
|
||||
* Wed Aug 10 2005 Brendan O'Dea <bod@optus.net> 2.1.2
|
||||
- Clear cluster_master on election so that slaves will accept a new master.
|
||||
- Provide more comments/defaults in etc/startup-config.default.
|
||||
- Add DAE support (PoD/CoA) from Vladislav Bjelic.
|
||||
- Clean up new warnings from gcc 4.0.
|
||||
- Replace flags used for LCP/IPCP with state machine.
|
||||
- Include Acct-Session-Time in interim records.
|
||||
- Fix DAE vector, generateload (Alex Kiernan).
|
||||
- Replace RSA MD5 with public domain version.
|
||||
|
||||
* Tue Jun 14 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.1
|
||||
- Add missing newline to backtrace macro.
|
||||
- Don't send CDN for each session when shutting down tunnels (this is
|
||||
implicit).
|
||||
- Move tunnel shutdown from SIGQUIT signal handler to be run once from
|
||||
still_busy(). Reject new tunnels/sessions while in the process of
|
||||
shutting down.
|
||||
- Clarify usage of shutdown signals in documentation.
|
||||
- Always initialise PRNG.
|
||||
- Sanity check length of random_vector.
|
||||
- Fix segv in unhide_value.
|
||||
- Ping new master when we get C_MASTER and delay next election to allow
|
||||
the unicast limp-along code to kick in if required.
|
||||
|
||||
* Sun Jun 5 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.0
|
||||
- Add IPv6 support from Jonathan McDowell.
|
||||
- Add CHAP support from Jordan Hrycaj.
|
||||
- Add interim accounting support from Vladislav Bjelic.
|
||||
- Negotiate MRU, default 1458 to avoid fragmentation.
|
||||
- Sanity check that cluster_send_session is not called from a child
|
||||
process.
|
||||
- Use bounds-checking lookup functions for string constants.
|
||||
- Add enum for RADIUS codes.
|
||||
- Make "call_" prefix implict in CSTAT() macro.
|
||||
- Fix some format string problems.
|
||||
- Remove "save_state" option. Not maintained anymore; use clustering
|
||||
to retain state across restarts.
|
||||
- Simplify AVP unhiding code.
|
||||
- Add optional "username" parameter to ungarden control, allowing the
|
||||
username to be reset before going online.
|
||||
- Add result/error codes and message to StopCCN when shutting down tunnels.
|
||||
- Add result/error codes to CDN when shutting down sessions. Sends 2/7
|
||||
(general error, try another LNS) when out of IP addresses, and 3
|
||||
(adminstrative) for everything else (suggestion from Chris Gates).
|
||||
- Use cli_error() for error messages and help.
|
||||
- Don't use LOG() macro in initdata() until the config struct has been
|
||||
allocated (uses config->debug).
|
||||
- Initialise log_stream to stderr to catch errors before the config file
|
||||
is read.
|
||||
- Make "show running-config" a privileged command (contains clear text
|
||||
shared secrets).
|
||||
- Add sessionctl plugin to provide drop/kill via nsctl.
|
||||
- New config option: allow_duplicate_users which determines whether
|
||||
or not to kill older sessions with the same username.
|
||||
- Fix byte counters in accounting records.
|
||||
- Add Acct-Output-Gigawords, Acct-Input-Gigawords attributes to RADIUS
|
||||
accounting packets.
|
||||
- Fix icmp host unreachable to use router address.
|
||||
- Include endpoint address in accounting dump files.
|
||||
- Convert mainloop to use epoll rather than select.
|
||||
- Add note about fragmentation in Docs/manual.html, and a sample
|
||||
iptables rule for MSS clamping.
|
||||
- Merge 2.0.22:
|
||||
+ Show session open time in "show session"/"show user" detailed output.
|
||||
+ Have slaves with BGP configured drop BGP on receipt of a shutdown
|
||||
signal, but hang about for an additional 5s to process any remaining
|
||||
traffic.
|
||||
+ Run regular_cleanups after processing the results of the select,
|
||||
looking at a sufficient slice of each table to ensure that all
|
||||
entries are examined at least once per second.
|
||||
- Merge 2.0.21:
|
||||
+ Cluster changes from Michael, intended to prevent a stray master
|
||||
from trashing a cluster:
|
||||
= Ignore heartbeats from peers claiming to be the master before the
|
||||
timeout on the old master has expired.
|
||||
= A master receiving a stray heartbeat sends a unicast HB back, which
|
||||
should cause the rogue to die due to the tie-breaker code.
|
||||
= Keep probing the master for late heartbeats.
|
||||
= Drop BGP as soon as we become master with the minumum required peers.
|
||||
= Any PING seen from a master forces an election (rather than just
|
||||
where basetime is zero).
|
||||
= A slave which receives a LASTSEEN message (presumably a restarted
|
||||
master) sends back new message type, C_MASTER which indicates the
|
||||
address of the current master.
|
||||
+ New config option: cluster_master_min_adv which determines the minimum
|
||||
number of up to date slaves required before the master will drop
|
||||
routes.
|
||||
- Merge 2.0.20:
|
||||
+ Add handling of "throttle=N" RADIUS attributes.
|
||||
+ Fix RADIUS indexing (should have 16K entries with 64 sockets).
|
||||
- Merge 2.0.19:
|
||||
+ Fix leak in session freelist when initial RADIUS session allocation
|
||||
fails.
|
||||
- Merge 2.0.18:
|
||||
+ Add a Cisco-Avpair with intercept details to RADIUS Start/Stop
|
||||
records.
|
||||
- Merge 2.0.17:
|
||||
+ Only send RADIUS stop record in sessionshutdown when there's an ip address.
|
||||
+ Reset .die on master takeover (so that dying sessions don't have to
|
||||
hang around until the new master has the same uptime as the old one).
|
||||
+ Update .last_packet in cluster_handle_bytes only when there have
|
||||
been bytes received from the modem (dead sessions were having the
|
||||
idle timeout reset by stray packets).
|
||||
- Merge 2.0.16:
|
||||
+ Ensure that sessionkill is not called on an unopened session (borks
|
||||
the freelist).
|
||||
+ Bump MAXSESSION to 60K.
|
||||
+ Fix off-by-one errors in session/tunnel initialisation and
|
||||
sessiont <-> sessionidt functions.
|
||||
+ Use session[s].opened consistently when checking for in-use sessions
|
||||
(rather than session[s].tunnel).
|
||||
+ Use <= cluster_highest_sessionid rather than < MAXSESSION in a
|
||||
couple of loops.
|
||||
+ Don't kill a whole tunnel if we're out of sessions.
|
||||
+ Change session[s].ip to 0 if set from RADIUS to 255.255.255.254;
|
||||
avoids the possibility that it will be interpreted as a valid IP
|
||||
address.
|
||||
+ Avoid a possible buffer overflow in processpap.
|
||||
+ Kill session if authentication was rejected.
|
||||
- Merge 2.0.15:
|
||||
+ More DoS prevention: add packet_limit option to apply a hard limit
|
||||
to downstream packets per session.
|
||||
+ Fix "clear counters".
|
||||
+ Log "Accepted connection to CLI" at 4 when connection is from localhost
|
||||
to reduce noise in logs.
|
||||
+ Show time since last counter reset in "show counters".
|
||||
- Merge 2.0.14:
|
||||
+ Throttle outgoing LASTSEEN packets to at most one per second for a
|
||||
given seq#.
|
||||
|
||||
* Fri Dec 17 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.13
|
||||
- Better cluster master collision resolution: keep a counter of state
|
||||
changes, propagated in the heartbeats; the master with the highest #
|
||||
of changes (that has kept in contact with the LAC through the
|
||||
outage) prevails.
|
||||
- Skip newlines in ringbuffer messages to CLI.
|
||||
- Drop "Session N is closing" message level to 4; don't process PPPIP
|
||||
packets in this state.
|
||||
- Use gzip --best for man pages, include pid_file in sample
|
||||
startup-config (from Jonathan's Debian package patches).
|
||||
- Read multiple packets off cluster_sockfd as well as udpfd, tunfd in an
|
||||
attempt to avoid losing the cluster in high load (DoS) conditions.
|
||||
- Add counters for select_called, multi_read_used and multi_read_exceeded.
|
||||
- Compress logs.
|
||||
- Retain counters of shutdown sessions to dump once per minute.
|
||||
- Use standard uintN_t types for portability.
|
||||
|
||||
* Wed Dec 1 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.12
|
||||
- The "This time, for sure!" release.
|
||||
- Fix throttlectl plugin argument parsing.
|
||||
|
||||
* Wed Dec 1 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.11
|
||||
- Don't send a RADIUS start record when ungardening on shutdown.
|
||||
|
||||
* Wed Dec 1 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.10
|
||||
- Fix byte ordering of LCP header length (thanks Yuri).
|
||||
- Increase ip_conntrack_max due to dropped packets.
|
||||
|
||||
* Tue Nov 30 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.9
|
||||
- Revise CCP, send ConfigReq once only.
|
||||
- Don't copy the old buffer into Config{Nak,Rej} LCP responses (oops);
|
||||
add length checks when appending.
|
||||
- Do copy the identifier from the request and update length.
|
||||
- Have makeppp print a backtrace on overflow.
|
||||
- Check control serial before clearing window, prevents looping tunnel
|
||||
setup in some instances.
|
||||
- Implement named access-lists which may be applied to a session
|
||||
either via Filter-Id RADIUS responses, or using the CLI.
|
||||
- Drop ip address from LOG.
|
||||
- autothrottle: revise parsing; ignore lcp:interface-config avpairs
|
||||
which don't start with serv[ice-policy].
|
||||
- Add THANKS file.
|
||||
|
||||
* Sat Nov 20 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8
|
||||
- Ignore gateway address in Framed-Route (from Jonathan McDowell).
|
||||
- Don't route Framed-IP-Address if contained in a Framed-Route.
|
||||
- Call sessionshutdown() when a tunnel is dropped rather than
|
||||
sessionkill() to ensure that RADIUS stop records are sent.
|
||||
- Cleanup: make a bunch of global functions/variables static.
|
||||
- Remove reference to old -a command line argument.
|
||||
- Add l2tpns(8) and nsctl(8) manpages from Jonathan McDowell.
|
||||
- Add startup-config(5) manpage.
|
||||
- Revise nsctl to allow arbitrary strings/args to be passed to plugins.
|
||||
- Add snoopctl, throttlectl plugins.
|
||||
- Fix deletion from linked list.
|
||||
- Allow LCP re-negotiation after connection completes (thanks Yuri).
|
||||
|
||||
* Mon Nov 15 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.7
|
||||
- Fix socket creation in host_unreachable() (thanks to Bjørn Augestad)
|
||||
- Don't assume BGP peer sends back negotiated hold time, pick smallest
|
||||
|
||||
* Thu Nov 11 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.6
|
||||
- Make BGP keepalive/hold time configurable
|
||||
- Revise BGP config to use "router bgp AS" syntax (requires libcli >= 1.8.2)
|
||||
|
||||
* Tue Nov 9 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.5
|
||||
- Handle routing properly in lone-master case
|
||||
- Fix intercepts: don't double-snoop throttled customers, ensure
|
||||
byte/packet counts are only updated once
|
||||
- Add a callback to allow plugins to fetch values from the running config
|
||||
|
||||
* Mon Nov 8 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.4
|
||||
- Added setrxspeed plugin
|
||||
- Added peer_address config option
|
||||
- Rename wrapper macros to LOG()/LOG_HEX(), use p->log() in plugins
|
||||
- Replace some PPP{PAP,CHAP} magic numebrs with constants
|
||||
- Nak asyncmap (unless == 0)
|
||||
- Bundle ConfigRej options
|
||||
- Clean up initlcp handling
|
||||
|
||||
* Wed Nov 3 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.3
|
||||
- Added support for hidden AVPs by Robert Clark
|
||||
- l2tpns-chap-response.patch from Robert Clark
|
||||
- Merge l2tpns-config-hostname.patch from Robert Clark
|
||||
- l2tpns-dont-timeshift-unidirectional-traffic.patch from Robert Clark - Dump accounting data if cin OR cout is non-zero
|
||||
- Don't write accounting files if no accounting dir is set - Yuri
|
||||
- Fix checking for mmap success
|
||||
- Renegotiate MRU - Yuri
|
||||
- Take LCP ConfigReq length from the packet length field - Yuri
|
||||
- Hostname set via command line not config
|
||||
- Make number of throttle buckets configurable
|
||||
- Shared_malloc returns NULL on failure
|
||||
- Sync changes
|
||||
- Unfsck 4->8 indenting change
|
||||
- Use 2 seperate u16 values for throttle rate in/out
|
||||
- Defer adding radius fds to the select loop until become_master
|
||||
|
||||
* Thu Sep 02 2004 David Parrish <david@dparrish.com> 2.0.2
|
||||
- Combined LCP patches from Iain and Yuri. This should allow Windows 2k/XP
|
||||
clients to connect, as well Linksys DSL modems.
|
||||
- Apply patch to fix -v option from Juergen Kammer.
|
||||
- Makefile fix from Juergen Kammer to not overwrite existing config files on
|
||||
make install
|
||||
- Configurable radius port patch from Juergen Kammer.
|
||||
- Send my_address if no bind_address when doing IPCP
|
||||
- Write pid file if filename is set
|
||||
- Add startup script and monitor script from Yuri
|
||||
- Some logging correctness fixes from Iain Wade
|
||||
- Add support for LCP Ident and CallBack (rejection only) from Yuri
|
||||
- Initiate LCP if not attempted by the client, or in renegotiation - Yuri
|
||||
- Indentation and style cleanups
|
||||
- Per-user upload and download throttle rates - Yuri
|
||||
- Make autothrottle.so understand cisco lcp:interface-config - Yuri
|
||||
- Show filter stats in show session - Yuri
|
||||
- Cleanup from Michael to change sid to unique_id
|
||||
- Add plugin to remove domain name from auth requests
|
||||
- Add .spec file for RPM generation
|
||||
|
||||
* Tue Jul 13 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.1
|
||||
- Update INSTALL, Docs/manual.html documentation.
|
||||
- Add INTERNALS documentation.
|
||||
- Add lock_pages option.
|
||||
- TerminateAck fix from Yuri
|
||||
- Adject cli_loop args for libcli 1.8.0
|
||||
- Allow for backward compatabity in C_PING packets
|
||||
- Don't send RADIUS stop messages from sessionshutdown when called from
|
||||
sessionkill.
|
||||
- s/tap/tun/ .
|
||||
- Fix for LASTSEEN breakage: don't do anything in the CLI other than
|
||||
flag changes to be made by the parent.
|
||||
- Split out master parts from cluster_check_master() into cluster_check_slaves().
|
||||
- Set hostname in CLI prompt.
|
||||
- Make cluster_hb_interval work; include interval/timeout in heartbeats
|
||||
so that a change on the master is propagated immediately to the slaves.
|
||||
- Use fast heartbeats when there are slaves not up to date.
|
||||
- Ensure basetime of shut down master is set to zero (prevent delayed election).
|
||||
- Fix radius session leak on IPCP timeout.
|
||||
- Fix some off-by-one errors in tunnel/session loops.
|
||||
- Add "limp along" fix for when a slave drops temporarily from the mcast group.
|
||||
- Rename l2tpns.cfg as startup-config to match CONFIGFILE.
|
||||
- Update cli callbacks to work with libcli 1.6.
|
||||
This supports privileged and unprivileged commands, as well as a configuration
|
||||
mode.
|
||||
- Add help for all cli commands.
|
||||
- Add "show version" command.
|
||||
- Fix uptime counter display.
|
||||
- Fix nasty bug where cluster basetime can be set to 0 when sending initial
|
||||
heartbeat.
|
||||
- Don't rmmod ip_conntrack, as this can take a lot of time.
|
||||
- Re-order logging in routeset such that the action is given before any error.
|
||||
- Use the correct gateway address when deleting routes.
|
||||
- Remove any routes when address changes.
|
||||
- Require authentication if telnet from remote ip.
|
||||
- Require enable password always.
|
||||
- Return error if show pool done on slave.
|
||||
- We MUST immediately exit if we're the wrong master!
|
||||
|
||||
* Wed Jun 23 2004 David Parrish <david@dparrish.com> 2.0.0
|
||||
- Major release
|
||||
- Completely replace active/standby clustering with a new peer-to-peer
|
||||
clustering method which allows much greater throughput and is a lot more fault
|
||||
tolerant
|
||||
- Add internal tbf implementation for throttling without relying on tc and
|
||||
kernel HTB
|
||||
- Add support for iBGP and eBGP to advertise routes
|
||||
- Add cli commands "show cluster", "show bgp", "show ipcache", "show throttle",
|
||||
"show tbf", "suspend bgp", "restart bgp", "show user"
|
||||
- Interception destination must be set per-user
|
||||
- If SMP machine, allow use of SCHED_FIFO, which should improve performance
|
||||
- Added config option to send GARP at startup
|
||||
- Added plugin_become_master and plugin_new_session_master plugin hooks
|
||||
- Remove useless sessionsendarp(). This isn't needed now that we are using TUN
|
||||
instead of TAP.
|
||||
- ICMP rate limiting so not every unreachable packet is replied with an ICMP
|
||||
unreachable message
|
||||
- mangle table is not required on anything but the cluster master, so slaves
|
||||
will drop the mangle table and attempt to unload the ip_conntrack module
|
||||
- Statically assigned IP addresses (by Radius) work now
|
||||
- Add -d command-line flag to detach and become a daemon
|
||||
- Configuration file is now "/etc/l2tpns/startup-config"
|
||||
- Reduced MIN_IP_SIZE to 0x19 to stop a pile of Short IP warnings
|
||||
- Resend initial IPCP request until it's acknowleged by the client
|
||||
- Better radius session cleanup logic
|
||||
- Many miscellaenous bugfixes and performance enhancements
|
||||
- Thanks to Michael O'Reilly and Brendan O'Dea for most of these new features
|
||||
|
||||
* Mon May 24 2004 David Parrish <david@dparrish.com> 1.2.0
|
||||
- Fix SEGFAULT in garden module
|
||||
- Use multiple radius sockets to allow more concurrent authentication requests
|
||||
- Add username parameter to "show users" command
|
||||
- Fix counting tunnel rx errors as tunnel tx errors
|
||||
- Add "show throttle" command
|
||||
- Add gcc __attribute__ to logging functions
|
||||
- Fix warnings shown by __attribute__
|
||||
- Make sure regular cleanup happens regularly under high load
|
||||
- Add variable cleanup_interval for changing cleanup interval
|
||||
- Add support for reading more than one packet per fd in each processing loop
|
||||
- This is configurable with the multi_read_count variable
|
||||
- Remove segv handler so core dumps can happen
|
||||
- Use nonblocking sockets
|
||||
- Increase tun queue length
|
||||
- Fix minimum length of IP packets
|
||||
- Remove per-packet plugin hooks (they are slow)
|
||||
- Don't drop session if no free RADIUS
|
||||
- Don't expire more than 1000 sessions per cleanup interval
|
||||
- Remove -a and -c command-line options. They don't work anyway
|
||||
- Don't require file: in log_filename
|
||||
- Bump version to 1.2.0
|
||||
- Check return code when throttling users
|
||||
|
||||
* Mon Apr 5 2004 David Parrish <david@dparrish.com> 1.1.1
|
||||
- Don't mention configure anymore, it's not used
|
||||
- Added the autosnoop and autothrottle modules
|
||||
- Don't default to using a htb for the class root
|
||||
|
||||
* Fri Mar 5 2004 David Parrish <david@dparrish.com> 1.1.0
|
||||
- Change all strcpy() calls to strncpy() to avoid buffer overflow potential
|
||||
- Add ICMP host unreachable support
|
||||
- Logging to syslog if log_file = "syslog:facility"
|
||||
- Now requires libcli 1.5
|
||||
- All configuration moves to a config structure
|
||||
- Ability to modify and write config on the fly through command-line interface
|
||||
- Config file support is removed, and now handled by the cli
|
||||
- Show hostname in cli prompt
|
||||
- Keep current state type for tunnels
|
||||
- Add uptime command do CLI, which also shows real-time bandwidth utilisation
|
||||
- Add goodbye command to cluster master, which forces droppping a slave
|
||||
- Cache IP address allocation, so that reconnecting users get the same address
|
||||
- Fix tunnel resend timeouts, so that dead tunnels will be cleaned up
|
||||
- Allocate tunnels and radius without using a linked list which had issues
|
||||
- Fix some off-by-one errors in tunnel and session and radius arrays
|
||||
- Save and reload ip address pool when dieing
|
||||
- Check version and size of reloaded data when restarting
|
||||
- Remove plugin_config support
|
||||
- Remove old support for TBF which didn't work anyway. HTB is required to do throttling now.
|
||||
- Add COPYING and Changes files
|
||||
70
Docs/l2tpns.8
Normal file
70
Docs/l2tpns.8
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
.\" -*- nroff -*-
|
||||
.de Id
|
||||
.ds Dt \\$4 \\$5
|
||||
..
|
||||
.Id $Id: l2tpns.8,v 1.4 2005/06/12 06:09:35 bodea Exp $
|
||||
.TH L2TPNS 8 "\*(Dt" L2TPNS "System Management Commands"
|
||||
.SH NAME
|
||||
l2tpns \- Layer 2 tunneling protocol network server (LNS)
|
||||
.SH SYNOPSIS
|
||||
.B l2tpns
|
||||
.RB [ \-d ]
|
||||
.RB [ \-v ]
|
||||
.RB [ \-c
|
||||
.IR file ]
|
||||
.RB [ \-h
|
||||
.IR hostname ]
|
||||
.SH DESCRIPTION
|
||||
.B l2tpns
|
||||
is a daemon for terminating layer 2 tunneling protocol (L2TP: RFC
|
||||
2661) sessions.
|
||||
.PP
|
||||
Once running,
|
||||
.B l2tpns
|
||||
may be controlled by telnetting to port 23 on the machine running the
|
||||
daemon and with the
|
||||
.B nsctl
|
||||
utility.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-d
|
||||
Detach from terminal and fork into the background. By default l2tpns
|
||||
will stay in the foreground.
|
||||
.TP
|
||||
.B \-v
|
||||
Increase verbosity for debugging. Can be used multiple times.
|
||||
.TP
|
||||
.BI "\-c " file
|
||||
Specify configuration file.
|
||||
.TP
|
||||
.BI "\-h " hostname
|
||||
Force hostname to
|
||||
.IR hostname .
|
||||
.SH FILES
|
||||
.TP
|
||||
.I /etc/l2tpns/startup-config
|
||||
The default configuration file.
|
||||
.TP
|
||||
.I /etc/l2tpns/ip_pool
|
||||
IP address pool configuration.
|
||||
.TP
|
||||
.I /etc/l2tpns/users
|
||||
Username/password configuration for access to admin interface.
|
||||
.SH SIGNALS
|
||||
.TP
|
||||
.B SIGHUP
|
||||
Reload the config from disk and re-open log file.
|
||||
.TP
|
||||
.BR SIGTERM ", " SIGINT
|
||||
Stop process. Tunnels and sessions are not terminated. This signal
|
||||
should be used to stop l2tpns on a cluster node where there are other
|
||||
machines to continue handling traffic.
|
||||
.TP
|
||||
.B SIGQUIT
|
||||
Shut down tunnels and sessions, exit process when complete.
|
||||
.SH SEE ALSO
|
||||
.BR startup-config (5),
|
||||
.BR nsctl (8)
|
||||
.SH AUTHOR
|
||||
This manual page was written by Jonathan McDowell <noodles@earth.li>,
|
||||
for the Debian GNU/Linux system (but may be used by others).
|
||||
1074
Docs/manual.html
Normal file
1074
Docs/manual.html
Normal file
File diff suppressed because it is too large
Load diff
69
Docs/nsctl.8
Normal file
69
Docs/nsctl.8
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
.\" -*- nroff -*-
|
||||
.de Id
|
||||
.ds Dt \\$4 \\$5
|
||||
..
|
||||
.Id $Id: nsctl.8,v 1.2 2004/11/17 15:08:19 bodea Exp $
|
||||
.TH NSCTL 8 "\*(Dt" L2TPNS "System Management Commands"
|
||||
.SH NAME
|
||||
nsctl \- manage running l2tpns instance
|
||||
.SH SYNOPSIS
|
||||
.B nsctl
|
||||
.RB [ \-d ]
|
||||
.RB [ \-h
|
||||
.IR host [: port ]]
|
||||
.RB [ \-t
|
||||
.IR timeout ]
|
||||
.I command
|
||||
.RI [ arg " ...]"
|
||||
.SH DESCRIPTION
|
||||
.B nsctl
|
||||
sends commands to a running
|
||||
.B l2tpns
|
||||
process. It provides both for the loading or unloading of plugins and
|
||||
also the management of sessions via functions provided by those plugins.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-d
|
||||
Enable debugging output.
|
||||
.TP
|
||||
.B \-h \fIhost\fR[:\fIport\fR]
|
||||
The host running
|
||||
.B l2tpns
|
||||
that should receive the message. By default the message is sent to
|
||||
UDP port 1702 on
|
||||
.BR localhost .
|
||||
.TP
|
||||
.B \-t \fItimeout\fR
|
||||
Timeout in seconds to wait for a response from the server.
|
||||
.SH COMMANDS
|
||||
The first argument specifies the command to send to
|
||||
.B l2tpns .
|
||||
The following commands are as defined:
|
||||
.TP
|
||||
.BI "load_plugin " plugin
|
||||
Load the named
|
||||
.IR plugin .
|
||||
.TP
|
||||
.BI "unload_plugin " plugin
|
||||
Unload the named
|
||||
.IR plugin .
|
||||
.TP
|
||||
.B help
|
||||
Each loaded plugin is queried for what commands it supports and the
|
||||
synopsis for each is output.
|
||||
.PP
|
||||
Any other value of
|
||||
.I command
|
||||
(and
|
||||
.I args
|
||||
if any)
|
||||
are sent to
|
||||
.B l2tpns
|
||||
as-is, to be passed to each plugin which registers a
|
||||
.B plugin_control
|
||||
function in turn (in which it may be acted upon).
|
||||
.SH SEE ALSO
|
||||
.BR l2tpns (8)
|
||||
.SH AUTHOR
|
||||
This manual page was written by Jonathan McDowell <noodles@the.earth.li>,
|
||||
for the Debian GNU/Linux system (but may be used by others).
|
||||
363
Docs/startup-config.5
Normal file
363
Docs/startup-config.5
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
.\" -*- nroff -*-
|
||||
.de Id
|
||||
.ds Dt \\$4 \\$5
|
||||
..
|
||||
.Id $Id: startup-config.5,v 1.15 2005/09/16 05:04:31 bodea Exp $
|
||||
.TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions"
|
||||
.SH NAME
|
||||
startup\-config \- configuration file for l2tpns
|
||||
.SH SYNOPSIS
|
||||
/etc/l2tpns/startup-config
|
||||
.SH DESCRIPTION
|
||||
.B startup-config
|
||||
is the configuration file for
|
||||
.BR l2tpns .
|
||||
.PP
|
||||
The format is plain text, in the same format as accepted by the
|
||||
configuration mode of
|
||||
.BR l2tpns 's
|
||||
telnet administrative interface. Comments are indicated by either the
|
||||
character
|
||||
.B #
|
||||
or
|
||||
.BR ! .
|
||||
.SS SETTINGS
|
||||
Settings are specified with
|
||||
.IP
|
||||
.BI "set " "variable value"
|
||||
.PP
|
||||
The following
|
||||
.IR variable s
|
||||
may be set:
|
||||
.RS
|
||||
.TP
|
||||
.B debug
|
||||
Set the level of debugging messages written to the log file. The
|
||||
value should be between 0 and 5, with 0 being no debugging, and 5
|
||||
being the highest.
|
||||
.TP
|
||||
.B log_file
|
||||
This will be where all logging and debugging information is written
|
||||
to. This may be either a filename, such as
|
||||
.BR /var/log/l2tpns ,
|
||||
or the string
|
||||
.BR syslog : \fIfacility\fR ,
|
||||
where
|
||||
.I facility
|
||||
is any one of the syslog logging facilities, such as
|
||||
.BR local5 .
|
||||
.TP
|
||||
.B pid_file
|
||||
If set, the process id will be written to the specified file. The
|
||||
value must be an absolute path.
|
||||
.TP
|
||||
.B random_device
|
||||
Path to random data source (default
|
||||
.BR /dev/urandom ).
|
||||
Use "" to use the rand() library function.
|
||||
.TP
|
||||
.B l2tp_secret
|
||||
The secret used by
|
||||
.B l2tpns
|
||||
for authenticating tunnel request. Must be the same as the LAC, or
|
||||
authentication will fail. Only actually be used if the LAC requests
|
||||
authentication.
|
||||
.TP
|
||||
.B l2tp_mtu
|
||||
MTU of interface for L2TP traffic (default: 1500). Used to set link
|
||||
MRU and adjust TCP MSS.
|
||||
.TP
|
||||
.B ppp_restart_time
|
||||
Restart timer for PPP protocol negotiation in seconds (default: 3).
|
||||
.TP
|
||||
.B ppp_max_configure
|
||||
Number of configure requests to send before giving up (default: 10).
|
||||
.TP
|
||||
.B ppp_max_failure
|
||||
Number of Configure-Nak requests to send before sending a
|
||||
Configure-Reject (default: 5).
|
||||
.TP
|
||||
.BR primary_dns , " secondary_dns"
|
||||
Whenever a PPP connection is established, DNS servers will be sent to the
|
||||
user, both a primary and a secondary. If either is set to 0.0.0.0, then that
|
||||
one will not be sent.
|
||||
.TP
|
||||
.BR primary_radius , " secondary_radius"
|
||||
Sets the RADIUS servers used for both authentication and accounting.
|
||||
If the primary server does not respond, then the secondary RADIUS
|
||||
server will be tried.
|
||||
.TP
|
||||
.BR primary_radius_port , " secondary_radius_port"
|
||||
Sets the authentication ports for the primary and secondary RADIUS
|
||||
servers. The accounting port is one more than the authentication
|
||||
port. If no ports are given, authentication defaults to 1645, and
|
||||
accounting to 1646.
|
||||
.TP
|
||||
.B radius_accounting
|
||||
If set to true, then RADIUS accounting packets will be sent. A
|
||||
.B Start
|
||||
record will be sent when the session is successfully authenticated,
|
||||
and a
|
||||
.B Stop
|
||||
record when the session is closed.
|
||||
.TP
|
||||
.B radius_interim
|
||||
If
|
||||
.B radius_accounting
|
||||
is on, defines the interval between sending of RADIUS interim
|
||||
accounting records (in seconds).
|
||||
.TP
|
||||
.B radius_secret
|
||||
Secret to be used in RADIUS packets.
|
||||
.TP
|
||||
.B radius_authtypes
|
||||
A comma separated list of supported RADIUS authentication methods
|
||||
("pap" or "chap"), in order of preference (default "pap").
|
||||
.TP
|
||||
.B radius_dae_port
|
||||
Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization)
|
||||
requests (default: 3799).
|
||||
.TP
|
||||
.B allow_duplicate_users
|
||||
Allow multiple logins with the same username. If false (the default),
|
||||
any prior session with the same username will be dropped when a new
|
||||
session is established.
|
||||
.TP
|
||||
.B bind_address
|
||||
When the tun interface is created, it is assigned the address
|
||||
specified here. If no address is given, 1.1.1.1 is used. Packets
|
||||
containing user traffic should be routed via this address if given,
|
||||
otherwise the primary address of the machine.
|
||||
.TP
|
||||
.B peer_address
|
||||
Address to send to clients as the default gateway.
|
||||
.TP
|
||||
.B send_garp
|
||||
Determines whether or not to send a gratuitous ARP for the
|
||||
.B bind_address
|
||||
when the server is ready to handle traffic (default: true). This
|
||||
setting is ignored if BGP is configured.
|
||||
.TP
|
||||
.B throttle_speed
|
||||
Sets the default speed (in kbits/s) which sessions will be limited to.
|
||||
.TP
|
||||
.B throttle_buckets
|
||||
Number of token buckets to allocate for throttling. Each throttled
|
||||
session requires two buckets (in and out).
|
||||
.TP
|
||||
.B accounting_dir
|
||||
If set to a directory, then every 5 minutes the current usage for
|
||||
every connected use will be dumped to a file in this directory.
|
||||
.TP
|
||||
.B setuid
|
||||
After starting up and binding the interface, change UID to this. This
|
||||
doesn't work properly.
|
||||
.TP
|
||||
.B dump_speed
|
||||
If set to true, then the current bandwidth utilization will be logged
|
||||
every second. Even if this is disabled, you can see this information
|
||||
by running the
|
||||
.B uptime
|
||||
command on the CLI.
|
||||
.TP
|
||||
.B multi_read_count
|
||||
Number of packets to read off each of the UDP and TUN fds when
|
||||
returned as readable by select (default: 10). Avoids incurring the
|
||||
unnecessary system call overhead of select on busy servers.
|
||||
.TP
|
||||
.B scheduler_fifo
|
||||
Sets the scheduling policy for the
|
||||
.B l2tpns
|
||||
process to
|
||||
.BR SCHED_FIFO .
|
||||
This causes the kernel to immediately preempt any currently running
|
||||
.B SCHED_OTHER
|
||||
(normal) process in favour of
|
||||
.B l2tpns
|
||||
when it becomes runnable.
|
||||
.br
|
||||
Ignored on uniprocessor systems.
|
||||
.TP
|
||||
.B lock_pages
|
||||
Keep all pages mapped by the
|
||||
.B l2tpns
|
||||
process in memory.
|
||||
.TP
|
||||
.B icmp_rate
|
||||
Maximum number of host unreachable ICMP packets to send per second.
|
||||
.TP
|
||||
.B packet_limit
|
||||
Maximum number of packets of downstream traffic to be handled each
|
||||
tenth of a second per session. If zero, no limit is applied (default:
|
||||
0). Intended as a DoS prevention mechanism and not a general
|
||||
throttling control (packets are dropped, not queued).
|
||||
.TP
|
||||
.B cluster_address
|
||||
Multicast cluster address (default: 239.192.13.13).
|
||||
.TP
|
||||
.B cluster_interface
|
||||
Interface for cluster packets (default: eth0).
|
||||
.TP
|
||||
.B cluster_mcast_ttl
|
||||
TTL for multicast packets (default: 1).
|
||||
.TP
|
||||
.B cluster_hb_interval
|
||||
Interval in tenths of a second between cluster heartbeat/pings.
|
||||
.TP
|
||||
.B cluster_hb_timeout
|
||||
Cluster heartbeat timeout in tenths of a second. A new master will be
|
||||
elected when this interval has been passed without seeing a heartbeat
|
||||
from the master.
|
||||
.TP
|
||||
.B cluster_master_min_adv
|
||||
Determines the minumum number of up to date slaves required before the
|
||||
master will drop routes (default: 1).
|
||||
.TP
|
||||
.B ipv6_prefix
|
||||
Enable negotiation of IPv6. This forms the the first 64 bits of the
|
||||
client allocated address. The remaining 64 come from the allocated
|
||||
IPv4 address and 4 bytes of 0s.
|
||||
.RE
|
||||
.SS BGP ROUTING
|
||||
The routing configuration section is entered by the command
|
||||
.IP
|
||||
.BI "router bgp " as
|
||||
.PP
|
||||
where
|
||||
.I as
|
||||
specifies the local AS number.
|
||||
.PP
|
||||
Subsequent lines prefixed with
|
||||
.BI "neighbour " peer
|
||||
define the attributes of BGP neighhbours. Valid commands are:
|
||||
.IP
|
||||
.BI "neighbour " peer " remote-as " as
|
||||
.br
|
||||
.BI "neighbour " peer " timers " "keepalive hold"
|
||||
.PP
|
||||
Where
|
||||
.I peer
|
||||
specifies the BGP neighbour as either a hostname or IP address,
|
||||
.I as
|
||||
is the remote AS number and
|
||||
.IR keepalive ,
|
||||
.I hold
|
||||
are the timer values in seconds.
|
||||
.SS NAMED ACCESS LISTS
|
||||
Named access lists may be defined with either of
|
||||
.IP
|
||||
.BI "ip access\-list standard " name
|
||||
.br
|
||||
.BI "ip access\-list extended " name
|
||||
.PP
|
||||
Subsequent lines starting with
|
||||
.B permit
|
||||
or
|
||||
.B deny
|
||||
define the body of the access\-list.
|
||||
.PP
|
||||
.B Standard Access Lists
|
||||
.RS 4n
|
||||
Standard access lists are defined with:
|
||||
.IP
|
||||
.RB { permit | deny }
|
||||
.IR source " [" dest ]
|
||||
.PP
|
||||
Where
|
||||
.I source
|
||||
and
|
||||
.I dest
|
||||
specify IP matches using one of:
|
||||
.IP
|
||||
.I address
|
||||
.I wildard
|
||||
.br
|
||||
.B host
|
||||
.I address
|
||||
.br
|
||||
.B any
|
||||
.PP
|
||||
.I address
|
||||
and
|
||||
.I wildard
|
||||
are in dotted-quad notation, bits in the
|
||||
.I wildard
|
||||
indicate which address bits in
|
||||
.I address
|
||||
are relevant to the match (0 = exact match; 1 = don't care).
|
||||
.PP
|
||||
The shorthand
|
||||
.RB ' host
|
||||
.IR address '
|
||||
is equivalent to
|
||||
.RI ' address
|
||||
.BR 0.0.0.0 ';
|
||||
.RB ' any '
|
||||
to
|
||||
.RB ' 0.0.0.0
|
||||
.BR 255.255.255.255 '.
|
||||
.RE
|
||||
.PP
|
||||
.B Extended Access Lists
|
||||
.RS 4n
|
||||
Extended access lists are defined with:
|
||||
.IP
|
||||
.RB { permit | deny }
|
||||
.I proto
|
||||
.IR source " [" ports "] " dest " [" ports "] [" flags ]
|
||||
.PP
|
||||
Where
|
||||
.I proto
|
||||
is one of
|
||||
.BR ip ,
|
||||
.B tcp
|
||||
or
|
||||
.BR udp ,
|
||||
and
|
||||
.I source
|
||||
and
|
||||
.I dest
|
||||
are as described above for standard lists.
|
||||
.PP
|
||||
For TCP and UDP matches, source and destination may be optionally
|
||||
followed by a
|
||||
.I ports
|
||||
specification:
|
||||
.IP
|
||||
.RB { eq | neq | gt | lt }
|
||||
.I port
|
||||
.br
|
||||
.B
|
||||
range
|
||||
.I from to
|
||||
.PP
|
||||
.I flags
|
||||
may be one of:
|
||||
.RS
|
||||
.HP
|
||||
.RB { match\-any | match\-all }
|
||||
.RB { + | - }{ fin | syn | rst | psh | ack | urg }
|
||||
\&...
|
||||
.br
|
||||
Match packets with any or all of the tcp flags set
|
||||
.RB ( + )
|
||||
or clear
|
||||
.RB ( - ).
|
||||
.HP
|
||||
.B established
|
||||
.br
|
||||
Match "established" TCP connections: packets with
|
||||
.B RST
|
||||
or
|
||||
.B ACK
|
||||
set, and
|
||||
.B SYN
|
||||
clear.
|
||||
.HP
|
||||
.B fragments
|
||||
.br
|
||||
Match IP fragments. May not be specified on rules with layer 4
|
||||
matches.
|
||||
.RE
|
||||
.SH SEE ALSO
|
||||
.BR l2tpns (8)
|
||||
74
INSTALL
Normal file
74
INSTALL
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
Brief Installation guide for L2TPNS
|
||||
|
||||
1. Requirements
|
||||
|
||||
* libcli 1.8.5 or greater
|
||||
You can get it from http://sourceforge.net/projects/libcli.
|
||||
|
||||
* A kernel with iptables support.
|
||||
|
||||
|
||||
2. Compile
|
||||
|
||||
* make
|
||||
|
||||
|
||||
3. Install
|
||||
|
||||
* make install. This process:
|
||||
- Installs the binaries into /usr/sbin (l2tpns and nsctl).
|
||||
- Creates the config dir /etc/l2tpns installs default config files.
|
||||
- Ensures that /dev/net/tun exists.
|
||||
|
||||
* Modify config file. You probably need to change most of the config
|
||||
options.
|
||||
|
||||
* Set up basic firewall rules. The l2tpns process listens on a bunch of
|
||||
ports:
|
||||
|
||||
23/tcp command line interface
|
||||
1701/udp l2tp (on bind_address)
|
||||
1702/udp control port (nsctl)
|
||||
3799/udp RADIUS DAE port
|
||||
32792/udp clustering messages
|
||||
|
||||
* If you are using the garden plugin, setup the walled garden firewall
|
||||
rules. These should be in /etc/l2tpns/build-garden, which is run by the
|
||||
plugin after creating/flushing the "garden" nat table.
|
||||
|
||||
iptables -t nat -A garden -p tcp -m tcp --dport 25 -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p udp -m udp --dport 53 -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p tcp -m tcp --dport 53 -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p tcp -m tcp --dport 80 -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p tcp -m tcp --dport 110 -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p tcp -m tcp --dport 443 -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p icmp -m icmp --icmp-type echo-request -j DNAT --to 192.168.1.1
|
||||
iptables -t nat -A garden -p icmp -j ACCEPT
|
||||
iptables -t nat -A garden -j DROP
|
||||
|
||||
* Set up IP address pools in /etc/l2tpns/ip_pool
|
||||
|
||||
* Set up routing.
|
||||
- If you are running a single instance, you can simply statically route
|
||||
the IP pools to the bind_address (l2tpns will send a gratuitous arp).
|
||||
|
||||
- For a cluster, configure the members as BGP neighbours on your router
|
||||
and configure multi-path load-balancing (on Cisco use "maximum-paths").
|
||||
|
||||
* Make l2tpns run on startup. In a clustered environment running from
|
||||
inittab is recomended:
|
||||
|
||||
l2tp:2345:respawn:/home/l2tpns/src/l2tpns >/dev/null 2>&1
|
||||
|
||||
* Test it out.
|
||||
|
||||
|
||||
|
||||
This software is quite stable and is being used in a production environment at
|
||||
a quite large ISP. However, you may have problems setting it up, and if so, I
|
||||
would appreciate it if you would file useful bug reports on the Source Forge
|
||||
page:
|
||||
|
||||
http://sourceforge.net/projects/l2tpns/
|
||||
|
||||
-- David Parrish
|
||||
265
INTERNALS
Normal file
265
INTERNALS
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
Documentation on various internal structures.
|
||||
|
||||
Most important structure use an anonymous shared mmap()
|
||||
so that child processes can watch them. (All the cli connections
|
||||
are handled in child processes).
|
||||
|
||||
TODO: Re-investigate threads to see if we can use a thread to handle
|
||||
cli connections without killing forwarding performance.
|
||||
|
||||
session[]
|
||||
An array of session structures. This is one of the two
|
||||
major data structures that are sync'ed across the cluster.
|
||||
|
||||
This array is statically allocated at startup time to a
|
||||
compile time size (currently 50k sessions). This sets a
|
||||
hard limit on the number of sessions a cluster can handle.
|
||||
|
||||
There is one element per l2tp session. (I.e. each active user).
|
||||
|
||||
The zero'th session is always invalid.
|
||||
|
||||
tunnel[]
|
||||
An array of tunnel structures. This is the other major data structure
|
||||
that's actively sync'ed across the cluster.
|
||||
|
||||
As per sessions, this is statically allocated at startup time
|
||||
to a compile time size limit.
|
||||
|
||||
There is one element per l2tp tunnel. (normally one per BRAS
|
||||
that this cluster talks to).
|
||||
|
||||
The zero'th tunnel is always invalid.
|
||||
|
||||
ip_pool[]
|
||||
|
||||
A table holding all the IP address in the pool. As addresses
|
||||
are used, they are tagged with the username of the session,
|
||||
and the session index.
|
||||
|
||||
When they are free'd the username tag ISN'T cleared. This is
|
||||
to ensure that were possible we re-allocate the same IP
|
||||
address back to the same user.
|
||||
|
||||
radius[]
|
||||
A table holding active radius session. Whenever a radius
|
||||
conversation is needed (login, accounting et al), a radius
|
||||
session is allocated.
|
||||
|
||||
char **ip_hash
|
||||
|
||||
A mapping of IP address to session structure. This is a
|
||||
tenary tree (each byte of the IP address is used in turn
|
||||
to index that level of the tree).
|
||||
|
||||
If the value is postive, it's considered to be an index
|
||||
into the session table.
|
||||
|
||||
If it's negative, it's considered to be an index into
|
||||
the ip_pool[] table.
|
||||
|
||||
If it's zero, then there is no associated value.
|
||||
|
||||
config->cluster_iam_master
|
||||
|
||||
If true, indicates that this node is the master for
|
||||
the cluster. This has many consequences...
|
||||
|
||||
config->cluster_iam_uptodate
|
||||
|
||||
On the slaves, this indicates if it's seen a full run
|
||||
of sessions from the master, and thus it's safe to be
|
||||
taking traffic.
|
||||
|
||||
On the master, this indicates that all slaves are
|
||||
up to date. If any of the slaves aren't up to date,
|
||||
this variable is false, and indicates that we should
|
||||
shift to more rapid heartbeats to bring the slave
|
||||
back up to date.
|
||||
|
||||
|
||||
============================================================
|
||||
|
||||
Clustering: How it works.
|
||||
|
||||
At a high level, the various members of the cluster elect
|
||||
a master. All other machines become slaves as soon as they hear
|
||||
a heartbeat from the master. Slaves handle normal packet forwarding.
|
||||
Whenever a slave get a 'state changing' packet (i.e. tunnel setup/teardown,
|
||||
session setup etc) it _doesn't_ handle it, but instead forwards it
|
||||
to the master.
|
||||
|
||||
'State changing' it defined to be "a packet that would cause
|
||||
a change in either a session or tunnel structure that isn't just
|
||||
updating the idle time or byte counters". In practise, this means
|
||||
almost all LCP, IPCP, and L2TP control packets.
|
||||
|
||||
The master then handles the packet normally, updating
|
||||
the session/tunnel structures. The changed structures are then
|
||||
flooded out to the slaves via a multicast packet.
|
||||
|
||||
|
||||
Heartbeat'ing:
|
||||
The master sends out a multicast 'heartbeat' packet
|
||||
at least once every second. This packet contains a sequence number,
|
||||
and any changes to the session/tunnel structures that have
|
||||
been queued up. If there is room in the packet, it also sends
|
||||
out a number of extra session/tunnel structures.
|
||||
|
||||
The sending out of 'extra' structures means that the
|
||||
master will slowly walk the entire session and tunnel tables.
|
||||
This allows a new slave to catch-up on cluster state.
|
||||
|
||||
|
||||
Each heartbeat has an in-order sequence number. If a
|
||||
slave receives a heartbeat with a sequence number other than
|
||||
the one it was expecting, it drops the unexpected packet and
|
||||
unicasts C_LASTSEEN to tell the master the last heartbeast it
|
||||
had seen. The master normally than unicasts the missing packets
|
||||
to the slave. If the master doesn't have the old packet any more
|
||||
(i.e. it's outside the transmission window) then the master
|
||||
unicasts C_KILL to the slave asking it to die. (The slave should
|
||||
then restart, and catchup on state via the normal process).
|
||||
|
||||
If a slave goes for more than a few seconds without
|
||||
hearing from the master, it sends out a preemptive C_LASTSEEN.
|
||||
If the master still exists, this forces to the master to unicast
|
||||
the missed heartbeats. This is a work around for a temporary
|
||||
multicast problem. (i.e. if an IGMP probe is missed, the slave
|
||||
will temporarily stop seeing the multicast heartbeats. This
|
||||
work around prevents the slave from becoming master with
|
||||
horrible consequences).
|
||||
|
||||
Ping'ing:
|
||||
All slaves send out a 'ping' once per second as a
|
||||
multicast packet. This 'ping' contains the slave's ip address,
|
||||
and most importantly, the number of seconds from epoch
|
||||
that the slave started up. (I.e. the value of time(2) at
|
||||
that the process started). (This is the 'basetime').
|
||||
Obviously, this is never zero.
|
||||
|
||||
There is a special case. The master can send a single
|
||||
ping on shutdown to indicate that it is dead and that an
|
||||
immediate election should be held. This special ping is
|
||||
send from the master with a 'basetime' of zero.
|
||||
|
||||
Elections:
|
||||
|
||||
All machines start up as slaves.
|
||||
|
||||
Each slave listens for a heartbeat from the master.
|
||||
If a slave fails to hear a heartbeat for N seconds then it
|
||||
checks to see if it should become master.
|
||||
|
||||
A slave will become master if:
|
||||
* It hasn't heard from a master for N seconds.
|
||||
* It is the oldest of all it's peers (the other slaves).
|
||||
* In the event of a tie, the machine with the
|
||||
lowest IP address will win.
|
||||
|
||||
A 'peer' is any other slave machine that's send out a
|
||||
ping in the last N seconds. (i.e. we must have seen
|
||||
a recent ping from that slave for it to be considered).
|
||||
|
||||
The upshot of this is that no special communication
|
||||
takes place when a slave becomes a master.
|
||||
|
||||
On initial cluster startup, the process would be (for example)
|
||||
|
||||
* 3 machines startup simultaneously, all as slaves.
|
||||
* each machine sends out a multicast 'ping' every second.
|
||||
* 15 seconds later, the machine with the lowest IP
|
||||
address becomes master, and starts sending
|
||||
out heartbeats.
|
||||
* The remaining two machine hear the heartbeat and
|
||||
set that machine as their master.
|
||||
|
||||
Becoming master:
|
||||
|
||||
When a slave become master, the only structure maintained up
|
||||
to date are the tunnel and session structures. This means
|
||||
the master will rebuild a number of mappings.
|
||||
|
||||
#0. All the session and table structures are marked as
|
||||
defined. (Even if we weren't fully up to date, it's
|
||||
too late now).
|
||||
|
||||
#1. All the token bucket filters are re-build from scratch
|
||||
with the associated session to tbf pointers being re-built.
|
||||
|
||||
TODO: These changed tbf pointers aren't flooded to the slave right away!
|
||||
Throttled session could take a couple of minutes to start working again
|
||||
on master failover!
|
||||
|
||||
#2. The ipcache to session hash is rebuilt. (This isn't
|
||||
strictly needed, but it's a safety measure).
|
||||
|
||||
#3. The mapping from the ippool into the session table
|
||||
(and vice versa) is re-built.
|
||||
|
||||
|
||||
Becoming slave:
|
||||
|
||||
At startup the entire session and table structures are
|
||||
marked undefined.
|
||||
|
||||
As it seens updates from the master, the updated structures
|
||||
are marked as defined.
|
||||
|
||||
When there are no undefined tunnel or session structures, the
|
||||
slave marks itself as 'up-to-date' and starts advertising routes
|
||||
(if BGP is enabled).
|
||||
|
||||
STONITH:
|
||||
|
||||
Currently, there is very minimal protection from split brain.
|
||||
In particular, there is no real STONITH protocol to stop two masters
|
||||
appearing in the event of a network problem.
|
||||
|
||||
|
||||
|
||||
TODO:
|
||||
Should slaves that have undefined sessions, and receive
|
||||
a packet from a non-existant session then forward it to the master??
|
||||
In normal practice, a slave with undefined session shouldn't be
|
||||
handling packets, but ...
|
||||
|
||||
There is far too much walking of large arrays (in the master
|
||||
specifically). Although this is mitigated somewhat by the
|
||||
cluster_high_{sess,tun}, this benefit is lost as that value gets
|
||||
closer to MAX{SESSION,TUNNEL}. There are two issues here:
|
||||
|
||||
* The tunnel, radius and tbf arrays should probably use a
|
||||
mechanism like sessions, where grabbing a new one is a
|
||||
single lookup rather than a walk.
|
||||
|
||||
* A list structure (simillarly rooted at [0].interesting) is
|
||||
required to avoid having to walk tables periodically. As a
|
||||
back-stop the code in the master which *does* walk the
|
||||
arrays can mark any entry it processes as "interesting" to
|
||||
ensure it gets looked at even if a bug causes it to be
|
||||
otherwiase overlooked.
|
||||
|
||||
Support for more than 64k sessions per cluster. There is
|
||||
currently a 64k session limit because each session gets an id that global
|
||||
over the cluster (as opposed to local to the tunnel). Obviously, the tunnel
|
||||
id needs to be used in conjunction with the session id to index into
|
||||
the session table. But how?
|
||||
|
||||
I think the best way is to use something like page tables.
|
||||
for a given <tid,sid>, the appropriate session index is
|
||||
session[ tunnel[tid].page[sid>>10] + (sid & 1023) ]
|
||||
Where tunnel[].page[] is a 64 element array. As a tunnel
|
||||
fills up it's page block, it allocated a new 1024 session block
|
||||
from the session table and fills in the appropriate .page[]
|
||||
entry.
|
||||
|
||||
This should be a reasonable compromise between wasting memory
|
||||
(average 500 sessions per tunnel wasted) and speed. (Still a direct
|
||||
index without searching, but extra lookups required). Obviously
|
||||
the <6,10> split on the sid can be moved around to tune the size
|
||||
of the page table v the session table block size.
|
||||
|
||||
This unfortunately means that the tunnel structure HAS to
|
||||
be filled on the slave before any of the sessions on it can be used.
|
||||
|
||||
133
Makefile
Normal file
133
Makefile
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
DESTDIR =
|
||||
bindir = /usr/sbin
|
||||
etcdir = /etc/l2tpns
|
||||
libdir = /usr/lib/l2tpns
|
||||
man5dir = /usr/share/man/man5
|
||||
man8dir = /usr/share/man/man8
|
||||
statedir = /var/lib/l2tpns
|
||||
|
||||
DEFINES =
|
||||
DEFINES += -DLIBDIR='"$(libdir)"'
|
||||
DEFINES += -DETCDIR='"$(etcdir)"'
|
||||
|
||||
OPTIM =
|
||||
OPTIM += -g
|
||||
OPTIM += -O3
|
||||
|
||||
CC = gcc
|
||||
LD = gcc
|
||||
INCLUDES = -I.
|
||||
CPPFLAGS = $(INCLUDES) $(DEFINES)
|
||||
CFLAGS = -Wall -Wformat-security -Wno-format-zero-length $(OPTIM)
|
||||
LDFLAGS =
|
||||
LDLIBS =
|
||||
INSTALL = install -c -D -o root -g root
|
||||
|
||||
l2tpns.LIBS = -lm -lcli -ldl
|
||||
|
||||
OBJS = arp.o cli.o cluster.o constants.o control.o icmp.o l2tpns.o \
|
||||
ll.o md5.o ppp.o radius.o tbf.o util.o
|
||||
|
||||
PROGRAMS = l2tpns nsctl
|
||||
PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \
|
||||
setrxspeed.so snoopctl.so stripdomain.so throttlectl.so
|
||||
|
||||
DEFINES += -DSTATISTICS
|
||||
DEFINES += -DSTAT_CALLS
|
||||
DEFINES += -DRINGBUFFER
|
||||
|
||||
ifneq (2.4, $(shell uname -r | perl -pe 's/^(\d+\.\d+).*/$$1/'))
|
||||
DEFINES += -DHAVE_EPOLL
|
||||
endif
|
||||
|
||||
DEFINES += -DBGP
|
||||
OBJS += bgp.o
|
||||
|
||||
all: programs plugins
|
||||
programs: $(PROGRAMS)
|
||||
plugins: $(PLUGINS)
|
||||
|
||||
clean:
|
||||
rm -f *.o test/*.o $(PROGRAMS) $(PLUGINS) Makefile.tmp Makefile.bak
|
||||
|
||||
depend:
|
||||
(sed -n 'p; /^## Dependencies: (autogenerated) ##/q' Makefile && \
|
||||
gcc -MM $(CPPFLAGS) $(OBJS:.o=.c) && \
|
||||
gcc -MM $(CPPFLAGS) $(PLUGINS:.so=.c) | sed 's/\.o/.so/') >Makefile.tmp
|
||||
mv Makefile Makefile.bak
|
||||
mv Makefile.tmp Makefile
|
||||
|
||||
l2tpns: $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $($@.LIBS)
|
||||
|
||||
nsctl: nsctl.o control.o
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $($@.LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
%.so: %.c
|
||||
$(CC) -fPIC -shared $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
install: all
|
||||
$(INSTALL) -m 0755 l2tpns $(DESTDIR)$(bindir)/l2tpns
|
||||
$(INSTALL) -m 0755 nsctl $(DESTDIR)$(bindir)/nsctl
|
||||
|
||||
$(INSTALL) -m 0644 Docs/startup-config.5 $(DESTDIR)$(man5dir)/startup-config.5
|
||||
$(INSTALL) -m 0644 Docs/l2tpns.8 $(DESTDIR)$(man8dir)/l2tpns.8
|
||||
$(INSTALL) -m 0644 Docs/nsctl.8 $(DESTDIR)$(man8dir)/nsctl.8
|
||||
|
||||
gzip --best --force $(DESTDIR)$(man5dir)/*.5 $(DESTDIR)$(man8dir)/*.8
|
||||
|
||||
@for config in startup-config users ip_pool; \
|
||||
do \
|
||||
suffix=; \
|
||||
mode=0600; [ $$config = ip_pool ] && mode=0644; \
|
||||
if [ -f $(DESTDIR)$(etcdir)/$$config ]; \
|
||||
then \
|
||||
cmp -s etc/$$config.default $(DESTDIR)$(etcdir)/$$config && continue; \
|
||||
suffix=.default; \
|
||||
fi; \
|
||||
echo $(INSTALL) -m $$mode etc/$$config.default $(DESTDIR)$(etcdir)/$$config$$suffix; \
|
||||
$(INSTALL) -m $$mode etc/$$config.default $(DESTDIR)$(etcdir)/$$config$$suffix; \
|
||||
done
|
||||
|
||||
@for plugin in $(PLUGINS); \
|
||||
do \
|
||||
echo $(INSTALL) -m 0755 $$plugin $(DESTDIR)$(libdir)/$$plugin; \
|
||||
$(INSTALL) -m 0755 $$plugin $(DESTDIR)$(libdir)/$$plugin; \
|
||||
done
|
||||
|
||||
@if [ -z $(DESTDIR) ] && [ ! -e /dev/net/tun ]; \
|
||||
then \
|
||||
mkdir /dev/net; \
|
||||
echo mknod /dev/net/tun c 10 200; \
|
||||
mknod /dev/net/tun c 10 200; \
|
||||
fi
|
||||
|
||||
.PHONY: all clean depend install
|
||||
|
||||
## Dependencies: (autogenerated) ##
|
||||
arp.o: arp.c l2tpns.h
|
||||
cli.o: cli.c l2tpns.h constants.h util.h cluster.h tbf.h ll.h bgp.h
|
||||
cluster.o: cluster.c l2tpns.h cluster.h util.h tbf.h bgp.h
|
||||
constants.o: constants.c constants.h
|
||||
control.o: control.c l2tpns.h control.h
|
||||
icmp.o: icmp.c l2tpns.h
|
||||
l2tpns.o: l2tpns.c md5.h l2tpns.h cluster.h plugin.h ll.h constants.h \
|
||||
control.h util.h tbf.h bgp.h
|
||||
ll.o: ll.c ll.h
|
||||
md5.o: md5.c md5.h
|
||||
ppp.o: ppp.c l2tpns.h constants.h plugin.h util.h tbf.h cluster.h
|
||||
radius.o: radius.c md5.h constants.h l2tpns.h plugin.h util.h cluster.h
|
||||
tbf.o: tbf.c l2tpns.h util.h tbf.h
|
||||
util.o: util.c l2tpns.h bgp.h
|
||||
bgp.o: bgp.c l2tpns.h bgp.h util.h
|
||||
autosnoop.so: autosnoop.c l2tpns.h plugin.h
|
||||
autothrottle.so: autothrottle.c l2tpns.h plugin.h
|
||||
garden.so: garden.c l2tpns.h plugin.h control.h
|
||||
sessionctl.so: sessionctl.c l2tpns.h plugin.h control.h
|
||||
setrxspeed.so: setrxspeed.c l2tpns.h plugin.h
|
||||
snoopctl.so: snoopctl.c l2tpns.h plugin.h control.h
|
||||
stripdomain.so: stripdomain.c l2tpns.h plugin.h
|
||||
throttlectl.so: throttlectl.c l2tpns.h plugin.h control.h
|
||||
29
THANKS
Normal file
29
THANKS
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
A list of people who have contributed to the development of L2TPNS
|
||||
by reporting problems, suggesting various improvements or submitting
|
||||
actual code follows.
|
||||
|
||||
Adrian Kennard <adrian.kennard@aaisp.net.uk>
|
||||
David Parrish <dparrish@gmail.com>
|
||||
Michael O'Reilly <michael@optus.net>
|
||||
Brendan O'Dea <bod@optus.net>
|
||||
Bradley Baetz <bradley.baetz@optus.net>
|
||||
Iain Wade <iwade@optus.net>
|
||||
Yuri <yuri@actcom.net.il>
|
||||
Juergen Kammer <j.kammer@eurodata.de>
|
||||
Simon Talbot <simont@nse.co.uk>
|
||||
Jonathan McDowell <noodles@earth.li>
|
||||
Bjørn Augestad <augestad@users.sourceforge.net>
|
||||
Roberto Chostakovis <rchostakovis@users.sourceforge.net>
|
||||
Jordan Hrycaj <jordan@mjh.teddy-net.com>
|
||||
Vladislav Bjelic <vladislav@gmail.com>
|
||||
Alex Kiernan <alex.kiernan@gmail.com>
|
||||
Dominique Rousseau <d.rousseau@nnx.com>
|
||||
Tim Devries <tdevries@northrock.bm>
|
||||
Slobodan Tomic <stomic@loznica.com>
|
||||
Michael Chapman <mike.chapman@optus.net>
|
||||
Charlie Brady <charlieb@e-smith.com>
|
||||
Jon Morby <jon@fido.net>
|
||||
Paul Martin <pm@zetnet.net>
|
||||
Jonathan Yarden <jyarden@bluegrass.net>
|
||||
Patrick Cole <z@amused.net>
|
||||
Rhys Kidd <rhys.kidd@staff.westnet.com.au>
|
||||
64
arp.c
Normal file
64
arp.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// L2TPNS: arp
|
||||
|
||||
char const *cvs_id_arp = "$Id: arp.c,v 1.7 2005/07/31 10:04:09 bodea Exp $";
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <linux/if_packet.h>
|
||||
|
||||
#include "l2tpns.h"
|
||||
|
||||
/* Most of this code is based on keepalived:vrrp_arp.c */
|
||||
|
||||
struct arp_buf {
|
||||
struct ether_header eth;
|
||||
struct arphdr arp;
|
||||
|
||||
/* Data bit - variably sized, so not present in |struct arphdr| */
|
||||
unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address */
|
||||
in_addr_t ar_sip; /* Sender IP address. */
|
||||
unsigned char ar_tha[ETH_ALEN]; /* Target hardware address */
|
||||
in_addr_t ar_tip; /* Target ip */
|
||||
} __attribute__((packed));
|
||||
|
||||
void sendarp(int ifr_idx, const unsigned char* mac, in_addr_t ip)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_ll sll;
|
||||
struct arp_buf buf;
|
||||
|
||||
CSTAT(sendarp);
|
||||
STAT(arp_sent);
|
||||
|
||||
/* Ethernet */
|
||||
memset(buf.eth.ether_dhost, 0xFF, ETH_ALEN);
|
||||
memcpy(buf.eth.ether_shost, mac, ETH_ALEN);
|
||||
buf.eth.ether_type = htons(ETHERTYPE_ARP);
|
||||
|
||||
/* ARP */
|
||||
buf.arp.ar_hrd = htons(ARPHRD_ETHER);
|
||||
buf.arp.ar_pro = htons(ETHERTYPE_IP);
|
||||
buf.arp.ar_hln = ETH_ALEN;
|
||||
buf.arp.ar_pln = 4; //IPPROTO_ADDR_LEN;
|
||||
buf.arp.ar_op = htons(ARPOP_REQUEST);
|
||||
|
||||
/* Data */
|
||||
memcpy(buf.ar_sha, mac, ETH_ALEN);
|
||||
memcpy(&buf.ar_sip, &ip, sizeof(ip));
|
||||
memcpy(buf.ar_tha, mac, ETH_ALEN);
|
||||
memcpy(&buf.ar_tip, &ip, sizeof(ip));
|
||||
|
||||
/* Now actually send the thing */
|
||||
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP));
|
||||
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
memcpy(sll.sll_addr, mac, sizeof(sll.sll_addr) - 1);
|
||||
sll.sll_halen = ETH_ALEN;
|
||||
sll.sll_ifindex = ifr_idx;
|
||||
|
||||
sendto(fd, &buf, sizeof(buf), 0, (struct sockaddr*)&sll, sizeof(sll));
|
||||
close(fd);
|
||||
}
|
||||
75
autosnoop.c
Normal file
75
autosnoop.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/* set up intercept based on RADIUS reply */
|
||||
|
||||
char const *cvs_id = "$Id: autosnoop.c,v 1.12 2005/10/11 09:04:53 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
int plugin_radius_response(struct param_radius_response *data)
|
||||
{
|
||||
if (!strcmp(data->key, "intercept"))
|
||||
{
|
||||
char *p;
|
||||
data->s->snoop_ip = 0;
|
||||
data->s->snoop_port = 0;
|
||||
if ((p = strchr(data->value, ':')))
|
||||
{
|
||||
*p++ = 0;
|
||||
if (*data->value)
|
||||
data->s->snoop_ip = inet_addr(data->value);
|
||||
|
||||
if (data->s->snoop_ip == INADDR_NONE)
|
||||
data->s->snoop_ip = 0;
|
||||
|
||||
if (*p)
|
||||
data->s->snoop_port = atoi(p);
|
||||
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Intercepting user to %s:%d\n",
|
||||
f->fmtaddr(data->s->snoop_ip, 0), data->s->snoop_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Not Intercepting user (reply string should"
|
||||
" be intercept=ip:port)\n");
|
||||
}
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_radius_reset(struct param_radius_reset *data)
|
||||
{
|
||||
data->s->snoop_ip = 0;
|
||||
data->s->snoop_port = 0;
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_radius_account(struct param_radius_account *data)
|
||||
{
|
||||
if (data->s->snoop_ip && data->s->snoop_port)
|
||||
{
|
||||
uint8_t *p = *data->packet;
|
||||
|
||||
*p = 26; // vendor-specific
|
||||
*(uint32_t *) (p + 2) = htonl(9); // Cisco
|
||||
p[6] = 1; // Cisco-AVPair
|
||||
p[7] = 2 + sprintf((char *) p + 8, "intercept=%s:%d",
|
||||
f->fmtaddr(data->s->snoop_ip, 0), data->s->snoop_port);
|
||||
|
||||
p[1] = p[7] + 6;
|
||||
*data->packet += p[1];
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
158
autothrottle.c
Normal file
158
autothrottle.c
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/* set up throttling based on RADIUS reply */
|
||||
|
||||
/*
|
||||
* lcp:interface-config#1=service-policy input N
|
||||
* lcp:interface-config#2=service-policy output N
|
||||
*
|
||||
* throttle=N
|
||||
* throttle=yes (use throttle_rate from config)
|
||||
* throttle=no
|
||||
*/
|
||||
|
||||
char const *cvs_id = "$Id: autothrottle.c,v 1.16 2005/10/11 09:04:53 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
#define THROTTLE_KEY "lcp:interface-config"
|
||||
|
||||
int plugin_radius_response(struct param_radius_response *data)
|
||||
{
|
||||
if (!strncmp(data->key, THROTTLE_KEY, sizeof(THROTTLE_KEY) - 1))
|
||||
{
|
||||
char *sp = strchr(data->value, ' ');
|
||||
char type;
|
||||
int rate;
|
||||
|
||||
if (!sp || sp - data->value < 4 ||
|
||||
strncmp("service-policy", data->value, sp - data->value))
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
while (*sp == ' ') sp++;
|
||||
data->value = sp;
|
||||
|
||||
if (!(sp = strchr(data->value, ' ')) ||
|
||||
(strncmp("input", data->value, sp - data->value) &&
|
||||
strncmp("output", data->value, sp - data->value)))
|
||||
{
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Not throttling user (invalid type %.*s)\n",
|
||||
sp - data->value, data->value);
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
type = *data->value;
|
||||
|
||||
while (*sp == ' ') sp++;
|
||||
data->value = sp;
|
||||
|
||||
if ((rate = strtol(data->value, &sp, 10)) < 0 || *sp)
|
||||
{
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Not throttling user (invalid rate %s)\n",
|
||||
data->value);
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
if (type == 'i')
|
||||
{
|
||||
data->s->throttle_in = rate;
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Throttling user input to %dkb/s\n", rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->s->throttle_out = rate;
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Throttling user output to %dkb/s\n", rate);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(data->key, "throttle"))
|
||||
{
|
||||
char *e;
|
||||
int rate;
|
||||
|
||||
if ((rate = strtol(data->value, &e, 10)) < 0 || *e)
|
||||
{
|
||||
rate = -1;
|
||||
if (!strcmp(data->value, "yes"))
|
||||
{
|
||||
unsigned long *ts = f->getconfig("throttle_speed", UNSIGNED_LONG);
|
||||
if (ts)
|
||||
rate = *ts;
|
||||
}
|
||||
else if (!strcmp(data->value, "no"))
|
||||
rate = 0;
|
||||
}
|
||||
|
||||
if (rate < 0)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
if (rate)
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Throttling user to %dkb/s\n", rate);
|
||||
else
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
" Not throttling user\n");
|
||||
|
||||
data->s->throttle_in = data->s->throttle_out = rate;
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_radius_reset(struct param_radius_reset *data)
|
||||
{
|
||||
f->throttle(f->get_id_by_session(data->s), 0, 0);
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_radius_account(struct param_radius_account *data)
|
||||
{
|
||||
if (data->s->throttle_in || data->s->throttle_out)
|
||||
{
|
||||
uint8_t *p = *data->packet;
|
||||
int i = 1;
|
||||
|
||||
if (data->s->throttle_in)
|
||||
{
|
||||
*p = 26; // vendor-specific
|
||||
*(uint32_t *) (p + 2) = htonl(9); // Cisco
|
||||
p[6] = 1; // Cisco-AVPair
|
||||
p[7] = 2 + sprintf((char *) p + 8,
|
||||
"lcp:interface-config#%d=service-policy input %d", i++,
|
||||
data->s->throttle_in);
|
||||
|
||||
p[1] = p[7] + 6;
|
||||
p += p[1];
|
||||
}
|
||||
|
||||
if (data->s->throttle_out)
|
||||
{
|
||||
*p = 26; // vendor-specific
|
||||
*(uint32_t *) (p + 2) = htonl(9); // Cisco
|
||||
p[6] = 1; // Cisco-AVPair
|
||||
p[7] = 2 + sprintf((char *) p + 8,
|
||||
"lcp:interface-config#%d=service-policy output %d", i++,
|
||||
data->s->throttle_out);
|
||||
|
||||
p[1] = p[7] + 6;
|
||||
p += p[1];
|
||||
}
|
||||
|
||||
*data->packet = p;
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
205
bgp.h
Normal file
205
bgp.h
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/* BGPv4 (RFC1771) */
|
||||
/* $Id: bgp.h,v 1.5 2005/06/04 15:42:35 bodea Exp $ */
|
||||
|
||||
#ifndef __BGP_H__
|
||||
#define __BGP_H__
|
||||
|
||||
#define BGP_MAX_PACKET_SIZE 4096
|
||||
#define BGP_HOLD_TIME 180 /* seconds before peer times us out */
|
||||
#define BGP_KEEPALIVE_TIME 60 /* seconds between messages */
|
||||
#define BGP_STATE_TIME 60 /* state transition timeout in seconds */
|
||||
#define BGP_MAX_RETRY 42 /* maximum number of times to retry */
|
||||
#define BGP_RETRY_BACKOFF 60 /* number of seconds between retries,
|
||||
cumulative */
|
||||
|
||||
#define BGP_METRIC 1 /* multi_exit_disc */
|
||||
#define BGP_LOCAL_PREF 100 /* local preference value */
|
||||
|
||||
struct bgp_header {
|
||||
char marker[16];
|
||||
uint16_t len;
|
||||
uint8_t type;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* bgp_header.type */
|
||||
#define BGP_MSG_OPEN 1
|
||||
#define BGP_MSG_UPDATE 2
|
||||
#define BGP_MSG_NOTIFICATION 3
|
||||
#define BGP_MSG_KEEPALIVE 4
|
||||
|
||||
struct bgp_packet {
|
||||
struct bgp_header header;
|
||||
char data[BGP_MAX_PACKET_SIZE - sizeof(struct bgp_header)]; /* variable */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bgp_data_open {
|
||||
uint8_t version;
|
||||
#define BGP_VERSION 4
|
||||
uint16_t as;
|
||||
uint16_t hold_time;
|
||||
uint32_t identifier;
|
||||
uint8_t opt_len;
|
||||
#define BGP_DATA_OPEN_SIZE 10 /* size of struct excluding opt_params */
|
||||
char opt_params[sizeof(((struct bgp_packet *)0)->data) - BGP_DATA_OPEN_SIZE]; /* variable */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bgp_ip_prefix {
|
||||
uint8_t len;
|
||||
uint32_t prefix; /* variable */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define BGP_IP_PREFIX_SIZE(p) (1 + ((p).len / 8) + ((p).len % 8 != 0))
|
||||
|
||||
struct bgp_path_attr {
|
||||
uint8_t flags;
|
||||
uint8_t code;
|
||||
union {
|
||||
struct {
|
||||
uint8_t len;
|
||||
char value[29]; /* semi-random size, adequate for l2tpns */
|
||||
} __attribute__ ((packed)) s; /* short */
|
||||
struct {
|
||||
uint16_t len;
|
||||
char value[28];
|
||||
} __attribute__ ((packed)) e; /* extended */
|
||||
} data; /* variable */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* bgp_path_attr.flags (bitfields) */
|
||||
#define BGP_PATH_ATTR_FLAG_OPTIONAL (1 << 7)
|
||||
#define BGP_PATH_ATTR_FLAG_TRANS (1 << 6)
|
||||
#define BGP_PATH_ATTR_FLAG_PARTIAL (1 << 5)
|
||||
#define BGP_PATH_ATTR_FLAG_EXTLEN (1 << 4)
|
||||
|
||||
/* bgp_path_attr.code, ...value */
|
||||
#define BGP_PATH_ATTR_CODE_ORIGIN 1 /* well-known, mandatory */
|
||||
# define BGP_PATH_ATTR_CODE_ORIGIN_IGP 0
|
||||
# define BGP_PATH_ATTR_CODE_ORIGIN_EGP 1
|
||||
# define BGP_PATH_ATTR_CODE_ORIGIN_INCOMPLETE 2
|
||||
#define BGP_PATH_ATTR_CODE_AS_PATH 2 /* well-known, mandatory */
|
||||
# define BGP_PATH_ATTR_CODE_AS_PATH_AS_SET 1
|
||||
# define BGP_PATH_ATTR_CODE_AS_PATH_AS_SEQUENCE 2
|
||||
#define BGP_PATH_ATTR_CODE_NEXT_HOP 3 /* well-known, mandatory */
|
||||
#define BGP_PATH_ATTR_CODE_MULTI_EXIT_DISC 4 /* optional, non-transitive */
|
||||
#define BGP_PATH_ATTR_CODE_LOCAL_PREF 5 /* well-known, discretionary */
|
||||
#define BGP_PATH_ATTR_CODE_ATOMIC_AGGREGATE 6 /* well-known, discretionary */
|
||||
#define BGP_PATH_ATTR_CODE_AGGREGATOR 7 /* optional, transitive */
|
||||
#define BGP_PATH_ATTR_CODE_COMMUNITIES 8 /* optional, transitive (RFC1997) */
|
||||
|
||||
#define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \
|
||||
? ((p).data.e.len + 1) : (p).data.s.len) + 3)
|
||||
|
||||
/* well known COMMUNITIES */
|
||||
#define BGP_COMMUNITY_NO_EXPORT 0xffffff01 /* don't advertise outside confederation */
|
||||
#define BGP_COMMUNITY_NO_ADVERTISE 0xffffff02 /* don't advertise to any peer */
|
||||
#define BGP_COMMUNITY_NO_EXPORT_SUBCONFED 0xffffff03 /* don't advertise to any other AS */
|
||||
|
||||
struct bgp_data_notification {
|
||||
uint8_t error_code;
|
||||
uint8_t error_subcode;
|
||||
char data[sizeof(((struct bgp_packet *)0)->data) - 2]; /* variable */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* bgp_data_notification.error_code, .error_subcode */
|
||||
#define BGP_ERR_HEADER 1
|
||||
# define BGP_ERR_HDR_NOT_SYNC 1
|
||||
# define BGP_ERR_HDR_BAD_LEN 2
|
||||
# define BGP_ERR_HDR_BAD_TYPE 3
|
||||
#define BGP_ERR_OPEN 2
|
||||
# define BGP_ERR_OPN_VERSION 1
|
||||
# define BGP_ERR_OPN_BAD_AS 2
|
||||
# define BGP_ERR_OPN_BAD_IDENT 3
|
||||
# define BGP_ERR_OPN_UNSUP_PARAM 4
|
||||
# define BGP_ERR_OPN_AUTH_FAILURE 5
|
||||
# define BGP_ERR_OPN_HOLD_TIME 6
|
||||
#define BGP_ERR_UPDATE 3
|
||||
# define BGP_ERR_UPD_BAD_ATTR_LIST 1
|
||||
# define BGP_ERR_UPD_UNKN_WK_ATTR 2
|
||||
# define BGP_ERR_UPD_MISS_WK_ATTR 3
|
||||
# define BGP_ERR_UPD_BAD_ATTR_FLAG 4
|
||||
# define BGP_ERR_UPD_BAD_ATTR_LEN 5
|
||||
# define BGP_ERR_UPD_BAD_ORIGIN 6
|
||||
# define BGP_ERR_UPD_ROUTING_LOOP 7
|
||||
# define BGP_ERR_UPD_BAD_NEXT_HOP 8
|
||||
# define BGP_ERR_UPD_BAD_OPT_ATTR 9
|
||||
# define BGP_ERR_UPD_BAD_NETWORK 10
|
||||
# define BGP_ERR_UPD_BAD_AS_PATH 11
|
||||
#define BGP_ERR_HOLD_TIMER_EXP 4
|
||||
#define BGP_ERR_FSM 5
|
||||
#define BGP_ERR_CEASE 6
|
||||
|
||||
enum bgp_state {
|
||||
Disabled, /* initial, or failed */
|
||||
Idle, /* trying to connect */
|
||||
Connect, /* connect issued */
|
||||
Active, /* connected, waiting to send OPEN */
|
||||
OpenSent, /* OPEN sent, waiting for peer OPEN */
|
||||
OpenConfirm, /* KEEPALIVE sent, waiting for peer KEEPALIVE */
|
||||
Established, /* established */
|
||||
};
|
||||
|
||||
struct bgp_route_list {
|
||||
struct bgp_ip_prefix dest;
|
||||
struct bgp_route_list *next;
|
||||
};
|
||||
|
||||
struct bgp_buf {
|
||||
struct bgp_packet packet; /* BGP packet */
|
||||
size_t done; /* bytes sent/recvd */
|
||||
};
|
||||
|
||||
/* state */
|
||||
struct bgp_peer {
|
||||
char name[32]; /* peer name */
|
||||
in_addr_t addr; /* peer address */
|
||||
int as; /* AS number */
|
||||
int sock;
|
||||
enum bgp_state state; /* FSM state */
|
||||
enum bgp_state next_state; /* next state after outbuf cleared */
|
||||
time_t state_time; /* time of last state change */
|
||||
time_t keepalive_time; /* time to send next keepalive */
|
||||
time_t retry_time; /* time for connection retry */
|
||||
int retry_count; /* connection retry count */
|
||||
int init_keepalive; /* initial keepalive time */
|
||||
int init_hold; /* initial hold time */
|
||||
int keepalive; /* negotiated keepalive time */
|
||||
int hold; /* negotiated hold time */
|
||||
time_t expire_time; /* time next peer packet expected */
|
||||
int routing; /* propagate routes */
|
||||
int update_routes; /* UPDATE required */
|
||||
struct bgp_route_list *routes; /* routes known by this peer */
|
||||
struct bgp_buf *outbuf; /* pending output */
|
||||
struct bgp_buf *inbuf; /* pending input */
|
||||
int cli_flag; /* updates requested from CLI */
|
||||
char *path_attrs; /* path attrs to send in UPDATE message */
|
||||
int path_attr_len; /* length of path attrs */
|
||||
uint32_t events; /* events to poll */
|
||||
struct event_data edata; /* poll data */
|
||||
};
|
||||
|
||||
/* bgp_peer.cli_flag */
|
||||
#define BGP_CLI_SUSPEND 1
|
||||
#define BGP_CLI_ENABLE 2
|
||||
#define BGP_CLI_RESTART 3
|
||||
|
||||
extern struct bgp_peer *bgp_peers;
|
||||
extern int bgp_configured;
|
||||
|
||||
/* actions */
|
||||
int bgp_setup(int as);
|
||||
int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive,
|
||||
int hold, int enable);
|
||||
|
||||
void bgp_stop(struct bgp_peer *peer);
|
||||
void bgp_halt(struct bgp_peer *peer);
|
||||
int bgp_restart(struct bgp_peer *peer);
|
||||
int bgp_add_route(in_addr_t ip, in_addr_t mask);
|
||||
int bgp_del_route(in_addr_t ip, in_addr_t mask);
|
||||
void bgp_enable_routing(int enable);
|
||||
int bgp_set_poll(void);
|
||||
int bgp_process(uint32_t events[]);
|
||||
char const *bgp_state_str(enum bgp_state state);
|
||||
|
||||
extern char const *cvs_id_bgp;
|
||||
|
||||
#endif /* __BGP_H__ */
|
||||
89
cluster.h
Normal file
89
cluster.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// L2TPNS Clustering Stuff
|
||||
// $Id: cluster.h,v 1.14 2005/07/31 10:04:10 bodea Exp $
|
||||
|
||||
#ifndef __CLUSTER_H__
|
||||
#define __CLUSTER_H__
|
||||
|
||||
|
||||
#define C_HEARTBEAT 1
|
||||
#define C_ACK 2
|
||||
#define C_PING 3
|
||||
#define C_TUNNEL 4 // Tunnel structure.
|
||||
#define C_SESSION 5 // Session structure.
|
||||
#define C_GOODBYE 6
|
||||
#define C_LASTSEEN 7 // Tell master the last heartbeat that I handled.
|
||||
#define C_KILL 8 // Tell a slave to die.
|
||||
#define C_FORWARD 9 // Forwarded packet..
|
||||
#define C_BYTES 10 // Update byte counters.
|
||||
#define C_THROTTLE 11 // A packet for the master to throttle. (The TBF tells direction).
|
||||
#define C_CSESSION 12 // Compressed session structure.
|
||||
#define C_CTUNNEL 13 // Compressed tunnel structure.
|
||||
#define C_GARDEN 14 // Gardened packet
|
||||
#define C_MASTER 15 // Tell a slave the address of the master.
|
||||
#define C_FORWARD_DAE 16 // A DAE packet for the master to handle
|
||||
|
||||
#define HB_VERSION 5 // Protocol version number..
|
||||
#define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
|
||||
#define HB_HISTORY_SIZE 64 // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ)
|
||||
|
||||
#define PING_INTERVAL 5 // 0.5 seconds. Needs to be short to keep session tables fresh.
|
||||
#define HB_TIMEOUT (15*2*PING_INTERVAL) // 15 seconds without heartbeat triggers an election..
|
||||
|
||||
#define CLUSTERPORT 32792
|
||||
#define CLUSTER_MAX_SIZE 32 // No more than 32 machines in a cluster!
|
||||
|
||||
#define DEFAULT_MCAST_ADDR "239.192.13.13" // Need an assigned number!
|
||||
#define DEFAULT_MCAST_INTERFACE "eth0"
|
||||
|
||||
typedef struct {
|
||||
uint32_t version; // protocol version.
|
||||
uint32_t seq; // Sequence number for this heatbeat.
|
||||
uint32_t basetime; // What time I started
|
||||
uint32_t clusterid; // Id for this cluster?
|
||||
|
||||
uint32_t highsession; // Id of the highest in-use session.
|
||||
uint32_t freesession; // Id of the first free session.
|
||||
uint32_t hightunnel; // Id of the highest used tunnel.
|
||||
uint32_t size_sess; // Size of the session structure.
|
||||
|
||||
uint32_t size_tunn; // size of the tunnel structure.
|
||||
uint32_t interval; // ping/heartbeat interval
|
||||
uint32_t timeout; // heartbeat timeout
|
||||
|
||||
uint64_t table_version; // # state changes processed by cluster
|
||||
|
||||
char reserved[128 - 13*sizeof(uint32_t)]; // Pad out to 128 bytes.
|
||||
} heartt;
|
||||
|
||||
typedef struct { /* Used to update byte counters on the */
|
||||
/* master. */
|
||||
uint32_t sid;
|
||||
uint32_t pin;
|
||||
uint32_t pout;
|
||||
uint32_t cin;
|
||||
uint32_t cout;
|
||||
} bytest;
|
||||
|
||||
typedef struct {
|
||||
in_addr_t addr; // peer address
|
||||
uint32_t ver; // version of structure.
|
||||
uint32_t undef; // Number of undefined structures. 0 if up-to-date.
|
||||
uint32_t basetime; // start time of this peer.
|
||||
} pingt;
|
||||
|
||||
int cluster_init(void);
|
||||
int processcluster(uint8_t *buf, int size, in_addr_t addr);
|
||||
int cluster_send_session(int sid);
|
||||
int cluster_send_tunnel(int tid);
|
||||
int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port);
|
||||
int master_forward_dae_packet(uint8_t *data, int size, in_addr_t addr, int port);
|
||||
int master_throttle_packet(int tid, uint8_t *data, int size);
|
||||
int master_garden_packet(sessionidt s, uint8_t *data, int size);
|
||||
void master_update_counts(void);
|
||||
void cluster_send_ping(time_t basetime);
|
||||
void cluster_heartbeat(void);
|
||||
void cluster_check_master(void);
|
||||
void cluster_check_slaves(void);
|
||||
int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc);
|
||||
|
||||
#endif /* __CLUSTER_H__ */
|
||||
227
constants.c
Normal file
227
constants.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
// L2TPNS: constants
|
||||
|
||||
char const *cvs_id_constants = "$Id: constants.c,v 1.7 2005/07/31 10:04:10 bodea Exp $";
|
||||
|
||||
#include <stdio.h>
|
||||
#include "constants.h"
|
||||
|
||||
#define CONSTANT(table, ...) \
|
||||
static char const *table ## s[] = { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
char const *table(int index) \
|
||||
{ \
|
||||
static char n[16]; \
|
||||
if (index >= 0 && index < sizeof(table ## s) / sizeof(table ## s[0]) \
|
||||
&& table ## s[index]) \
|
||||
return table ## s[index]; \
|
||||
snprintf(n, sizeof(n), "%d", index); \
|
||||
return n; \
|
||||
}
|
||||
|
||||
CONSTANT(l2tp_code,
|
||||
0, // 0
|
||||
"SCCRQ", // 1
|
||||
"SCCRP", // 2
|
||||
"SCCCN", // 3
|
||||
"StopCCN", // 4
|
||||
0, // 5
|
||||
"HELLO", // 6
|
||||
"OCRQ", // 7
|
||||
"OCRP", // 8
|
||||
"OCCN", // 9
|
||||
"ICRQ", // 10
|
||||
"ICRP", // 11
|
||||
"ICCN", // 12
|
||||
0, // 13
|
||||
"CDN", // 14
|
||||
"WEN", // 15
|
||||
"SLI" // 16
|
||||
)
|
||||
|
||||
CONSTANT(l2tp_avp_name,
|
||||
"Message Type", // 0
|
||||
"Result Code", // 1
|
||||
"Protocol Version", // 2
|
||||
"Framing Capabilities", // 3
|
||||
"Bearer Capabilities", // 4
|
||||
"Tie Breaker", // 5
|
||||
"Firmware Revision", // 6
|
||||
"Host Name", // 7
|
||||
"Vendor Name", // 8
|
||||
"Assigned Tunnel ID", // 9
|
||||
"Receive Window Size", // 10
|
||||
"Challenge", // 11
|
||||
"Q.931 Cause Code", // 12
|
||||
"Challenge Response", // 13
|
||||
"Assigned Session ID", // 14
|
||||
"Call Serial Number", // 15
|
||||
"Minimum BPS", // 16
|
||||
"Maximum BPS", // 17
|
||||
"Bearer Type", // 18 (2 = Analog, 1 = Digital)
|
||||
"Framing Type", // 19 (2 = Async, 1 = Sync)
|
||||
0, // 20
|
||||
"Called Number", // 21
|
||||
"Calling Number", // 22
|
||||
"Sub Address", // 23
|
||||
"Tx Connect Speed", // 24
|
||||
"Physical Channel ID", // 25
|
||||
"Initial Received LCP CONFREQ", // 26
|
||||
"Last Sent LCP CONFREQ", // 27
|
||||
"Last Received LCP CONFREQ", // 28
|
||||
"Proxy Authen Type", // 29
|
||||
"Proxy Authen Name", // 30
|
||||
"Proxy Authen Challenge", // 31
|
||||
"Proxy Authen ID", // 32
|
||||
"Proxy Authen Response", // 33
|
||||
"Call Errors", // 34
|
||||
"ACCM", // 35
|
||||
"Random Vector", // 36
|
||||
"Private Group ID", // 37
|
||||
"Rx Connect Speed", // 38
|
||||
"Sequencing Required" // 39
|
||||
)
|
||||
|
||||
CONSTANT(l2tp_stopccn_result_code,
|
||||
0, // 0
|
||||
"General request to clear control connection", // 1
|
||||
"General error--Error Code indicates the problem", // 2
|
||||
"Control channel already exists", // 3
|
||||
"Requester is not authorized to establish a"
|
||||
" control channel", // 4
|
||||
"The protocol version of the requester is not"
|
||||
" supported", // 5
|
||||
"Requester is being shut down", // 6
|
||||
"Finite State Machine error" // 7
|
||||
)
|
||||
|
||||
CONSTANT(l2tp_cdn_result_code,
|
||||
0, // 0
|
||||
"Call disconnected due to loss of carrier", // 1
|
||||
"Call disconnected for the reason indicated in"
|
||||
" error code", // 2
|
||||
"Call disconnected for administrative reasons", // 3
|
||||
"Call failed due to lack of appropriate facilities"
|
||||
" being available (temporary condition)", // 4
|
||||
"Call failed due to lack of appropriate facilities"
|
||||
" being available (permanent condition)", // 5
|
||||
"Invalid destination", // 6
|
||||
"Call failed due to no carrier detected", // 7
|
||||
"Call failed due to detection of a busy signal", // 8
|
||||
"Call failed due to lack of a dial tone", // 9
|
||||
"Call was not established within time allotted by"
|
||||
" LAC", // 10
|
||||
"Call was connected but no appropriate framing was"
|
||||
" detected" // 11
|
||||
)
|
||||
|
||||
CONSTANT(l2tp_error_code,
|
||||
"No general error", // 0
|
||||
"No control connection exists yet for this LAC-LNS"
|
||||
" pair", // 1
|
||||
"Length is wrong", // 2
|
||||
"One of the field values was out of range or"
|
||||
" reserved field was non-zero", // 3
|
||||
"Insufficient resources to handle this operation"
|
||||
" now", // 4
|
||||
"The Session ID is invalid in this context", // 5
|
||||
"A generic vendor-specific error occurred in the"
|
||||
" LAC", // 6
|
||||
"Try another LNS", // 7
|
||||
"Session or tunnel was shutdown due to receipt of"
|
||||
" an unknown AVP with the M-bit set" // 8
|
||||
)
|
||||
|
||||
CONSTANT(ppp_phase,
|
||||
"Dead", // 0
|
||||
"Establish", // 1
|
||||
"Authenticate", // 2
|
||||
"Network", // 3
|
||||
"Terminate", // 4
|
||||
)
|
||||
|
||||
CONSTANT(ppp_state,
|
||||
"Initial", // 0
|
||||
"Starting", // 1
|
||||
"Closed", // 2
|
||||
"Stopped", // 3
|
||||
"Closing", // 4
|
||||
"Stopping", // 5
|
||||
"Request-Sent", // 6
|
||||
"Ack-Received", // 7
|
||||
"Ack-Sent", // 8
|
||||
"Opened" // 9
|
||||
)
|
||||
|
||||
CONSTANT(ppp_auth_type,
|
||||
0, // 0
|
||||
"Textual username/password exchange", // 1
|
||||
"PPP CHAP", // 2
|
||||
"PPP PAP", // 3
|
||||
"No Authentication", // 4
|
||||
"Microsoft CHAP Version 1 (MSCHAPv1)" // 5
|
||||
)
|
||||
|
||||
CONSTANT(ppp_code,
|
||||
0, // 0
|
||||
"ConfigReq", // 1
|
||||
"ConfigAck", // 2
|
||||
"ConfigNak", // 3
|
||||
"ConfigRej", // 4
|
||||
"TerminateReq", // 5
|
||||
"TerminateAck", // 6
|
||||
"CodeRej", // 7
|
||||
"ProtocolRej", // 8
|
||||
"EchoReq", // 9
|
||||
"EchoReply", // 10
|
||||
"DiscardRequest", // 11
|
||||
"IdentRequest" // 12
|
||||
)
|
||||
|
||||
CONSTANT(ppp_lcp_option,
|
||||
0, // 0
|
||||
"Maximum-Receive-Unit", // 1
|
||||
"Async-Control-Map", // 2
|
||||
"Authentication-Protocol", // 3
|
||||
"Quality-Protocol", // 4
|
||||
"Magic-Number", // 5
|
||||
0, // 6
|
||||
"Protocol-Field-Compression", // 7
|
||||
"Address-and-Control-Field-Compression" // 8
|
||||
)
|
||||
|
||||
CONSTANT(radius_state,
|
||||
"RADIUSNULL", // 0
|
||||
"RADIUSCHAP", // 1
|
||||
"RADIUSAUTH", // 2
|
||||
"RADIUSSTART", // 3
|
||||
"RADIUSSTOP", // 4
|
||||
"RADIUSINTERIM", // 5
|
||||
"RADIUSWAIT" // 6
|
||||
)
|
||||
|
||||
CONSTANT(radius_code,
|
||||
0, // 0
|
||||
"Access-Request", // 1
|
||||
"Access-Accept", // 2
|
||||
"Access-Reject", // 3
|
||||
"Accounting-Request", // 4
|
||||
"Accounting-Response", // 5
|
||||
0, // 6
|
||||
0, // 7
|
||||
0, // 8
|
||||
0, // 9
|
||||
0, // 10
|
||||
"Access-Challenge", // 11
|
||||
"Status-Server", // 12
|
||||
"Status-Client", // 13
|
||||
0, 0, 0, 0, 0, 0, // 14-19
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-29
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30-39
|
||||
"Disconnect-Request", // 40
|
||||
"Disconnect-ACK", // 41
|
||||
"Disconnect-NAK", // 42
|
||||
"CoA-Request", // 43
|
||||
"CoA-ACK", // 44
|
||||
"CoA-NAK" // 45
|
||||
)
|
||||
17
constants.h
Normal file
17
constants.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef __CONSTANTS_H__
|
||||
#define __CONSTANTS_H__
|
||||
|
||||
char const *l2tp_code(int type);
|
||||
char const *l2tp_avp_name(int avp);
|
||||
char const *l2tp_stopccn_result_code(int code);
|
||||
char const *l2tp_cdn_result_code(int code);
|
||||
char const *l2tp_error_code(int code);
|
||||
char const *ppp_phase(int code);
|
||||
char const *ppp_state(int code);
|
||||
char const *ppp_auth_type(int type);
|
||||
char const *ppp_code(int type);
|
||||
char const *ppp_lcp_option(int type);
|
||||
char const *radius_state(int state);
|
||||
char const *radius_code(int code);
|
||||
|
||||
#endif /* __CONSTANTS_H__ */
|
||||
163
control.c
Normal file
163
control.c
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// L2TPNS: control
|
||||
|
||||
char const *cvs_id_control = "$Id: control.c,v 1.5 2005/07/31 10:04:10 bodea Exp $";
|
||||
|
||||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "control.h"
|
||||
|
||||
int pack_control(uint8_t *data, int len, uint8_t type, int argc, char *argv[])
|
||||
{
|
||||
struct nsctl_packet pkt;
|
||||
struct nsctl_args arg;
|
||||
char *p = pkt.argv;
|
||||
int sz = (p - (char *) &pkt);
|
||||
|
||||
if (len > sizeof(pkt))
|
||||
len = sizeof(pkt);
|
||||
|
||||
if (argc > 0xff)
|
||||
argc = 0xff; // paranoia
|
||||
|
||||
pkt.magic = ntohs(NSCTL_MAGIC);
|
||||
pkt.type = type;
|
||||
pkt.argc = argc;
|
||||
|
||||
while (argc-- > 0)
|
||||
{
|
||||
char *a = *argv++;
|
||||
int s = strlen(a);
|
||||
|
||||
if (s > sizeof(arg.value))
|
||||
s = sizeof(arg.value); // silently truncate
|
||||
|
||||
arg.len = s;
|
||||
s += sizeof(arg.len);
|
||||
|
||||
if (sz + s > len)
|
||||
return -1; // overflow
|
||||
|
||||
if (arg.len)
|
||||
memcpy(arg.value, a, arg.len);
|
||||
|
||||
memcpy(p, &arg, s);
|
||||
sz += s;
|
||||
p += s;
|
||||
}
|
||||
|
||||
/*
|
||||
* terminate: this is both a sanity check and additionally
|
||||
* ensures that there's a spare byte in the packet to null
|
||||
* terminate the last argument when unpacking (see unpack_control)
|
||||
*/
|
||||
if (sz + sizeof(arg.len) > len)
|
||||
return -1; // overflow
|
||||
|
||||
arg.len = 0xff;
|
||||
memcpy(p, &arg.len, sizeof(arg.len));
|
||||
|
||||
sz += sizeof(arg.len);
|
||||
memcpy(data, &pkt, sz);
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int unpack_control(struct nsctl *control, uint8_t *data, int len)
|
||||
{
|
||||
struct nsctl_packet pkt;
|
||||
char *p = pkt.argv;
|
||||
int sz = (p - (char *) &pkt);
|
||||
int i;
|
||||
|
||||
if (len < sz)
|
||||
return NSCTL_ERR_SHORT;
|
||||
|
||||
if (len > sizeof(pkt))
|
||||
return NSCTL_ERR_LONG;
|
||||
|
||||
memcpy(&pkt, data, len);
|
||||
if (ntohs(pkt.magic) != NSCTL_MAGIC)
|
||||
return NSCTL_ERR_MAGIC;
|
||||
|
||||
switch (pkt.type)
|
||||
{
|
||||
case NSCTL_REQ_LOAD:
|
||||
case NSCTL_REQ_UNLOAD:
|
||||
case NSCTL_REQ_HELP:
|
||||
case NSCTL_REQ_CONTROL:
|
||||
case NSCTL_RES_OK:
|
||||
case NSCTL_RES_ERR:
|
||||
control->type = pkt.type;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NSCTL_ERR_TYPE;
|
||||
}
|
||||
|
||||
control->argc = pkt.argc;
|
||||
for (i = 0; i <= control->argc; i++)
|
||||
{
|
||||
unsigned s;
|
||||
|
||||
if (len < sz + 1)
|
||||
return NSCTL_ERR_SHORT;
|
||||
|
||||
s = (uint8_t) *p;
|
||||
*p++ = 0; // null terminate previous arg
|
||||
sz++;
|
||||
|
||||
if (i < control->argc)
|
||||
{
|
||||
if (len < sz + s)
|
||||
return NSCTL_ERR_SHORT;
|
||||
|
||||
control->argv[i] = p;
|
||||
p += s;
|
||||
sz += s;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check for terminator */
|
||||
if (s != 0xff)
|
||||
return NSCTL_ERR_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (sz != len)
|
||||
return NSCTL_ERR_LONG; // trailing cr*p
|
||||
|
||||
return control->type;
|
||||
}
|
||||
|
||||
void dump_control(struct nsctl *control, FILE *stream)
|
||||
{
|
||||
char *type = "*unknown*";
|
||||
|
||||
if (!stream)
|
||||
stream = stdout;
|
||||
|
||||
switch (control->type)
|
||||
{
|
||||
case NSCTL_REQ_LOAD: type = "NSCTL_REQ_LOAD"; break;
|
||||
case NSCTL_REQ_UNLOAD: type = "NSCTL_REQ_UNLOAD"; break;
|
||||
case NSCTL_REQ_HELP: type = "NSCTL_REQ_HELP"; break;
|
||||
case NSCTL_REQ_CONTROL: type = "NSCTL_REQ_CONTROL"; break;
|
||||
case NSCTL_RES_OK: type = "NSCTL_RES_OK"; break;
|
||||
case NSCTL_RES_ERR: type = "NSCTL_RES_ERR"; break;
|
||||
}
|
||||
|
||||
fprintf(stream, "Control packet:\n");
|
||||
fprintf(stream, " Type: %d (%s)\n", (int) control->type, type);
|
||||
fprintf(stream, " Args: %d", (int) control->argc);
|
||||
if (control->argc)
|
||||
{
|
||||
int i;
|
||||
fprintf(stream, " (\"");
|
||||
for (i = 0; i < control->argc; i++)
|
||||
fprintf(stream, "%s%s", i ? "\", \"" : "", control->argv[i]);
|
||||
|
||||
fprintf(stream, "\")");
|
||||
}
|
||||
|
||||
fprintf(stream, "\n\n");
|
||||
}
|
||||
54
control.h
Normal file
54
control.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef __CONTROL_H__
|
||||
#define __CONTROL_H__
|
||||
|
||||
#define NSCTL_PORT 1702
|
||||
#define NSCTL_MAGIC 0x9013
|
||||
|
||||
/* builtin commands */
|
||||
#define NSCTL_REQUEST (1 << 4)
|
||||
#define NSCTL_REQ_LOAD (NSCTL_REQUEST | 1)
|
||||
#define NSCTL_REQ_UNLOAD (NSCTL_REQUEST | 2)
|
||||
#define NSCTL_REQ_HELP (NSCTL_REQUEST | 3)
|
||||
|
||||
/* general control message, passed to plugins */
|
||||
#define NSCTL_REQ_CONTROL (NSCTL_REQUEST | 4)
|
||||
|
||||
/* response messages */
|
||||
#define NSCTL_RESPONSE (1 << 5)
|
||||
#define NSCTL_RES_OK (NSCTL_RESPONSE | 1)
|
||||
#define NSCTL_RES_ERR (NSCTL_RESPONSE | 2)
|
||||
|
||||
/* unpack errors */
|
||||
#define NSCTL_ERR_SHORT -1 // short packet
|
||||
#define NSCTL_ERR_LONG -2 // packet exceeds max, or trailing cr*p
|
||||
#define NSCTL_ERR_MAGIC -3 // invalid magic number
|
||||
#define NSCTL_ERR_TYPE -4 // unrecognised type
|
||||
|
||||
#define NSCTL_MAX_PKT_SZ 4096
|
||||
|
||||
struct nsctl_packet {
|
||||
uint16_t magic;
|
||||
uint8_t type;
|
||||
uint8_t argc;
|
||||
char argv[NSCTL_MAX_PKT_SZ - 4];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define NSCTL_MAX_ARG_SZ 512
|
||||
|
||||
struct nsctl_args {
|
||||
uint8_t len;
|
||||
char value[NSCTL_MAX_ARG_SZ - 1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* parsed packet */
|
||||
struct nsctl {
|
||||
uint8_t type;
|
||||
uint8_t argc;
|
||||
char *argv[0xff];
|
||||
};
|
||||
|
||||
int pack_control(uint8_t *data, int len, uint8_t type, int argc, char *argv[]);
|
||||
int unpack_control(struct nsctl *packet, uint8_t *data, int len);
|
||||
void dump_control(struct nsctl *control, FILE *stream);
|
||||
|
||||
#endif /* __CONTROL_H__ */
|
||||
2
etc/ip_pool.default
Normal file
2
etc/ip_pool.default
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
10.10.10.0/24
|
||||
10.13.10.0/24
|
||||
9
etc/l2tpns.logrotate
Normal file
9
etc/l2tpns.logrotate
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/var/log/l2tpns {
|
||||
daily
|
||||
missingok
|
||||
rotate 14
|
||||
compress
|
||||
postrotate
|
||||
/usr/bin/killall -HUP l2tpns
|
||||
endscript
|
||||
}
|
||||
117
etc/startup-config.default
Normal file
117
etc/startup-config.default
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# Debugging level
|
||||
set debug 3
|
||||
|
||||
# Log file: comment out to use stderr, use "syslog:facility" for syslog
|
||||
set log_file "/var/log/l2tpns"
|
||||
|
||||
# Write pid to this file
|
||||
set pid_file "/var/run/l2tpns.pid"
|
||||
|
||||
# Shared secret with LAC
|
||||
set l2tp_secret "secret"
|
||||
|
||||
# MTU of interface for L2TP traffic
|
||||
#set l2tp_mtu 1500
|
||||
|
||||
# PPP counter and timer values
|
||||
#set ppp_restart_time 3
|
||||
#set ppp_max_configure 10
|
||||
#set ppp_max_failure 5
|
||||
|
||||
# Only 2 DNS server entries are allowed
|
||||
set primary_dns 10.0.0.1
|
||||
set secondary_dns 10.0.0.2
|
||||
|
||||
# Can have multiple radius server entries, but ony one radius secret
|
||||
set primary_radius 10.0.0.3
|
||||
#set primary_radius_port 1645
|
||||
#set secondary_radius 0.0.0.0
|
||||
#set secondary_radius_port 1645
|
||||
set radius_secret "secret"
|
||||
|
||||
# Acceptable authentication types (pap, chap) in order of preference
|
||||
#set radius_authtypes "pap"
|
||||
|
||||
# Turn on or off Radius Accounting
|
||||
#set radius_accounting no
|
||||
|
||||
# Port for DAE RADIUS requests
|
||||
#set radius_dae_port 3799
|
||||
|
||||
# Allow multiple logins for the same username
|
||||
#set allow_duplicate_users no
|
||||
|
||||
# Write usage accounting files into specified directory
|
||||
set accounting_dir "/var/run/l2tpns/acct"
|
||||
|
||||
# Listen address for L2TP
|
||||
#set bind_address 1.1.1.1
|
||||
|
||||
# Send a gratiuitous ARP for bind address
|
||||
#set send_garp no
|
||||
|
||||
# Gateway address given to clients
|
||||
#set peer_address 0.0.0.0
|
||||
|
||||
# Default throttle rate in kb/s
|
||||
#set throttle_speed 0
|
||||
|
||||
# Number of buckets to allocate for throttling
|
||||
#set throttle_buckets 3000
|
||||
|
||||
# If set to anything other than 0, setuid when initialised.
|
||||
#set setuid 0
|
||||
|
||||
# If set to true, dump current speed to stderr every second
|
||||
#set dump_speed no
|
||||
|
||||
# Number of packets to read from tun/udp/cluster fd when select
|
||||
# returns readable
|
||||
#set multi_read_count 10
|
||||
|
||||
# Set scheduling priority of process to SCHED_FIFO
|
||||
#set scheduler_fifo no
|
||||
|
||||
# Lock pages into memory
|
||||
#set lock_pages no
|
||||
|
||||
# Maximum number of host unreachable packets to send per second
|
||||
#set icmp_rate 0
|
||||
|
||||
# Maximum number of downstream packets per 0.1s to handle for each
|
||||
# session (0 = ulimited)
|
||||
#set packet_limit 0
|
||||
|
||||
# Cluster multicast address, interface
|
||||
#set cluster_address 239.192.13.13
|
||||
#set cluster_interface eth0
|
||||
|
||||
# Cluster multicast TTL
|
||||
#set cluster_mcast_ttl 1
|
||||
|
||||
# Cluster timers (1/10th second)
|
||||
#set cluster_hb_interval 5
|
||||
#set cluster_hb_timeout 150
|
||||
|
||||
# Minimum number of slaves before master withdraws routes
|
||||
#set cluster_master_min_adv 1
|
||||
|
||||
# Drop/kill sessions
|
||||
#load plugin "sessionctl"
|
||||
|
||||
# Throttle/snoop based on RADIUS
|
||||
#load plugin "autothrottle"
|
||||
#load plugin "autosnoop"
|
||||
|
||||
# Control throttle/snoop with nsctl
|
||||
#load plugin "throttlectl"
|
||||
#load plugin "snoopctl"
|
||||
|
||||
# Punt RX speed if not supplied
|
||||
#load plugin "setrxspeed"
|
||||
|
||||
# Remove domain from username
|
||||
#load plugin "stripdomain"
|
||||
|
||||
# Walled garden
|
||||
#load plugin "garden"
|
||||
1
etc/users.default
Normal file
1
etc/users.default
Normal file
|
|
@ -0,0 +1 @@
|
|||
# List username:password combinations here for cli users
|
||||
179
fake_epoll.h
Normal file
179
fake_epoll.h
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/* kludge up some limited epoll semantics using select for 2.4 kernels */
|
||||
/* $Id: fake_epoll.h,v 1.1 2005/06/04 15:42:35 bodea Exp $ */
|
||||
|
||||
#ifndef __FAKE_EPOLL_H__
|
||||
#define __FAKE_EPOLL_H__
|
||||
|
||||
#define EPOLLIN 0x01
|
||||
#define EPOLLOUT 0x04
|
||||
#define EPOLLERR 0x08
|
||||
#define EPOLLHUP 0x10
|
||||
|
||||
#define EPOLL_CTL_ADD 1
|
||||
#define EPOLL_CTL_DEL 2
|
||||
#define EPOLL_CTL_MOD 3
|
||||
|
||||
struct epoll_event {
|
||||
uint32_t events;
|
||||
union epoll_data {
|
||||
void *ptr;
|
||||
int fd;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
} data;
|
||||
};
|
||||
|
||||
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
|
||||
|
||||
#ifdef FAKE_EPOLL_IMPLEMENTATION
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
static fd_set _epoll_read_set;
|
||||
static fd_set _epoll_write_set;
|
||||
static int _epoll_fds;
|
||||
static struct epoll_event *_epoll_data[128];
|
||||
|
||||
static int epoll_create(int size __attribute__ ((unused)))
|
||||
{
|
||||
static int once = 0;
|
||||
if (once++)
|
||||
{
|
||||
errno = ENFILE; /* only support one instance */
|
||||
return -1;
|
||||
}
|
||||
|
||||
FD_ZERO(&_epoll_read_set);
|
||||
FD_ZERO(&_epoll_write_set);
|
||||
_epoll_fds = 0;
|
||||
|
||||
memset(_epoll_data, 0, sizeof(_epoll_data));
|
||||
|
||||
return 1; /* "descriptor" */
|
||||
}
|
||||
|
||||
int epoll_ctl(int epfd __attribute__ ((unused)), int op, int fd,
|
||||
struct epoll_event *event)
|
||||
{
|
||||
if (fd > (sizeof(_epoll_data)/sizeof(*_epoll_data)) - 1)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case EPOLL_CTL_ADD:
|
||||
if (event->events & EPOLLIN)
|
||||
FD_SET(fd, &_epoll_read_set);
|
||||
|
||||
if (event->events & EPOLLOUT)
|
||||
FD_SET(fd, &_epoll_write_set);
|
||||
|
||||
if (fd >= _epoll_fds)
|
||||
_epoll_fds = fd + 1;
|
||||
|
||||
if (_epoll_data[fd])
|
||||
free(_epoll_data[fd]);
|
||||
|
||||
if (!(_epoll_data[fd] = malloc(sizeof(*_epoll_data))))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(_epoll_data[fd], &event->data, sizeof(*_epoll_data));
|
||||
break;
|
||||
|
||||
case EPOLL_CTL_MOD:
|
||||
if (event->events & EPOLLIN)
|
||||
FD_SET(fd, &_epoll_read_set);
|
||||
else
|
||||
FD_CLR(fd, &_epoll_read_set);
|
||||
|
||||
if (event->events & EPOLLOUT)
|
||||
FD_SET(fd, &_epoll_write_set);
|
||||
else
|
||||
FD_CLR(fd, &_epoll_write_set);
|
||||
|
||||
memcpy(_epoll_data[fd], &event->data, sizeof(*_epoll_data));
|
||||
break;
|
||||
|
||||
case EPOLL_CTL_DEL:
|
||||
FD_CLR(fd, &_epoll_read_set);
|
||||
FD_CLR(fd, &_epoll_write_set);
|
||||
|
||||
free(_epoll_data[fd]);
|
||||
_epoll_data[fd] = 0;
|
||||
|
||||
if (fd == _epoll_fds - 1)
|
||||
{
|
||||
_epoll_fds = 0;
|
||||
while (fd-- > 0)
|
||||
{
|
||||
if (FD_ISSET(fd, &_epoll_read_set) ||
|
||||
FD_ISSET(fd, &_epoll_write_set))
|
||||
{
|
||||
_epoll_fds = fd + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int epoll_wait(int epfd __attribute__ ((unused)),
|
||||
struct epoll_event *events, int maxevents, int timout)
|
||||
{
|
||||
fd_set r;
|
||||
fd_set w;
|
||||
struct timeval t;
|
||||
struct timeval *tp;
|
||||
int n;
|
||||
int e;
|
||||
int i;
|
||||
|
||||
memcpy(&r, &_epoll_read_set, sizeof(r));
|
||||
memcpy(&w, &_epoll_write_set, sizeof(w));
|
||||
|
||||
if (timout >= 0)
|
||||
{
|
||||
t.tv_sec = 0;
|
||||
t.tv_usec = timout * 1000;
|
||||
tp = &t;
|
||||
}
|
||||
else
|
||||
tp = 0;
|
||||
|
||||
n = select(_epoll_fds, &r, &w, 0, tp);
|
||||
if (n > maxevents)
|
||||
n = maxevents;
|
||||
|
||||
for (i = e = 0; n > 0 && i < _epoll_fds; i++)
|
||||
{
|
||||
if (!_epoll_data[i])
|
||||
continue;
|
||||
|
||||
events[e].events = 0;
|
||||
if (FD_ISSET(i, &r))
|
||||
events[e].events |= EPOLLIN;
|
||||
|
||||
if (FD_ISSET(i, &w))
|
||||
events[e].events |= EPOLLOUT;
|
||||
|
||||
if (events[e].events)
|
||||
{
|
||||
memcpy(&events[e++].data, _epoll_data[i], sizeof(events[0].data));
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
#endif /* FAKE_EPOLL_IMPLEMENTATION */
|
||||
#endif /* __FAKE_EPOLL_H__ */
|
||||
297
garden.c
Normal file
297
garden.c
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
#include "control.h"
|
||||
|
||||
/* walled garden */
|
||||
|
||||
char const *cvs_id = "$Id: garden.c,v 1.25 2006/02/23 01:07:23 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
static int iam_master = 0; // We're all slaves! Slaves I tell you!
|
||||
|
||||
char *up_commands[] = {
|
||||
"iptables -t nat -N garden >/dev/null 2>&1", // Create a chain that all gardened users will go through
|
||||
"iptables -t nat -F garden",
|
||||
". " PLUGINCONF "/build-garden", // Populate with site-specific DNAT rules
|
||||
"iptables -t nat -N garden_users >/dev/null 2>&1", // Empty chain, users added/removed by garden_session
|
||||
"iptables -t nat -F garden_users",
|
||||
"iptables -t nat -A PREROUTING -j garden_users", // DNAT any users on the garden_users chain
|
||||
"sysctl -w net.ipv4.netfilter.ip_conntrack_max=512000" // lots of entries
|
||||
" net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=18000 >/dev/null", // 5hrs
|
||||
NULL,
|
||||
};
|
||||
|
||||
char *down_commands[] = {
|
||||
"iptables -t nat -F PREROUTING",
|
||||
"iptables -t nat -F garden_users",
|
||||
"iptables -t nat -X garden_users",
|
||||
"iptables -t nat -F garden",
|
||||
"iptables -t nat -X garden",
|
||||
"rmmod iptable_nat", // Should also remove ip_conntrack, but
|
||||
// doing so can take hours... literally.
|
||||
// If a master is re-started as a slave,
|
||||
// either rmmod manually, or reboot.
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define F_UNGARDEN 0
|
||||
#define F_GARDEN 1
|
||||
#define F_CLEANUP 2
|
||||
|
||||
int garden_session(sessiont *s, int flag, char *newuser);
|
||||
|
||||
int plugin_post_auth(struct param_post_auth *data)
|
||||
{
|
||||
// Ignore if user authentication was successful
|
||||
if (data->auth_allowed)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
f->log(3, f->get_id_by_session(data->s), data->s->tunnel,
|
||||
"Walled Garden allowing login\n");
|
||||
|
||||
data->auth_allowed = 1;
|
||||
data->s->walled_garden = 1;
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_new_session(struct param_new_session *data)
|
||||
{
|
||||
if (!iam_master)
|
||||
return PLUGIN_RET_OK; // Slaves don't do walled garden processing.
|
||||
|
||||
if (data->s->walled_garden)
|
||||
garden_session(data->s, F_GARDEN, 0);
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_kill_session(struct param_new_session *data)
|
||||
{
|
||||
if (!iam_master)
|
||||
return PLUGIN_RET_OK; // Slaves don't do walled garden processing.
|
||||
|
||||
if (data->s->walled_garden)
|
||||
garden_session(data->s, F_CLEANUP, 0);
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
char *plugin_control_help[] = {
|
||||
" garden USER|SID Put user into the walled garden",
|
||||
" ungarden SID [USER] Release session from garden",
|
||||
0
|
||||
};
|
||||
|
||||
int plugin_control(struct param_control *data)
|
||||
{
|
||||
sessionidt session;
|
||||
sessiont *s = 0;
|
||||
int flag;
|
||||
char *end;
|
||||
|
||||
if (data->argc < 1)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
if (strcmp(data->argv[0], "garden") && strcmp(data->argv[0], "ungarden"))
|
||||
return PLUGIN_RET_OK; // not for us
|
||||
|
||||
if (!iam_master)
|
||||
return PLUGIN_RET_NOTMASTER;
|
||||
|
||||
flag = data->argv[0][0] == 'g' ? F_GARDEN : F_UNGARDEN;
|
||||
|
||||
if (data->argc < 2 || data->argc > 3 || (data->argc > 2 && flag == F_GARDEN))
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = flag == F_GARDEN
|
||||
? "requires username or session id"
|
||||
: "requires session id and optional username";
|
||||
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (!(session = strtol(data->argv[1], &end, 10)) || *end)
|
||||
{
|
||||
if (flag)
|
||||
session = f->get_session_by_username(data->argv[1]);
|
||||
else
|
||||
session = 0; // can't ungarden by username
|
||||
}
|
||||
|
||||
if (session)
|
||||
s = f->get_session_by_id(session);
|
||||
|
||||
if (!s || !s->ip)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "session not found";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (s->walled_garden == flag)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = flag ? "already in walled garden" : "not in walled garden";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
garden_session(s, flag, data->argc > 2 ? data->argv[2] : 0);
|
||||
f->session_changed(session);
|
||||
|
||||
data->response = NSCTL_RES_OK;
|
||||
data->additional = 0;
|
||||
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
int plugin_become_master(void)
|
||||
{
|
||||
int i;
|
||||
iam_master = 1; // We just became the master. Wow!
|
||||
|
||||
for (i = 0; up_commands[i] && *up_commands[i]; i++)
|
||||
{
|
||||
f->log(3, 0, 0, "Running %s\n", up_commands[i]);
|
||||
system(up_commands[i]);
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
// Called for each active session after becoming master
|
||||
int plugin_new_session_master(sessiont *s)
|
||||
{
|
||||
if (s->walled_garden)
|
||||
garden_session(s, F_GARDEN, 0);
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int garden_session(sessiont *s, int flag, char *newuser)
|
||||
{
|
||||
char cmd[2048];
|
||||
sessionidt sess;
|
||||
|
||||
if (!s) return 0;
|
||||
if (!s->opened) return 0;
|
||||
|
||||
sess = f->get_id_by_session(s);
|
||||
if (flag == F_GARDEN)
|
||||
{
|
||||
f->log(2, sess, s->tunnel, "Garden user %s (%s)\n", s->user,
|
||||
f->fmtaddr(htonl(s->ip), 0));
|
||||
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"iptables -t nat -A garden_users -s %s -j garden",
|
||||
f->fmtaddr(htonl(s->ip), 0));
|
||||
|
||||
f->log(3, sess, s->tunnel, "%s\n", cmd);
|
||||
system(cmd);
|
||||
s->walled_garden = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sessionidt other;
|
||||
int count = 40;
|
||||
|
||||
// Normal User
|
||||
f->log(2, sess, s->tunnel, "Un-Garden user %s (%s)\n", s->user, f->fmtaddr(htonl(s->ip), 0));
|
||||
if (newuser)
|
||||
{
|
||||
snprintf(s->user, MAXUSER, "%s", newuser);
|
||||
f->log(2, sess, s->tunnel, " Setting username to %s\n", s->user);
|
||||
}
|
||||
|
||||
// Kick off any duplicate usernames
|
||||
// but make sure not to kick off ourself
|
||||
if (s->ip && !s->die && (other = f->get_session_by_username(s->user)) &&
|
||||
s != f->get_session_by_id(other))
|
||||
{
|
||||
f->sessionkill(other,
|
||||
"Duplicate session when user released from walled garden");
|
||||
}
|
||||
|
||||
/* Clean up counters */
|
||||
s->pin = s->pout = 0;
|
||||
s->cin = s->cout = 0;
|
||||
s->cin_delta = s->cout_delta = 0;
|
||||
s->cin_wrap = s->cout_wrap = 0;
|
||||
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"iptables -t nat -D garden_users -s %s -j garden",
|
||||
f->fmtaddr(htonl(s->ip), 0));
|
||||
|
||||
f->log(3, sess, s->tunnel, "%s\n", cmd);
|
||||
while (--count)
|
||||
{
|
||||
int status = system(cmd);
|
||||
if (WEXITSTATUS(status) != 0) break;
|
||||
}
|
||||
|
||||
s->walled_garden = 0;
|
||||
|
||||
if (flag != F_CLEANUP)
|
||||
{
|
||||
/* OK, we're up! */
|
||||
uint16_t r = f->radiusnew(f->get_id_by_session(s));
|
||||
if (r) f->radiussend(r, RADIUSSTART);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
FILE *tables;
|
||||
int found_nat = 0;
|
||||
|
||||
if (!funcs)
|
||||
return 0;
|
||||
|
||||
f = funcs;
|
||||
|
||||
if ((tables = fopen("/proc/net/ip_tables_names", "r")))
|
||||
{
|
||||
char buf[1024];
|
||||
while (fgets(buf, sizeof(buf), tables) && !found_nat)
|
||||
found_nat = !strcmp(buf, "nat\n");
|
||||
|
||||
fclose(tables);
|
||||
}
|
||||
|
||||
/* master killed/crashed? */
|
||||
if (found_nat)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; down_commands[i] && *down_commands[i]; i++)
|
||||
{
|
||||
f->log(3, 0, 0, "Running %s\n", down_commands[i]);
|
||||
system(down_commands[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void plugin_done()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!iam_master) // Never became master. nothing to do.
|
||||
return;
|
||||
|
||||
for (i = 0; down_commands[i] && *down_commands[i]; i++)
|
||||
{
|
||||
f->log(3, 0, 0, "Running %s\n", down_commands[i]);
|
||||
system(down_commands[i]);
|
||||
}
|
||||
}
|
||||
|
||||
177
icmp.c
Normal file
177
icmp.c
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// L2TPNS: icmp
|
||||
|
||||
char const *cvs_id_icmp = "$Id: icmp.c,v 1.10 2005/08/10 11:25:56 bodea Exp $";
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <asm/types.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "l2tpns.h"
|
||||
|
||||
static uint16_t _checksum(uint8_t *addr, int count);
|
||||
|
||||
struct ipv6_pseudo_hdr {
|
||||
struct in6_addr src;
|
||||
struct in6_addr dest;
|
||||
uint32_t ulp_length;
|
||||
uint32_t zero : 24;
|
||||
uint32_t nexthdr : 8;
|
||||
};
|
||||
|
||||
void host_unreachable(in_addr_t destination, uint16_t id, in_addr_t source, uint8_t *packet, int packet_len)
|
||||
{
|
||||
char buf[128] = {0};
|
||||
struct iphdr *iph;
|
||||
struct icmphdr *icmp;
|
||||
int len = 0, on = 1, icmp_socket;
|
||||
struct sockaddr_in whereto = {0};
|
||||
|
||||
if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
||||
return;
|
||||
|
||||
setsockopt(icmp_socket, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on));
|
||||
|
||||
whereto.sin_addr.s_addr = destination;
|
||||
whereto.sin_family = AF_INET;
|
||||
|
||||
iph = (struct iphdr *)(buf);
|
||||
len = sizeof(struct iphdr);
|
||||
icmp = (struct icmphdr *)(buf + len);
|
||||
len += sizeof(struct icmphdr);
|
||||
|
||||
/* ip header + first 8 bytes of payload */
|
||||
if (packet_len > (sizeof(struct iphdr) + 8))
|
||||
packet_len = sizeof(struct iphdr) + 8;
|
||||
|
||||
memcpy(buf + len, packet, packet_len);
|
||||
len += packet_len;
|
||||
|
||||
iph->tos = 0;
|
||||
iph->id = id;
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = 30;
|
||||
iph->check = 0;
|
||||
iph->version = 4;
|
||||
iph->ihl = 5;
|
||||
iph->protocol = 1;
|
||||
iph->check = 0;
|
||||
iph->daddr = destination;
|
||||
iph->saddr = source;
|
||||
|
||||
iph->tot_len = ntohs(len);
|
||||
|
||||
icmp->type = ICMP_DEST_UNREACH;
|
||||
icmp->code = ICMP_HOST_UNREACH;
|
||||
icmp->checksum = _checksum((uint8_t *) icmp, sizeof(struct icmphdr) + packet_len);
|
||||
|
||||
iph->check = _checksum((uint8_t *) iph, sizeof(struct iphdr));
|
||||
|
||||
sendto(icmp_socket, buf, len, 0, (struct sockaddr *)&whereto, sizeof(struct sockaddr));
|
||||
close(icmp_socket);
|
||||
}
|
||||
|
||||
static uint16_t _checksum(uint8_t *addr, int count)
|
||||
{
|
||||
register long sum = 0;
|
||||
|
||||
for (; count > 1; count -= 2)
|
||||
{
|
||||
sum += ntohs(*(uint32_t *) addr);
|
||||
addr += 2;
|
||||
}
|
||||
|
||||
if (count > 1) sum += *(unsigned char *)addr;
|
||||
|
||||
// take only 16 bits out of the 32 bit sum and add up the carries
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
|
||||
// one's complement the result
|
||||
sum = ~sum;
|
||||
|
||||
return htons((uint16_t) sum);
|
||||
}
|
||||
|
||||
void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip)
|
||||
{
|
||||
struct nd_opt_prefix_info *pinfo;
|
||||
struct ipv6_pseudo_hdr *phdr;
|
||||
uint8_t b[MAXETHER + 20];
|
||||
uint8_t c[MAXETHER + 20];
|
||||
int l;
|
||||
uint8_t *o;
|
||||
|
||||
LOG(3, s, t, "Sending IPv6 RA\n");
|
||||
|
||||
memset(b, 0, sizeof(b));
|
||||
o = makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6);
|
||||
|
||||
if (!o)
|
||||
{
|
||||
LOG(3, s, t, "failed to send IPv6 RA\n");
|
||||
return;
|
||||
}
|
||||
|
||||
*o = 0x60; // IPv6
|
||||
*(o+1) = 0;
|
||||
*(o+5) = 48; // Length of payload (not header)
|
||||
*(o+6) = 58; // icmp6 is next
|
||||
*(o+7) = 255; // Hop limit
|
||||
memset(o+8, 0, 16); // source = FE80::1
|
||||
*(o+8) = 0xFE;
|
||||
*(o+9) = 0x80;
|
||||
*(o+23) = 1;
|
||||
if (ip != NULL)
|
||||
memcpy(o+24, ip, 16); // dest = ip
|
||||
else
|
||||
{
|
||||
// FF02::1 - all hosts
|
||||
*(o+24) = 0xFF;
|
||||
*(o+25) = 2;
|
||||
*(o+39) = 1;
|
||||
}
|
||||
*(o+40) = 134; // RA message
|
||||
*(o+41) = 0; // Code
|
||||
*(o+42) = *(o+43) = 0; // Checksum
|
||||
*(o+44) = 64; // Hop count
|
||||
*(o+45) = 0; // Flags
|
||||
*(o+46) = *(o+47) = 255; // Lifetime
|
||||
*(uint32_t *)(o+48) = 0; // Reachable time
|
||||
*(uint32_t *)(o+52) = 0; // Retrans timer
|
||||
pinfo = (struct nd_opt_prefix_info *)(o+56);
|
||||
pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
|
||||
pinfo->nd_opt_pi_len = 4;
|
||||
pinfo->nd_opt_pi_prefix_len = 64; // prefix length
|
||||
pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK;
|
||||
pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
|
||||
pinfo->nd_opt_pi_valid_time = htonl(2592000);
|
||||
pinfo->nd_opt_pi_preferred_time = htonl(604800);
|
||||
pinfo->nd_opt_pi_reserved2 = 0;
|
||||
pinfo->nd_opt_pi_prefix = config->ipv6_prefix;
|
||||
l = sizeof(*pinfo) + 56;
|
||||
|
||||
memset(c, 0, sizeof(c));
|
||||
phdr = (struct ipv6_pseudo_hdr *) c;
|
||||
memcpy(&phdr->src, o+8, 16);
|
||||
memcpy(&phdr->dest, o+24, 16);
|
||||
phdr->ulp_length = htonl(l - 40);
|
||||
phdr->nexthdr = IPPROTO_ICMPV6;
|
||||
|
||||
memcpy(c + sizeof(*phdr), o + 40, l - 40);
|
||||
|
||||
// Checksum is over the icmp6 payload plus the pseudo header
|
||||
*(uint16_t *)(o+42) = _checksum(c, l - 40 + sizeof(*phdr));
|
||||
|
||||
tunnelsend(b, l + (o-b), t); // send it...
|
||||
return;
|
||||
}
|
||||
859
l2tpns.h
Normal file
859
l2tpns.h
Normal file
|
|
@ -0,0 +1,859 @@
|
|||
// L2TPNS Global Stuff
|
||||
// $Id: l2tpns.h,v 1.113.2.3 2006/12/02 14:09:14 bodea Exp $
|
||||
|
||||
#ifndef __L2TPNS_H__
|
||||
#define __L2TPNS_H__
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <libcli.h>
|
||||
|
||||
#define VERSION "2.1.21"
|
||||
|
||||
// Limits
|
||||
#define MAXTUNNEL 500 // could be up to 65535
|
||||
#define MAXSESSION 60000 // could be up to 65535
|
||||
#define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session.
|
||||
|
||||
#define RADIUS_SHIFT 6
|
||||
#define RADIUS_FDS (1 << RADIUS_SHIFT)
|
||||
#define RADIUS_MASK ((1 << RADIUS_SHIFT) - 1)
|
||||
#define MAXRADIUS (1 << (8 + RADIUS_SHIFT))
|
||||
|
||||
#define T_UNDEF (0xffff) // A tunnel ID that won't ever be used. Mark session as undefined.
|
||||
#define T_FREE (0) // A tunnel ID that won't ever be used. Mark session as free.
|
||||
|
||||
#define MAXCONTROL 1000 // max length control message we ever send...
|
||||
#define MINMTU 576 // minimum recommended MTU (rfc1063)
|
||||
#define MAXMTU 2600 // arbitrary maximum MTU
|
||||
#define PPPoE_MRU 1492 // maximum PPPoE MRU (rfc2516: 1500 less PPPoE header (6) and PPP protocol ID (2))
|
||||
#define MAXETHER (MAXMTU+18) // max packet we try sending to tun
|
||||
#define MAXTEL 96 // telephone number
|
||||
#define MAXUSER 128 // username
|
||||
#define MAXPASS 128 // password
|
||||
#define MAXPLUGINS 20 // maximum number of plugins to load
|
||||
#define MAXRADSERVER 10 // max radius servers
|
||||
#define MAXROUTE 10 // max static routes per session
|
||||
#define MAXIPPOOL 131072 // max number of ip addresses in pool
|
||||
#define RINGBUFFER_SIZE 10000 // Number of ringbuffer entries to allocate
|
||||
#define MAX_LOG_LENGTH 512 // Maximum size of log message
|
||||
#define ECHO_TIMEOUT 60 // Time between last packet sent and LCP ECHO generation
|
||||
#define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation
|
||||
#define BUSY_WAIT_TIME 3000 // 5 minutes in 1/10th seconds to wait for radius to cleanup on shutdown
|
||||
|
||||
// Constants
|
||||
#ifndef ETCDIR
|
||||
#define ETCDIR "/etc/l2tpns"
|
||||
#endif
|
||||
|
||||
#ifndef LIBDIR
|
||||
#define LIBDIR "/usr/lib/l2tpns"
|
||||
#endif
|
||||
|
||||
#ifndef PLUGINDIR
|
||||
#define PLUGINDIR LIBDIR // Plugins
|
||||
#endif
|
||||
|
||||
#ifndef PLUGINCONF
|
||||
#define PLUGINCONF ETCDIR // Plugin config dir
|
||||
#endif
|
||||
|
||||
#ifndef FLASHDIR
|
||||
#define FLASHDIR ETCDIR
|
||||
#endif
|
||||
|
||||
#define TUNDEVICE "/dev/net/tun"
|
||||
#define RANDOMDEVICE "/dev/urandom" // default, not as secure as /dev/random but non-blocking
|
||||
#define CONFIGFILE FLASHDIR "/startup-config" // Configuration file
|
||||
#define CLIUSERS FLASHDIR "/users" // CLI Users file
|
||||
#define IPPOOLFILE FLASHDIR "/ip_pool" // Address pool configuration
|
||||
#define ACCT_TIME 3000 // 5 minute accounting interval
|
||||
#define ACCT_SHUT_TIME 600 // 1 minute for counters of shutdown sessions
|
||||
#define L2TPPORT 1701 // L2TP port
|
||||
#define RADPORT 1645 // old radius port...
|
||||
#define DAEPORT 3799 // DAE port
|
||||
#define PKTARP 0x0806 // ARP packet type
|
||||
#define PKTIP 0x0800 // IPv4 packet type
|
||||
#define PKTIPV6 0x86DD // IPv6 packet type
|
||||
#define PPPPAP 0xC023
|
||||
#define PPPCHAP 0xC223
|
||||
#define PPPLCP 0xC021
|
||||
#define PPPIPCP 0x8021
|
||||
#define PPPIPV6CP 0x8057
|
||||
#define PPPCCP 0x80FD
|
||||
#define PPPIP 0x0021
|
||||
#define PPPIPV6 0x0057
|
||||
#define PPPMP 0x003D
|
||||
#define MIN_IP_SIZE 0x19
|
||||
|
||||
enum {
|
||||
ConfigReq = 1,
|
||||
ConfigAck,
|
||||
ConfigNak,
|
||||
ConfigRej,
|
||||
TerminateReq,
|
||||
TerminateAck,
|
||||
CodeRej,
|
||||
ProtocolRej,
|
||||
EchoReq,
|
||||
EchoReply,
|
||||
DiscardRequest,
|
||||
IdentRequest
|
||||
};
|
||||
|
||||
enum {
|
||||
AccessRequest = 1,
|
||||
AccessAccept,
|
||||
AccessReject,
|
||||
AccountingRequest,
|
||||
AccountingResponse,
|
||||
AccessChallenge = 11,
|
||||
DisconnectRequest = 40,
|
||||
DisconnectACK,
|
||||
DisconnectNAK,
|
||||
CoARequest,
|
||||
CoAACK,
|
||||
CoANAK
|
||||
};
|
||||
|
||||
// PPP phases
|
||||
enum {
|
||||
Dead,
|
||||
Establish,
|
||||
Authenticate,
|
||||
Network,
|
||||
Terminate
|
||||
};
|
||||
|
||||
// PPP states
|
||||
enum {
|
||||
Initial,
|
||||
Starting,
|
||||
Closed,
|
||||
Stopped,
|
||||
Closing,
|
||||
Stopping,
|
||||
RequestSent,
|
||||
AckReceived,
|
||||
AckSent,
|
||||
Opened
|
||||
};
|
||||
|
||||
// reset state machine counters
|
||||
#define initialise_restart_count(_s, _fsm) \
|
||||
sess_local[_s]._fsm.conf_sent = \
|
||||
sess_local[_s]._fsm.nak_sent = 0
|
||||
|
||||
// no more attempts
|
||||
#define zero_restart_count(_s, _fsm) ({ \
|
||||
sess_local[_s]._fsm.conf_sent = \
|
||||
config->ppp_max_configure; \
|
||||
sess_local[_s]._fsm.restart = \
|
||||
time_now + config->ppp_restart_time; \
|
||||
})
|
||||
|
||||
// increment ConfReq counter and reset timer
|
||||
#define restart_timer(_s, _fsm) ({ \
|
||||
sess_local[_s]._fsm.conf_sent++; \
|
||||
sess_local[_s]._fsm.restart = \
|
||||
time_now + config->ppp_restart_time; \
|
||||
})
|
||||
|
||||
// stop timer on change to state where timer does not run
|
||||
#define change_state(_s, _fsm, _new) ({ \
|
||||
if (_new != session[_s].ppp._fsm) \
|
||||
{ \
|
||||
switch (_new) \
|
||||
{ \
|
||||
case Initial: \
|
||||
case Starting: \
|
||||
case Closed: \
|
||||
case Stopped: \
|
||||
case Opened: \
|
||||
sess_local[_s]._fsm.restart = 0; \
|
||||
initialise_restart_count(_s, _fsm); \
|
||||
} \
|
||||
session[_s].ppp._fsm = _new; \
|
||||
cluster_send_session(_s); \
|
||||
} \
|
||||
})
|
||||
|
||||
// Types
|
||||
typedef uint16_t sessionidt;
|
||||
typedef uint16_t tunnelidt;
|
||||
typedef uint32_t clockt;
|
||||
typedef uint8_t hasht[16];
|
||||
|
||||
// CLI actions
|
||||
struct cli_session_actions {
|
||||
char action;
|
||||
in_addr_t snoop_ip;
|
||||
uint16_t snoop_port;
|
||||
int throttle_in;
|
||||
int throttle_out;
|
||||
int filter_in;
|
||||
int filter_out;
|
||||
};
|
||||
|
||||
#define CLI_SESS_KILL 0x01
|
||||
#define CLI_SESS_SNOOP 0x02
|
||||
#define CLI_SESS_NOSNOOP 0x04
|
||||
#define CLI_SESS_THROTTLE 0x08
|
||||
#define CLI_SESS_NOTHROTTLE 0x10
|
||||
#define CLI_SESS_FILTER 0x20
|
||||
#define CLI_SESS_NOFILTER 0x40
|
||||
|
||||
struct cli_tunnel_actions {
|
||||
char action;
|
||||
};
|
||||
|
||||
#define CLI_TUN_KILL 0x01
|
||||
|
||||
// structures
|
||||
typedef struct // route
|
||||
{
|
||||
in_addr_t ip;
|
||||
in_addr_t mask;
|
||||
}
|
||||
routet;
|
||||
|
||||
typedef struct controls // control message
|
||||
{
|
||||
struct controls *next; // next in queue
|
||||
uint16_t length; // length
|
||||
uint8_t buf[MAXCONTROL];
|
||||
}
|
||||
controlt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sessionidt next; // next session in linked list
|
||||
sessionidt far; // far end session ID
|
||||
tunnelidt tunnel; // near end tunnel ID
|
||||
uint8_t flags; // session flags: see SESSION_*
|
||||
struct {
|
||||
uint8_t phase; // PPP phase
|
||||
uint8_t lcp:4; // LCP state
|
||||
uint8_t ipcp:4; // IPCP state
|
||||
uint8_t ipv6cp:4; // IPV6CP state
|
||||
uint8_t ccp:4; // CCP state
|
||||
} ppp;
|
||||
char reserved_1[2]; // unused: padding
|
||||
in_addr_t ip; // IP of session set by RADIUS response (host byte order).
|
||||
int ip_pool_index; // index to IP pool
|
||||
uint32_t unique_id; // unique session id
|
||||
char reserved_2[4]; // unused: was ns/nr
|
||||
uint32_t magic; // ppp magic number
|
||||
uint32_t pin, pout; // packet counts
|
||||
uint32_t cin, cout; // byte counts
|
||||
uint32_t cin_wrap, cout_wrap; // byte counter wrap count (RADIUS accounting giagawords)
|
||||
uint32_t cin_delta, cout_delta; // byte count changes (for dump_session())
|
||||
uint16_t throttle_in; // upstream throttle rate (kbps)
|
||||
uint16_t throttle_out; // downstream throttle rate
|
||||
uint8_t filter_in; // input filter index (to ip_filters[N-1]; 0 if none)
|
||||
uint8_t filter_out; // output filter index
|
||||
uint16_t mru; // maximum receive unit
|
||||
clockt opened; // when started
|
||||
clockt die; // being closed, when to finally free
|
||||
time_t last_packet; // Last packet from the user (used for idle timeouts)
|
||||
in_addr_t dns1, dns2; // DNS servers
|
||||
routet route[MAXROUTE]; // static routes
|
||||
uint16_t tbf_in; // filter bucket for throttling in from the user.
|
||||
uint16_t tbf_out; // filter bucket for throttling out to the user.
|
||||
int random_vector_length;
|
||||
uint8_t random_vector[MAXTEL];
|
||||
char user[MAXUSER]; // user (needed in seesion for radius stop messages)
|
||||
char called[MAXTEL]; // called number
|
||||
char calling[MAXTEL]; // calling number
|
||||
uint32_t tx_connect_speed;
|
||||
uint32_t rx_connect_speed;
|
||||
in_addr_t snoop_ip; // Interception destination IP
|
||||
uint16_t snoop_port; // Interception destination port
|
||||
uint8_t walled_garden; // is this session gardened?
|
||||
uint8_t ipv6prefixlen; // IPv6 route prefix length
|
||||
struct in6_addr ipv6route; // Static IPv6 route
|
||||
char reserved_3[11]; // Space to expand structure without changing HB_VERSION
|
||||
}
|
||||
sessiont;
|
||||
|
||||
#define AUTHPAP 1 // allow PAP
|
||||
#define AUTHCHAP 2 // allow CHAP
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// packet counters
|
||||
uint32_t pin;
|
||||
uint32_t pout;
|
||||
|
||||
// byte counters
|
||||
uint32_t cin;
|
||||
uint32_t cout;
|
||||
|
||||
// PPP restart timer/counters
|
||||
struct {
|
||||
time_t restart;
|
||||
int conf_sent;
|
||||
int nak_sent;
|
||||
} lcp, ipcp, ipv6cp, ccp;
|
||||
|
||||
// identifier for Protocol-Reject, Code-Reject
|
||||
uint8_t lcp_ident;
|
||||
|
||||
// authentication to use
|
||||
int lcp_authtype;
|
||||
|
||||
// our MRU
|
||||
uint16_t ppp_mru;
|
||||
|
||||
// DoS prevention
|
||||
clockt last_packet_out;
|
||||
uint32_t packets_out;
|
||||
uint32_t packets_dropped;
|
||||
|
||||
// RADIUS session in use
|
||||
uint16_t radius;
|
||||
|
||||
// interim RADIUS
|
||||
time_t last_interim;
|
||||
|
||||
// last LCP Echo
|
||||
time_t last_echo;
|
||||
} sessionlocalt;
|
||||
|
||||
// session flags
|
||||
#define SESSION_PFC (1 << 0) // use Protocol-Field-Compression
|
||||
#define SESSION_ACFC (1 << 1) // use Address-and-Control-Field-Compression
|
||||
#define SESSION_STARTED (1 << 2) // RADIUS Start record sent
|
||||
|
||||
// 168 bytes per tunnel
|
||||
typedef struct
|
||||
{
|
||||
tunnelidt far; // far end tunnel ID
|
||||
in_addr_t ip; // Ip for far end
|
||||
uint16_t port; // port for far end
|
||||
uint16_t window; // Rx window
|
||||
uint16_t nr; // next receive
|
||||
uint16_t ns; // next send
|
||||
int state; // current state (tunnelstate enum)
|
||||
clockt last; // when last control message sent (used for resend timeout)
|
||||
clockt retry; // when to try resending pending control
|
||||
clockt die; // being closed, when to finally free
|
||||
clockt lastrec; // when the last control message was received
|
||||
char hostname[128]; // tunnel hostname
|
||||
char vendor[128]; // LAC vendor
|
||||
uint8_t try; // number of retrys on a control message
|
||||
uint16_t controlc; // outstaind messages in queue
|
||||
controlt *controls; // oldest message
|
||||
controlt *controle; // newest message
|
||||
}
|
||||
tunnelt;
|
||||
|
||||
// 164 bytes per radius session
|
||||
typedef struct // outstanding RADIUS requests
|
||||
{
|
||||
sessionidt session; // which session this applies to
|
||||
hasht auth; // request authenticator
|
||||
clockt retry; // when to try next
|
||||
char pass[129]; // password
|
||||
uint8_t id; // ID for PPP response
|
||||
uint8_t try; // which try we are on
|
||||
uint8_t state; // state of radius requests
|
||||
uint8_t chap; // set if CHAP used (is CHAP identifier)
|
||||
uint8_t term_cause; // Stop record: Acct-Terminate-Cause
|
||||
char const *term_msg; // terminate reason
|
||||
}
|
||||
radiust;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
in_addr_t address; // Host byte order..
|
||||
char assigned; // 1 if assigned, 0 if free
|
||||
sessionidt session;
|
||||
clockt last; // last used
|
||||
char user[129]; // user (try to have ip addresses persistent)
|
||||
}
|
||||
ippoolt;
|
||||
|
||||
#ifdef RINGBUFFER
|
||||
struct Tringbuffer
|
||||
{
|
||||
struct {
|
||||
char level;
|
||||
sessionidt session;
|
||||
tunnelidt tunnel;
|
||||
char message[MAX_LOG_LENGTH];
|
||||
} buffer[RINGBUFFER_SIZE];
|
||||
int head;
|
||||
int tail;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Possible tunnel states
|
||||
* TUNNELFREE -> TUNNELOPEN -> TUNNELDIE -> TUNNELFREE
|
||||
*/
|
||||
enum
|
||||
{
|
||||
TUNNELFREE, // Not in use
|
||||
TUNNELOPEN, // Active tunnel
|
||||
TUNNELDIE, // Currently closing
|
||||
TUNNELOPENING, // Busy opening
|
||||
TUNNELUNDEF, // Undefined
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
RADIUSNULL, // Not in use
|
||||
RADIUSCHAP, // sending CHAP down PPP
|
||||
RADIUSAUTH, // sending auth to RADIUS server
|
||||
RADIUSSTART, // sending start accounting to RADIUS server
|
||||
RADIUSSTOP, // sending stop accounting to RADIUS server
|
||||
RADIUSINTERIM, // sending interim accounting to RADIUS server
|
||||
RADIUSWAIT, // waiting timeout before available, in case delayed replies
|
||||
};
|
||||
|
||||
struct Tstats
|
||||
{
|
||||
time_t start_time;
|
||||
time_t last_reset;
|
||||
|
||||
uint32_t tun_rx_packets;
|
||||
uint32_t tun_tx_packets;
|
||||
uint32_t tun_rx_bytes;
|
||||
uint32_t tun_tx_bytes;
|
||||
uint32_t tun_rx_errors;
|
||||
uint32_t tun_tx_errors;
|
||||
uint32_t tun_rx_dropped;
|
||||
|
||||
uint32_t tunnel_rx_packets;
|
||||
uint32_t tunnel_tx_packets;
|
||||
uint32_t tunnel_rx_bytes;
|
||||
uint32_t tunnel_tx_bytes;
|
||||
uint32_t tunnel_rx_errors;
|
||||
uint32_t tunnel_tx_errors;
|
||||
|
||||
uint32_t tunnel_retries;
|
||||
uint32_t radius_retries;
|
||||
|
||||
uint32_t arp_sent;
|
||||
|
||||
uint32_t packets_snooped;
|
||||
|
||||
uint32_t tunnel_created;
|
||||
uint32_t session_created;
|
||||
uint32_t tunnel_timeout;
|
||||
uint32_t session_timeout;
|
||||
uint32_t radius_timeout;
|
||||
uint32_t radius_overflow;
|
||||
uint32_t tunnel_overflow;
|
||||
uint32_t session_overflow;
|
||||
|
||||
uint32_t ip_allocated;
|
||||
uint32_t ip_freed;
|
||||
|
||||
uint32_t c_forwarded;
|
||||
uint32_t recv_forward;
|
||||
|
||||
uint32_t select_called;
|
||||
uint32_t multi_read_used;
|
||||
uint32_t multi_read_exceeded;
|
||||
|
||||
#ifdef STATISTICS
|
||||
uint32_t call_processtun;
|
||||
uint32_t call_processipout;
|
||||
uint32_t call_processipv6out;
|
||||
uint32_t call_processudp;
|
||||
uint32_t call_sessionbyip;
|
||||
uint32_t call_sessionbyipv6;
|
||||
uint32_t call_sessionbyuser;
|
||||
uint32_t call_sendarp;
|
||||
uint32_t call_sendipcp;
|
||||
uint32_t call_sendipv6cp;
|
||||
uint32_t call_processipv6cp;
|
||||
uint32_t call_tunnelsend;
|
||||
uint32_t call_sessionkill;
|
||||
uint32_t call_sessionshutdown;
|
||||
uint32_t call_tunnelkill;
|
||||
uint32_t call_tunnelshutdown;
|
||||
uint32_t call_assign_ip_address;
|
||||
uint32_t call_free_ip_address;
|
||||
uint32_t call_dump_acct_info;
|
||||
uint32_t call_sessionsetup;
|
||||
uint32_t call_processpap;
|
||||
uint32_t call_processchap;
|
||||
uint32_t call_processlcp;
|
||||
uint32_t call_processipcp;
|
||||
uint32_t call_processipin;
|
||||
uint32_t call_processipv6in;
|
||||
uint32_t call_processccp;
|
||||
uint32_t call_sendchap;
|
||||
uint32_t call_processrad;
|
||||
uint32_t call_radiussend;
|
||||
uint32_t call_radiusretry;
|
||||
uint32_t call_random_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef STATISTICS
|
||||
|
||||
#ifdef STAT_CALLS
|
||||
#define CSTAT(x) STAT(call_ ## x)
|
||||
#else
|
||||
#define CSTAT(x)
|
||||
#endif
|
||||
|
||||
#define STAT(x) (_statistics->x++)
|
||||
#define INC_STAT(x,y) (_statistics->x += (y))
|
||||
#define GET_STAT(x) (_statistics->x)
|
||||
#define SET_STAT(x, y) (_statistics->x = (y))
|
||||
#else
|
||||
#define CSTAT(x)
|
||||
#define STAT(x)
|
||||
#define INC_STAT(x,y)
|
||||
#define GET_STAT(x) 0
|
||||
#define SET_STAT(x, y)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int debug; // debugging level
|
||||
time_t start_time; // time when l2tpns was started
|
||||
char bandwidth[256]; // current bandwidth
|
||||
char pid_file[256]; // file to write PID to on startup
|
||||
int wrote_pid;
|
||||
clockt current_time; // 1/10ths of a second since the process started.
|
||||
// means that we can only run a given process
|
||||
// for 13 years without re-starting!
|
||||
|
||||
char config_file[128];
|
||||
int reload_config; // flag to re-read config (set by cli)
|
||||
int multi_read_count; // amount of packets to read per fd in processing loop
|
||||
|
||||
char tundevice[10]; // tun device name
|
||||
char log_filename[128];
|
||||
|
||||
char l2tp_secret[64]; // L2TP shared secret
|
||||
int l2tp_mtu; // MTU of interface used for L2TP
|
||||
|
||||
char random_device[256]; // random device path, defaults to RANDOMDEVICE
|
||||
|
||||
int ppp_restart_time; // timeout for PPP restart
|
||||
int ppp_max_configure; // max lcp configure requests to send
|
||||
int ppp_max_failure; // max lcp configure naks to send
|
||||
|
||||
char radiussecret[64];
|
||||
int radius_accounting;
|
||||
int radius_interim;
|
||||
in_addr_t radiusserver[MAXRADSERVER]; // radius servers
|
||||
uint16_t radiusport[MAXRADSERVER]; // radius base ports
|
||||
uint8_t numradiusservers; // radius server count
|
||||
|
||||
uint16_t radius_dae_port; // local port for radius dae
|
||||
|
||||
char radius_authtypes_s[32]; // list of valid authentication types (chap, pap) in order of preference
|
||||
int radius_authtypes;
|
||||
int radius_authprefer;
|
||||
|
||||
int allow_duplicate_users; // allow multiple logins with the same username
|
||||
|
||||
in_addr_t default_dns1, default_dns2;
|
||||
|
||||
unsigned long rl_rate; // default throttle rate
|
||||
int num_tbfs; // number of throttle buckets
|
||||
|
||||
char accounting_dir[128];
|
||||
in_addr_t bind_address;
|
||||
in_addr_t peer_address;
|
||||
int send_garp; // Set to true to garp for vip address on startup
|
||||
|
||||
int target_uid;
|
||||
int dump_speed;
|
||||
char plugins[64][MAXPLUGINS];
|
||||
char old_plugins[64][MAXPLUGINS];
|
||||
|
||||
int next_tbf; // Next HTB id available to use
|
||||
int scheduler_fifo; // If the system has multiple CPUs, use FIFO scheduling
|
||||
// policy for this process.
|
||||
int lock_pages; // Lock pages into memory.
|
||||
int icmp_rate; // Max number of ICMP unreachable per second to send
|
||||
int max_packets; // DoS prevention: per session limit of packets/0.1s
|
||||
|
||||
in_addr_t cluster_address; // Multicast address of cluster.
|
||||
// Send to this address to have everyone hear.
|
||||
char cluster_interface[64]; // Which interface to listen for multicast on.
|
||||
int cluster_iam_master; // Are we the cluster master???
|
||||
int cluster_iam_uptodate; // Set if we've got a full set of state from the master.
|
||||
in_addr_t cluster_master_address; // The network address of the cluster master.
|
||||
// Zero if i am the cluster master.
|
||||
int cluster_seq_number; // Sequence number of the next heartbeat we'll send out
|
||||
// (or the seq number we're next expecting if we're a slave).
|
||||
int cluster_undefined_sessions; // How many sessions we're yet to receive from the master.
|
||||
int cluster_undefined_tunnels; // How many tunnels we're yet to receive from the master.
|
||||
int cluster_highest_sessionid;
|
||||
int cluster_highest_tunnelid;
|
||||
clockt cluster_last_hb; // Last time we saw a heartbeat from the master.
|
||||
int cluster_last_hb_ver; // Heartbeat version last seen from master
|
||||
int cluster_num_changes; // Number of changes queued.
|
||||
|
||||
int cluster_mcast_ttl; // TTL for multicast packets
|
||||
int cluster_hb_interval; // How often to send a heartbeat.
|
||||
int cluster_hb_timeout; // How many missed heartbeats trigger an election.
|
||||
uint64_t cluster_table_version; // # state changes processed by cluster
|
||||
|
||||
struct in6_addr ipv6_prefix; // Our IPv6 network pool.
|
||||
|
||||
|
||||
int cluster_master_min_adv; // Master advertises routes while the number of up to date
|
||||
// slaves is less than this value.
|
||||
|
||||
#ifdef BGP
|
||||
#define BGP_NUM_PEERS 2
|
||||
uint16_t as_number;
|
||||
struct {
|
||||
char name[64];
|
||||
uint16_t as;
|
||||
int keepalive;
|
||||
int hold;
|
||||
} neighbour[BGP_NUM_PEERS];
|
||||
#endif
|
||||
} configt;
|
||||
|
||||
enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IPv4, IPv6 };
|
||||
typedef struct
|
||||
{
|
||||
char *key;
|
||||
int offset;
|
||||
int size;
|
||||
enum config_typet type;
|
||||
} config_descriptt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t op; // operation
|
||||
#define FILTER_PORT_OP_NONE 0 // all ports match
|
||||
#define FILTER_PORT_OP_EQ 1
|
||||
#define FILTER_PORT_OP_NEQ 2
|
||||
#define FILTER_PORT_OP_GT 3
|
||||
#define FILTER_PORT_OP_LT 4
|
||||
#define FILTER_PORT_OP_RANGE 5
|
||||
uint16_t port; // port (host byte order)
|
||||
uint16_t port2; // range
|
||||
} ip_filter_portt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int action; // permit/deny
|
||||
#define FILTER_ACTION_DENY 1
|
||||
#define FILTER_ACTION_PERMIT 2
|
||||
uint8_t proto; // protocol: IPPROTO_* (netinet/in.h)
|
||||
in_addr_t src_ip; // source ip (network byte order)
|
||||
in_addr_t src_wild;
|
||||
ip_filter_portt src_ports;
|
||||
in_addr_t dst_ip; // dest ip
|
||||
in_addr_t dst_wild;
|
||||
ip_filter_portt dst_ports;
|
||||
uint8_t frag; // apply to non-initial fragments
|
||||
uint8_t tcp_flag_op; // match type: any, all, established
|
||||
#define FILTER_FLAG_OP_ANY 1
|
||||
#define FILTER_FLAG_OP_ALL 2
|
||||
#define FILTER_FLAG_OP_EST 3
|
||||
uint8_t tcp_sflags; // flags set
|
||||
uint8_t tcp_cflags; // flags clear
|
||||
uint32_t counter; // match count
|
||||
} ip_filter_rulet;
|
||||
|
||||
#define TCP_FLAG_FIN 0x01
|
||||
#define TCP_FLAG_SYN 0x02
|
||||
#define TCP_FLAG_RST 0x04
|
||||
#define TCP_FLAG_PSH 0x08
|
||||
#define TCP_FLAG_ACK 0x10
|
||||
#define TCP_FLAG_URG 0x20
|
||||
|
||||
#define MAXFILTER 32
|
||||
#define MAXFILTER_RULES 32
|
||||
typedef struct
|
||||
{
|
||||
char name[32]; // ACL name
|
||||
int extended; // type: 0 = standard, 1 = extended
|
||||
ip_filter_rulet rules[MAXFILTER_RULES];
|
||||
int used; // session ref count
|
||||
} ip_filtert;
|
||||
|
||||
// CDN result/error codes
|
||||
#define CDN_NONE 0, 0
|
||||
#define CDN_TRY_ANOTHER 2, 7
|
||||
#define CDN_ADMIN_DISC 3, 0
|
||||
#define CDN_UNAVAILABLE 4, 0
|
||||
|
||||
// RADIUS Acct-Terminate-Cause values
|
||||
#define TERM_USER_REQUEST 1
|
||||
#define TERM_LOST_CARRIER 2
|
||||
#define TERM_LOST_SERVICE 3
|
||||
#define TERM_IDLE_TIMEOUT 4
|
||||
#define TERM_SESSION_TIMEOUT 5
|
||||
#define TERM_ADMIN_RESET 6
|
||||
#define TERM_ADMIN_REBOOT 7
|
||||
#define TERM_PORT_ERROR 8
|
||||
#define TERM_NAS_ERROR 9
|
||||
#define TERM_NAS_REQUEST 10
|
||||
#define TERM_NAS_REBOOT 11
|
||||
#define TERM_PORT_UNNEEDED 12
|
||||
#define TERM_PORT_PREEMPTED 13
|
||||
#define TERM_PORT_SUSPENDED 14
|
||||
#define TERM_SERVICE_UNAVAILABLE 15
|
||||
#define TERM_CALLBACK 16
|
||||
#define TERM_USER_ERROR 17
|
||||
#define TERM_HOST_REQUEST 18
|
||||
#define TERM_SUPPLICANT_RESTART 19
|
||||
#define TERM_REAUTHENTICATION_FAILURE 20
|
||||
#define TERM_PORT_REINIT 21
|
||||
#define TERM_PORT_DISABLED 22
|
||||
|
||||
// arp.c
|
||||
void sendarp(int ifr_idx, const unsigned char* mac, in_addr_t ip);
|
||||
|
||||
|
||||
// ppp.c
|
||||
void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void lcp_open(sessionidt s, tunnelidt t);
|
||||
void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
|
||||
void sendchap(sessionidt s, tunnelidt t);
|
||||
uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype);
|
||||
void sendlcp(sessionidt s, tunnelidt t);
|
||||
void send_ipin(sessionidt s, uint8_t *buf, int len);
|
||||
void sendccp(sessionidt s, tunnelidt t);
|
||||
void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t proto);
|
||||
|
||||
|
||||
// radius.c
|
||||
void initrad(void);
|
||||
void radiussend(uint16_t r, uint8_t state);
|
||||
void processrad(uint8_t *buf, int len, char socket_index);
|
||||
void radiusretry(uint16_t r);
|
||||
uint16_t radiusnew(sessionidt s);
|
||||
void radiusclear(uint16_t r, sessionidt s);
|
||||
void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
|
||||
|
||||
|
||||
// l2tpns.c
|
||||
clockt backoff(uint8_t try);
|
||||
void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip);
|
||||
void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add);
|
||||
sessionidt sessionbyip(in_addr_t ip);
|
||||
sessionidt sessionbyipv6(struct in6_addr ip);
|
||||
sessionidt sessionbyuser(char *username);
|
||||
void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta);
|
||||
void random_data(uint8_t *buf, int len);
|
||||
void sessionkill(sessionidt s, char *reason);
|
||||
void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause);
|
||||
void filter_session(sessionidt s, int filter_in, int filter_out);
|
||||
void send_garp(in_addr_t ip);
|
||||
void tunnelsend(uint8_t *buf, uint16_t l, tunnelidt t);
|
||||
int tun_write(uint8_t *data, int size);
|
||||
void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp);
|
||||
void sendipcp(sessionidt s, tunnelidt t);
|
||||
void sendipv6cp(sessionidt s, tunnelidt t);
|
||||
void processudp(uint8_t *buf, int len, struct sockaddr_in *addr);
|
||||
void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, uint16_t port);
|
||||
int find_filter(char const *name, size_t len);
|
||||
int ip_filter(uint8_t *buf, int len, uint8_t filter);
|
||||
int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc);
|
||||
int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc);
|
||||
int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc);
|
||||
|
||||
#undef LOG
|
||||
#undef LOG_HEX
|
||||
#define LOG(D, s, t, f, ...) ({ if (D <= config->debug) _log(D, s, t, f, ## __VA_ARGS__); })
|
||||
#define LOG_HEX(D, t, d, s) ({ if (D <= config->debug) _log_hex(D, t, d, s); })
|
||||
|
||||
void _log(int level, sessionidt s, tunnelidt t, const char *format, ...) __attribute__((format (printf, 4, 5)));
|
||||
void _log_hex(int level, const char *title, const uint8_t *data, int maxsize);
|
||||
|
||||
|
||||
int sessionsetup(sessionidt s, tunnelidt t);
|
||||
int run_plugins(int plugin_type, void *data);
|
||||
void rebuild_address_pool(void);
|
||||
void throttle_session(sessionidt s, int rate_in, int rate_out);
|
||||
int load_session(sessionidt, sessiont *);
|
||||
void become_master(void); // We're the master; kick off any required master initializations.
|
||||
|
||||
|
||||
// cli.c
|
||||
void init_cli(char *hostname);
|
||||
void cli_do_file(FILE *fh);
|
||||
void cli_do(int sockfd);
|
||||
int cli_arg_help(struct cli_def *cli, int cr_ok, char *entry, ...);
|
||||
|
||||
|
||||
// icmp.c
|
||||
void host_unreachable(in_addr_t destination, uint16_t id, in_addr_t source, uint8_t *packet, int packet_len);
|
||||
|
||||
|
||||
extern tunnelt *tunnel;
|
||||
extern sessiont *session;
|
||||
extern sessionlocalt *sess_local;
|
||||
extern ippoolt *ip_address_pool;
|
||||
#define sessionfree (session[0].next)
|
||||
|
||||
|
||||
extern configt *config;
|
||||
extern time_t basetime; // Time when this process started.
|
||||
extern time_t time_now; // Seconds since EPOCH.
|
||||
extern char main_quit;
|
||||
extern uint32_t last_id;
|
||||
extern struct Tstats *_statistics;
|
||||
extern in_addr_t my_address;
|
||||
extern int clifd;
|
||||
extern int epollfd;
|
||||
|
||||
struct event_data {
|
||||
enum {
|
||||
FD_TYPE_CLI,
|
||||
FD_TYPE_CLUSTER,
|
||||
FD_TYPE_TUN,
|
||||
FD_TYPE_UDP,
|
||||
FD_TYPE_CONTROL,
|
||||
FD_TYPE_DAE,
|
||||
FD_TYPE_RADIUS,
|
||||
FD_TYPE_BGP,
|
||||
} type;
|
||||
int index; // for RADIUS, BGP
|
||||
};
|
||||
|
||||
#define TIME (config->current_time)
|
||||
|
||||
extern uint16_t MRU;
|
||||
extern uint16_t MSS;
|
||||
|
||||
// macros for handling help in cli commands
|
||||
#define CLI_HELP_REQUESTED (argc > 0 && argv[argc-1][strlen(argv[argc-1])-1] == '?')
|
||||
#define CLI_HELP_NO_ARGS (argc > 1 || argv[0][1]) ? CLI_OK : cli_arg_help(cli, 1, NULL)
|
||||
|
||||
// CVS identifiers (for "show version file")
|
||||
extern char const *cvs_id_arp;
|
||||
extern char const *cvs_id_cli;
|
||||
extern char const *cvs_id_cluster;
|
||||
extern char const *cvs_id_constants;
|
||||
extern char const *cvs_id_control;
|
||||
extern char const *cvs_id_icmp;
|
||||
extern char const *cvs_id_l2tpns;
|
||||
extern char const *cvs_id_ll;
|
||||
extern char const *cvs_id_md5;
|
||||
extern char const *cvs_id_ppp;
|
||||
extern char const *cvs_id_radius;
|
||||
extern char const *cvs_id_tbf;
|
||||
extern char const *cvs_id_util;
|
||||
|
||||
#endif /* __L2TPNS_H__ */
|
||||
47
l2tpns.spec
Normal file
47
l2tpns.spec
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
Summary: A high-speed clustered L2TP LNS
|
||||
Name: l2tpns
|
||||
Version: 2.1.21
|
||||
Release: 1
|
||||
License: GPL
|
||||
Group: System Environment/Daemons
|
||||
Source: http://optusnet.dl.sourceforge.net/sourceforge/l2tpns/l2tpns-%{version}.tar.gz
|
||||
URL: http://sourceforge.net/projects/l2tpns
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%(%__id -un)
|
||||
Prereq: /sbin/chkconfig
|
||||
BuildRequires: libcli >= 1.8.5
|
||||
Requires: libcli >= 1.8.5
|
||||
|
||||
%description
|
||||
l2tpns is a layer 2 tunneling protocol network server (LNS). It
|
||||
supports up to 65535 concurrent sessions per server/cluster plus ISP
|
||||
features such as rate limiting, walled garden, usage accounting, and
|
||||
more.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
mkdir -p %{buildroot}
|
||||
make install DESTDIR=%{buildroot}
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc Changes INSTALL INTERNALS COPYING THANKS Docs/manual.html
|
||||
%dir /etc/l2tpns
|
||||
%config(noreplace) /etc/l2tpns/users
|
||||
%config(noreplace) /etc/l2tpns/startup-config
|
||||
%config(noreplace) /etc/l2tpns/ip_pool
|
||||
%attr(755,root,root) /usr/sbin/*
|
||||
%attr(755,root,root) /usr/lib/l2tpns
|
||||
%attr(644,root,root) /usr/share/man/man[58]/*
|
||||
|
||||
%changelog
|
||||
* Fri Dec 1 2006 Brendan O'Dea <bod@optus.net> 2.1.21-1
|
||||
- 2.1.21 release, see /usr/share/doc/l2tpns-2.1.21/Changes
|
||||
141
ll.c
Normal file
141
ll.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// L2TPNS Linked List Stuff
|
||||
|
||||
char const *cvs_id_ll = "$Id: ll.c,v 1.6 2004/11/18 08:12:55 bodea Exp $";
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "ll.h"
|
||||
|
||||
linked_list *ll_init()
|
||||
{
|
||||
return (linked_list *)calloc(sizeof(linked_list), 1);
|
||||
}
|
||||
|
||||
void ll_done(linked_list *l)
|
||||
{
|
||||
li *i = l->head, *n;
|
||||
|
||||
while (i)
|
||||
{
|
||||
n = i->next;
|
||||
free(i);
|
||||
i = n;
|
||||
}
|
||||
|
||||
free(l);
|
||||
}
|
||||
|
||||
li *ll_push(linked_list *l, void *data)
|
||||
{
|
||||
li *i;
|
||||
|
||||
if (!l) return NULL;
|
||||
if (!(i = (li *)calloc(sizeof(li), 1))) return NULL;
|
||||
|
||||
i->data = data;
|
||||
i->next = NULL;
|
||||
if (l->end)
|
||||
l->end->next = i;
|
||||
else
|
||||
l->head = i;
|
||||
l->end = i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void *ll_pop(linked_list *l)
|
||||
{
|
||||
li *i;
|
||||
void *data;
|
||||
|
||||
if (!l) return NULL;
|
||||
if (!l->head)
|
||||
return NULL;
|
||||
|
||||
data = l->head->data;
|
||||
i = l->head->next;
|
||||
free(l->head);
|
||||
l->head = i;
|
||||
return data;
|
||||
}
|
||||
|
||||
void ll_iterate(linked_list *l, int(*func)(void *))
|
||||
{
|
||||
li *i;
|
||||
if (!l || !func) return;
|
||||
|
||||
for (i = l->head; i; i = i->next)
|
||||
{
|
||||
if (i->data && !func(i->data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ll_reset(linked_list *l)
|
||||
{
|
||||
if (!l) return;
|
||||
l->current = NULL;
|
||||
}
|
||||
|
||||
void *ll_next(linked_list *l)
|
||||
{
|
||||
if (!l) return NULL;
|
||||
if (!l->current)
|
||||
l->current = l->head;
|
||||
else
|
||||
l->current = l->current->next;
|
||||
if (!l->current)
|
||||
return NULL;
|
||||
return l->current->data;
|
||||
}
|
||||
|
||||
void ll_delete(linked_list *l, void *data)
|
||||
{
|
||||
li *i = l->head, *p = NULL;
|
||||
|
||||
while (i)
|
||||
{
|
||||
if (i->data == data)
|
||||
{
|
||||
if (l->head == i) l->head = i->next;
|
||||
if (l->end == i) l->end = p;
|
||||
if (p) p->next = i->next;
|
||||
free(i);
|
||||
l->current = NULL;
|
||||
return;
|
||||
}
|
||||
p = i;
|
||||
i = i->next;
|
||||
}
|
||||
}
|
||||
|
||||
int ll_size(linked_list *l)
|
||||
{
|
||||
int count = 0;
|
||||
li *i;
|
||||
|
||||
if (!l) return 0;
|
||||
|
||||
for (i = l->head; i; i = i->next)
|
||||
if (i->data) count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int ll_contains(linked_list *l, void *search)
|
||||
{
|
||||
li *i;
|
||||
for (i = l->head; i; i = i->next)
|
||||
if (i->data == search)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
28
ll.h
Normal file
28
ll.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef __LL_H__
|
||||
#define __LL_H__
|
||||
|
||||
typedef struct s_li
|
||||
{
|
||||
void *data;
|
||||
struct s_li *next;
|
||||
} li;
|
||||
|
||||
typedef struct s_ll
|
||||
{
|
||||
li *head;
|
||||
li *end;
|
||||
li *current;
|
||||
} linked_list;
|
||||
|
||||
linked_list *ll_init();
|
||||
void ll_done(linked_list *l);
|
||||
li *ll_push(linked_list *l, void *data);
|
||||
void ll_delete(linked_list *l, void *data);
|
||||
void *ll_pop(linked_list *l);
|
||||
void ll_iterate(linked_list *l, int(*func)(void *));
|
||||
void ll_reset(linked_list *l);
|
||||
void *ll_next(linked_list *l);
|
||||
int ll_size(linked_list *l);
|
||||
int ll_contains(linked_list *l, void *search);
|
||||
|
||||
#endif /* __LL_H__ */
|
||||
274
md5.c
Normal file
274
md5.c
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security,
|
||||
* Inc. MD5 Message-Digest Algorithm.
|
||||
*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2001, and placed
|
||||
* in the public domain. There's absolutely no warranty.
|
||||
*
|
||||
* This differs from Colin Plumb's older public domain implementation in
|
||||
* that no 32-bit integer data type is required, there's no compile-time
|
||||
* endianness configuration, and the function prototypes match OpenSSL's.
|
||||
* The primary goals are portability and ease of use.
|
||||
*
|
||||
* This implementation is meant to be fast, but not as fast as possible.
|
||||
* Some known optimizations are not included to reduce source code size
|
||||
* and avoid compile-time configuration.
|
||||
*/
|
||||
|
||||
#ifndef HAVE_OPENSSL
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* F is optimized compared to its RFC 1321 definition just like in Colin
|
||||
* Plumb's implementation.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them
|
||||
* in a properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures which tolerate unaligned
|
||||
* memory accesses is just an optimization. Nothing will break if it
|
||||
* doesn't work.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__vax__)
|
||||
#define SET(n) \
|
||||
(*(MD5_u32plus *)&ptr[(n) * 4])
|
||||
#define GET(n) \
|
||||
SET(n)
|
||||
#else
|
||||
#define SET(n) \
|
||||
(ctx->block[(n)] = \
|
||||
(MD5_u32plus)ptr[(n) * 4] | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
|
||||
#define GET(n) \
|
||||
(ctx->block[(n)])
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update
|
||||
* the bit counters. There're no alignment requirements.
|
||||
*/
|
||||
static void *body(MD5_CTX *ctx, void *data, unsigned long size)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
MD5_u32plus a, b, c, d;
|
||||
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
ptr = data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
|
||||
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
|
||||
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
|
||||
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
|
||||
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
|
||||
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
|
||||
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
|
||||
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
|
||||
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
|
||||
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
|
||||
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
|
||||
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
|
||||
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
|
||||
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
|
||||
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
|
||||
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
|
||||
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
|
||||
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
|
||||
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
|
||||
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
|
||||
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
|
||||
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
|
||||
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
|
||||
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
|
||||
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
|
||||
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
|
||||
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
|
||||
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
|
||||
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
|
||||
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
|
||||
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
|
||||
STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
|
||||
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
|
||||
STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
|
||||
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
|
||||
STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
|
||||
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
|
||||
STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
|
||||
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
|
||||
STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
|
||||
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
|
||||
STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
|
||||
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
|
||||
STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
|
||||
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
|
||||
STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
|
||||
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
|
||||
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
|
||||
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
|
||||
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
|
||||
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
|
||||
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
|
||||
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
|
||||
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
|
||||
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
|
||||
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
|
||||
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
|
||||
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
|
||||
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
|
||||
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
|
||||
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void MD5_Init(MD5_CTX *ctx)
|
||||
{
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
}
|
||||
|
||||
void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
|
||||
{
|
||||
MD5_u32plus saved_lo;
|
||||
unsigned long used, free;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
|
||||
ctx->hi++;
|
||||
ctx->hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
free = 64 - used;
|
||||
|
||||
if (size < free) {
|
||||
memcpy(&ctx->buffer[used], data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ctx->buffer[used], data, free);
|
||||
data = (unsigned char *)data + free;
|
||||
size -= free;
|
||||
body(ctx, ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(ctx, data, size & ~(unsigned long)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
memcpy(ctx->buffer, data, size);
|
||||
}
|
||||
|
||||
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
|
||||
{
|
||||
unsigned long used, free;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
free = 64 - used;
|
||||
|
||||
if (free < 8) {
|
||||
memset(&ctx->buffer[used], 0, free);
|
||||
body(ctx, ctx->buffer, 64);
|
||||
used = 0;
|
||||
free = 64;
|
||||
}
|
||||
|
||||
memset(&ctx->buffer[used], 0, free - 8);
|
||||
|
||||
ctx->lo <<= 3;
|
||||
ctx->buffer[56] = ctx->lo;
|
||||
ctx->buffer[57] = ctx->lo >> 8;
|
||||
ctx->buffer[58] = ctx->lo >> 16;
|
||||
ctx->buffer[59] = ctx->lo >> 24;
|
||||
ctx->buffer[60] = ctx->hi;
|
||||
ctx->buffer[61] = ctx->hi >> 8;
|
||||
ctx->buffer[62] = ctx->hi >> 16;
|
||||
ctx->buffer[63] = ctx->hi >> 24;
|
||||
|
||||
body(ctx, ctx->buffer, 64);
|
||||
|
||||
result[0] = ctx->a;
|
||||
result[1] = ctx->a >> 8;
|
||||
result[2] = ctx->a >> 16;
|
||||
result[3] = ctx->a >> 24;
|
||||
result[4] = ctx->b;
|
||||
result[5] = ctx->b >> 8;
|
||||
result[6] = ctx->b >> 16;
|
||||
result[7] = ctx->b >> 24;
|
||||
result[8] = ctx->c;
|
||||
result[9] = ctx->c >> 8;
|
||||
result[10] = ctx->c >> 16;
|
||||
result[11] = ctx->c >> 24;
|
||||
result[12] = ctx->d;
|
||||
result[13] = ctx->d >> 8;
|
||||
result[14] = ctx->d >> 16;
|
||||
result[15] = ctx->d >> 24;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
|
||||
#endif
|
||||
28
md5.h
Normal file
28
md5.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security,
|
||||
* Inc. MD5 Message-Digest Algorithm.
|
||||
*
|
||||
* Written by Solar Designer <solar at openwall.com> in 2001, and placed
|
||||
* in the public domain. See md5.c for more information.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <openssl/md5.h>
|
||||
#elif !defined(_MD5_H)
|
||||
#define _MD5_H
|
||||
|
||||
/* Any 32-bit or wider unsigned integer data type will do */
|
||||
typedef unsigned long MD5_u32plus;
|
||||
|
||||
typedef struct {
|
||||
MD5_u32plus lo, hi;
|
||||
MD5_u32plus a, b, c, d;
|
||||
unsigned char buffer[64];
|
||||
MD5_u32plus block[16];
|
||||
} MD5_CTX;
|
||||
|
||||
extern void MD5_Init(MD5_CTX *ctx);
|
||||
extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
|
||||
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
|
||||
|
||||
#endif
|
||||
238
nsctl.c
Normal file
238
nsctl.c
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/* l2tpns plugin control */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "l2tpns.h"
|
||||
#include "control.h"
|
||||
|
||||
struct {
|
||||
char *command;
|
||||
char *usage;
|
||||
int action;
|
||||
} builtins[] = {
|
||||
{ "load_plugin", " PLUGIN Load named plugin", NSCTL_REQ_LOAD },
|
||||
{ "unload_plugin", " PLUGIN Unload named plugin", NSCTL_REQ_UNLOAD },
|
||||
{ "help", " List available commands", NSCTL_REQ_HELP },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int debug = 0;
|
||||
static int timeout = 2; // 2 seconds
|
||||
static char *me;
|
||||
|
||||
#define USAGE() fprintf(stderr, "Usage: %s [-d] [-h HOST[:PORT]] [-t TIMEOUT] COMMAND [ARG ...]\n", me)
|
||||
|
||||
static struct nsctl *request(char *host, int port, int type, int argc, char *argv[]);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int req_type = 0;
|
||||
char *host = 0;
|
||||
int port;
|
||||
int i;
|
||||
char *p;
|
||||
struct nsctl *res;
|
||||
|
||||
if ((p = strrchr((me = argv[0]), '/')))
|
||||
me = p + 1;
|
||||
|
||||
opterr = 0;
|
||||
while ((i = getopt(argc, argv, "dh:t:")) != -1)
|
||||
switch (i)
|
||||
{
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
host = optarg;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
timeout = atoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
USAGE();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 1 || !argv[0][0])
|
||||
{
|
||||
USAGE();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!host)
|
||||
host = "127.0.0.1";
|
||||
|
||||
if ((p = strchr(host, ':')))
|
||||
{
|
||||
port = atoi(p + 1);
|
||||
if (!port)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid port `%s'\n", me, p + 1);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = NSCTL_PORT;
|
||||
}
|
||||
|
||||
for (i = 0; !req_type && builtins[i].command; i++)
|
||||
if (!strcmp(argv[0], builtins[i].command))
|
||||
req_type = builtins[i].action;
|
||||
|
||||
if (req_type == NSCTL_REQ_HELP)
|
||||
{
|
||||
printf("Available commands:\n");
|
||||
for (i = 0; builtins[i].command; i++)
|
||||
printf(" %s%s\n", builtins[i].command, builtins[i].usage);
|
||||
}
|
||||
|
||||
if (req_type)
|
||||
{
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
else
|
||||
{
|
||||
req_type = NSCTL_REQ_CONTROL;
|
||||
}
|
||||
|
||||
if ((res = request(host, port, req_type, argc, argv)))
|
||||
{
|
||||
FILE *stream = stderr;
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
if (res->type == NSCTL_RES_OK)
|
||||
{
|
||||
stream = stdout;
|
||||
status = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
for (i = 0; i < res->argc; i++)
|
||||
fprintf(stream, "%s\n", res->argv[i]);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static void sigalrm_handler(int sig) { }
|
||||
|
||||
static struct nsctl *request(char *host, int port, int type, int argc, char *argv[])
|
||||
{
|
||||
static struct nsctl res;
|
||||
struct sockaddr_in peer;
|
||||
socklen_t len = sizeof(peer);
|
||||
struct hostent *h = gethostbyname(host);
|
||||
int fd;
|
||||
uint8_t buf[NSCTL_MAX_PKT_SZ];
|
||||
int sz;
|
||||
char *err;
|
||||
|
||||
if (!h || h->h_addrtype != AF_INET)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid host `%s'\n", me, host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: can't create udp socket (%s)\n", me, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&peer, 0, len);
|
||||
peer.sin_family = AF_INET;
|
||||
peer.sin_port = htons(port);
|
||||
memcpy(&peer.sin_addr.s_addr, h->h_addr, sizeof(peer.sin_addr.s_addr));
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &peer, sizeof(peer)) < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: udp connect failed (%s)\n", me, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((sz = pack_control(buf, sizeof(buf), type, argc, argv)) < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: error packing request\n", me);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
{
|
||||
struct nsctl req;
|
||||
if (unpack_control(&req, buf, sz) == type)
|
||||
{
|
||||
fprintf(stderr, "Sending ");
|
||||
dump_control(&req, stderr);
|
||||
}
|
||||
}
|
||||
|
||||
if (send(fd, buf, sz, 0) < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: error sending request (%s)\n", me, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set timer */
|
||||
if (timeout)
|
||||
{
|
||||
struct sigaction alrm;
|
||||
alrm.sa_handler = sigalrm_handler;
|
||||
sigemptyset(&alrm.sa_mask);
|
||||
alrm.sa_flags = 0;
|
||||
|
||||
sigaction(SIGALRM, &alrm, 0);
|
||||
alarm(timeout);
|
||||
}
|
||||
|
||||
sz = recv(fd, buf, sizeof(buf), 0);
|
||||
alarm(0);
|
||||
|
||||
if (sz < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: error receiving response (%s)\n", me,
|
||||
errno == EINTR ? "timed out" : strerror(errno));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((type = unpack_control(&res, buf, sz)) > 0 && type & NSCTL_RESPONSE)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
fprintf(stderr, "Received ");
|
||||
dump_control(&res, stderr);
|
||||
}
|
||||
|
||||
return &res;
|
||||
}
|
||||
|
||||
err = "unknown error";
|
||||
switch (type)
|
||||
{
|
||||
case NSCTL_ERR_SHORT: err = "short packet"; break;
|
||||
case NSCTL_ERR_LONG: err = "extra data"; break;
|
||||
case NSCTL_ERR_MAGIC: err = "bad magic"; break;
|
||||
case NSCTL_ERR_TYPE: err = "invalid type"; break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: %s\n", me, err);
|
||||
return 0;
|
||||
}
|
||||
129
plugin.h
Normal file
129
plugin.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#ifndef __PLUGIN_H__
|
||||
#define __PLUGIN_H__
|
||||
|
||||
#define PLUGIN_API_VERSION 7
|
||||
#define MAX_PLUGIN_TYPES 30
|
||||
|
||||
enum
|
||||
{
|
||||
PLUGIN_PRE_AUTH = 1,
|
||||
PLUGIN_POST_AUTH,
|
||||
PLUGIN_PACKET_RX,
|
||||
PLUGIN_PACKET_TX,
|
||||
PLUGIN_TIMER,
|
||||
PLUGIN_NEW_SESSION,
|
||||
PLUGIN_KILL_SESSION,
|
||||
PLUGIN_CONTROL,
|
||||
PLUGIN_RADIUS_RESPONSE,
|
||||
PLUGIN_RADIUS_RESET,
|
||||
PLUGIN_RADIUS_ACCOUNT,
|
||||
PLUGIN_BECOME_MASTER,
|
||||
PLUGIN_NEW_SESSION_MASTER,
|
||||
};
|
||||
|
||||
#define PLUGIN_RET_ERROR 0
|
||||
#define PLUGIN_RET_OK 1
|
||||
#define PLUGIN_RET_STOP 2
|
||||
#define PLUGIN_RET_NOTMASTER 3
|
||||
|
||||
struct pluginfuncs
|
||||
{
|
||||
void (*log)(int level, sessionidt s, tunnelidt t, const char *format, ...);
|
||||
void (*log_hex)(int level, const char *title, const uint8_t *data, int maxsize);
|
||||
char *(*fmtaddr)(in_addr_t addr, int n);
|
||||
sessionidt (*get_session_by_username)(char *username);
|
||||
sessiont *(*get_session_by_id)(sessionidt s);
|
||||
sessionidt (*get_id_by_session)(sessiont *s);
|
||||
uint16_t (*radiusnew)(sessionidt s);
|
||||
void (*radiussend)(uint16_t r, uint8_t state);
|
||||
void *(*getconfig)(char *key, enum config_typet type);
|
||||
void (*sessionshutdown)(sessionidt s, char const *reason, int result, int error, int term_cause);
|
||||
void (*sessionkill)(sessionidt s, char *reason);
|
||||
void (*throttle)(sessionidt s, int rate_in, int rate_out);
|
||||
int (*session_changed)(int sid);
|
||||
};
|
||||
|
||||
struct param_pre_auth
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
char *username;
|
||||
char *password;
|
||||
int protocol;
|
||||
int continue_auth;
|
||||
};
|
||||
|
||||
struct param_post_auth
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
char *username;
|
||||
short auth_allowed;
|
||||
int protocol;
|
||||
};
|
||||
|
||||
struct param_packet_rx
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
char *buf;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct param_packet_tx
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
char *buf;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct param_timer
|
||||
{
|
||||
time_t time_now;
|
||||
};
|
||||
|
||||
struct param_control
|
||||
{
|
||||
int iam_master;
|
||||
int argc;
|
||||
char **argv;
|
||||
// output
|
||||
int response;
|
||||
char *additional;
|
||||
};
|
||||
|
||||
struct param_new_session
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
};
|
||||
|
||||
struct param_kill_session
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
};
|
||||
|
||||
struct param_radius_response
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
char *key;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct param_radius_reset
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
};
|
||||
|
||||
struct param_radius_account
|
||||
{
|
||||
tunnelt *t;
|
||||
sessiont *s;
|
||||
uint8_t **packet;
|
||||
};
|
||||
|
||||
#endif /* __PLUGIN_H__ */
|
||||
68
scripts/l2tpns-capture
Normal file
68
scripts/l2tpns-capture
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#! /usr/bin/perl -w
|
||||
|
||||
#
|
||||
# Accept intercept data from l2tpns, write to a file in pcap format
|
||||
# (http://wiki.ethereal.com/Development/LibpcapFileFormat) suffixed
|
||||
# with timestamp. Killing the process with SIGHUP causes a new file
|
||||
# to be opened.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use IO::File;
|
||||
use IO::Socket;
|
||||
use Time::HiRes 'gettimeofday';
|
||||
|
||||
(my $cmd = $0) =~ s!.*/!!;
|
||||
|
||||
die "Usage: $cmd PREFIX PORT\n" unless @ARGV == 2 and $ARGV[1] =~ /^\d+$/;
|
||||
|
||||
my ($prefix, $port) = @ARGV;
|
||||
my $sock = IO::Socket::INET->new(
|
||||
LocalPort => $port,
|
||||
Proto => 'udp',
|
||||
Type => SOCK_DGRAM,
|
||||
) or die "$cmd: can't bind to port $port ($!)\n";
|
||||
|
||||
my $restart = 0;
|
||||
$SIG{HUP} = sub { $restart++ };
|
||||
|
||||
my $header = pack LSSlLLL =>
|
||||
0xa1b2c3d4, # magic no
|
||||
2, # version maj
|
||||
4, # version min
|
||||
0, # timezone offset (GMT)
|
||||
0, # timestamp accuracy
|
||||
65536, # snaplen
|
||||
12; # link type (RAW_IP)
|
||||
|
||||
my $cap;
|
||||
my $buf;
|
||||
my $file;
|
||||
for (;;)
|
||||
{
|
||||
unless ($cap)
|
||||
{
|
||||
$file = $prefix . time;
|
||||
$cap = IO::File->new("> $file")
|
||||
or die "$0: can't create capture file $file ($!)\n";
|
||||
|
||||
$cap->print($header)
|
||||
or die "$0: error writing to $file ($!)\n";
|
||||
}
|
||||
|
||||
while ($sock->recv($buf, 1600))
|
||||
{
|
||||
$cap->print(
|
||||
# packet header: sec, usec, included size, original size
|
||||
(pack LLLL => (gettimeofday), (length $buf) x 2),
|
||||
$buf
|
||||
) or die "$0: error writing to $file ($!)\n";
|
||||
}
|
||||
|
||||
if ($restart)
|
||||
{
|
||||
$restart = 0;
|
||||
$cap->close;
|
||||
undef $cap;
|
||||
}
|
||||
}
|
||||
28
scripts/l2tpns-monitor
Normal file
28
scripts/l2tpns-monitor
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
stopfile=/tmp/l2tpns.stop
|
||||
first=`date +%s`
|
||||
min_first_time=3
|
||||
restart_delay=5
|
||||
prog=${0##*/}
|
||||
|
||||
while :
|
||||
do
|
||||
echo "`date`: Starting l2tpns $@"
|
||||
start=`date +%s`
|
||||
/usr/sbin/l2tpns ${1+"$@"}
|
||||
RETVAL=$?
|
||||
stop=`date +%s`
|
||||
t=$(($stop - $start));
|
||||
first=$(($stop - $first));
|
||||
echo "`date`: l2tpns exited after $t seconds, status $RETVAL"
|
||||
if [ $first -lt $min_first_time ]; then
|
||||
echo "`date`: l2tpns exited immediately, $prog exiting"
|
||||
exit $RETVAL
|
||||
fi
|
||||
if [ -f $stopfile ]; then
|
||||
ls -l $stopfile
|
||||
echo "`date`: stop file found, $prog exiting"
|
||||
exit
|
||||
fi
|
||||
sleep $restart_delay
|
||||
done >>/var/log/$prog 2>&1 & # execute in background
|
||||
93
scripts/l2tpns.script
Normal file
93
scripts/l2tpns.script
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Startup script for l2tpns
|
||||
#
|
||||
# chkconfig: 2345 83 25
|
||||
# description: l2tpns.
|
||||
# processname: l2tpns
|
||||
# pidfile: /var/run/l2tpns.pid
|
||||
# config: /etc/l2tpns
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
if [ -f /etc/sysconfig/lt2pns ]; then
|
||||
. /etc/sysconfig/lt2pns
|
||||
fi
|
||||
|
||||
# Path to the l2tpns-monitor script, server binary, and short-form for messages.
|
||||
l2tpns_monitor=/usr/sbin/l2tpns-monitor
|
||||
l2tpns=/usr/sbin/l2tpns
|
||||
prog=${l2tpns##*/}
|
||||
RETVAL=0
|
||||
|
||||
start() {
|
||||
echo -n $"Starting $prog: "
|
||||
rm -f /tmp/l2tpns.stop
|
||||
daemon --check=$prog $l2tpns_monitor $OPTIONS
|
||||
RETVAL=$?
|
||||
echo
|
||||
sleep 5
|
||||
pid=`pidofproc $l2tpns_monitor`
|
||||
if [ -z "$pid" ] || [ "$pid" -eq 0 ]; then
|
||||
echo -n "Error starting $prog"
|
||||
echo_failure
|
||||
echo
|
||||
return 99
|
||||
fi
|
||||
[ $RETVAL = 0 ] && touch /var/lock/subsys/l2tpns
|
||||
return $RETVAL
|
||||
}
|
||||
stop() {
|
||||
echo -n $"Stopping $prog: "
|
||||
echo >/tmp/l2tpns.stop
|
||||
killproc $l2tpns
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL = 0 ] && rm -f /var/lock/subsys/l2tpns /var/run/l2tpns.pid
|
||||
}
|
||||
reload() {
|
||||
echo -n $"Reloading $prog: "
|
||||
killproc $l2tpns -HUP
|
||||
RETVAL=$?
|
||||
echo
|
||||
}
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status $l2tpns
|
||||
RETVAL=$?
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
sleep 5
|
||||
start
|
||||
;;
|
||||
condrestart)
|
||||
if [ -f /var/run/l2tpns.pid ] ; then
|
||||
stop
|
||||
start
|
||||
fi
|
||||
;;
|
||||
reload)
|
||||
reload
|
||||
;;
|
||||
coldrestart)
|
||||
stop
|
||||
sleep 10
|
||||
rm -f /tmp/l2tpns.dump
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $prog {start|stop|restart|condrestart|reload|status|coldrestart}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $RETVAL
|
||||
74
sessionctl.c
Normal file
74
sessionctl.c
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
#include "control.h"
|
||||
|
||||
/* session control */
|
||||
|
||||
char const *cvs_id = "$Id: sessionctl.c,v 1.5 2006/04/13 11:14:35 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
char *plugin_control_help[] = {
|
||||
" drop USER|SID [REASON] Shutdown user session",
|
||||
" kill USER|SID [REASON] Kill user session",
|
||||
0
|
||||
};
|
||||
|
||||
int plugin_control(struct param_control *data)
|
||||
{
|
||||
sessionidt session;
|
||||
sessiont *s = 0;
|
||||
char *end;
|
||||
char *reason;
|
||||
|
||||
if (data->argc < 1)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
if (strcmp(data->argv[0], "drop") && strcmp(data->argv[0], "kill"))
|
||||
return PLUGIN_RET_OK; // not for us
|
||||
|
||||
if (!data->iam_master)
|
||||
return PLUGIN_RET_NOTMASTER;
|
||||
|
||||
if (data->argc < 2 || data->argc > 3)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "requires username or session id and optional reason";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (!(session = strtol(data->argv[1], &end, 10)) || *end)
|
||||
session = f->get_session_by_username(data->argv[1]);
|
||||
|
||||
if (session)
|
||||
s = f->get_session_by_id(session);
|
||||
|
||||
if (!s || !s->ip)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "session not found";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (data->argc > 2)
|
||||
reason = data->argv[2];
|
||||
else
|
||||
reason = "Requested by administrator.";
|
||||
|
||||
if (data->argv[0][0] == 'd')
|
||||
f->sessionshutdown(session, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);
|
||||
else
|
||||
f->sessionkill(session, reason);
|
||||
|
||||
data->response = NSCTL_RES_OK;
|
||||
data->additional = 0;
|
||||
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
41
setrxspeed.c
Normal file
41
setrxspeed.c
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/* fudge up session rx speed if not set */
|
||||
|
||||
char const *cvs_id = "$Id: setrxspeed.c,v 1.4 2005/10/11 09:04:53 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
int plugin_post_auth(struct param_post_auth *data)
|
||||
{
|
||||
if (!data->auth_allowed)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
if (data->s->rx_connect_speed)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
switch (data->s->tx_connect_speed)
|
||||
{
|
||||
case 256:
|
||||
data->s->rx_connect_speed = 64;
|
||||
break;
|
||||
|
||||
case 512:
|
||||
data->s->rx_connect_speed = 128;
|
||||
break;
|
||||
|
||||
case 1500:
|
||||
data->s->rx_connect_speed = 256;
|
||||
break;
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
122
snoopctl.c
Normal file
122
snoopctl.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
#include "control.h"
|
||||
|
||||
/* snoop control */
|
||||
|
||||
char const *cvs_id = "$Id: snoopctl.c,v 1.7 2005/10/11 09:04:53 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
char *plugin_control_help[] = {
|
||||
" snoop USER|SID IP PORT Intercept user traffic",
|
||||
" unsnoop USER|SID Stop intercepting user",
|
||||
0
|
||||
};
|
||||
|
||||
int plugin_control(struct param_control *data)
|
||||
{
|
||||
sessionidt session;
|
||||
sessiont *s = 0;
|
||||
int flag;
|
||||
char *end;
|
||||
|
||||
if (data->argc < 1)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
if (strcmp(data->argv[0], "snoop") && strcmp(data->argv[0], "unsnoop"))
|
||||
return PLUGIN_RET_OK; // not for us
|
||||
|
||||
if (!data->iam_master)
|
||||
return PLUGIN_RET_NOTMASTER;
|
||||
|
||||
flag = data->argv[0][0] != 'u';
|
||||
|
||||
if (flag)
|
||||
{
|
||||
if (data->argc != 4)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "requires username or session id and host, port";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->argc != 2)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "requires username or session id";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(session = strtol(data->argv[1], &end, 10)) || *end)
|
||||
session = f->get_session_by_username(data->argv[1]);
|
||||
|
||||
if (session)
|
||||
s = f->get_session_by_id(session);
|
||||
|
||||
if (!s || !s->ip)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "session not found";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
in_addr_t ip = inet_addr(data->argv[2]);
|
||||
uint16_t port = atoi(data->argv[3]);
|
||||
|
||||
if (!ip || ip == INADDR_NONE)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "invalid ip address";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (!port)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "invalid port";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (ip == s->snoop_ip && port == s->snoop_port)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "already intercepted";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
s->snoop_ip = ip;
|
||||
s->snoop_port = port;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!s->snoop_ip)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "not intercepted";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
s->snoop_ip = 0;
|
||||
s->snoop_port = 0;
|
||||
}
|
||||
|
||||
f->session_changed(session);
|
||||
|
||||
data->response = NSCTL_RES_OK;
|
||||
data->additional = 0;
|
||||
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
31
stripdomain.c
Normal file
31
stripdomain.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/* strip domain part of username before sending RADIUS requests */
|
||||
|
||||
char const *cvs_id = "$Id: stripdomain.c,v 1.8 2005/10/11 09:04:53 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
int plugin_pre_auth(struct param_pre_auth *data)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (!data->continue_auth) return PLUGIN_RET_STOP;
|
||||
|
||||
// Strip off @domain
|
||||
if ((p = strchr(data->username, '@')))
|
||||
{
|
||||
f->log(3, 0, 0, "Stripping off trailing domain name \"%s\"\n", p);
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
return PLUGIN_RET_OK;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
354
tbf.c
Normal file
354
tbf.c
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
// L2TPNS: token bucket filters
|
||||
|
||||
char const *cvs_id_tbf = "$Id: tbf.c,v 1.13 2005/07/31 10:04:10 bodea Exp $";
|
||||
|
||||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "util.h"
|
||||
#include "tbf.h"
|
||||
|
||||
tbft *filter_list = NULL;
|
||||
static int filter_list_size = 0;
|
||||
|
||||
static int timer_chain = -1; // Head of timer chain.
|
||||
|
||||
static void tbf_run_queue(int tbf_id);
|
||||
|
||||
void init_tbf(int num_tbfs)
|
||||
{
|
||||
if (!(filter_list = shared_malloc(sizeof(*filter_list) * num_tbfs)))
|
||||
return;
|
||||
|
||||
filter_list_size = num_tbfs;
|
||||
filter_list[0].sid = -1; // Reserved.
|
||||
}
|
||||
//
|
||||
// Put a TBF on the timer list.
|
||||
// This is a doubly linked list..
|
||||
// We put ourselves on the tail of the list.
|
||||
//
|
||||
static void add_to_timer(int id)
|
||||
{
|
||||
if (!filter_list)
|
||||
return;
|
||||
|
||||
if (timer_chain == -1) {
|
||||
filter_list[id].next = filter_list[id].prev = id;
|
||||
timer_chain = id;
|
||||
return;
|
||||
}
|
||||
|
||||
filter_list[id].next = timer_chain;
|
||||
filter_list[id].prev = filter_list[timer_chain].prev;
|
||||
filter_list[filter_list[timer_chain].prev].next = id;
|
||||
filter_list[timer_chain].prev = id;
|
||||
}
|
||||
|
||||
//
|
||||
// Remove a TBF from the timer list.
|
||||
// This is a doubly linked list.
|
||||
static void del_from_timer(int id)
|
||||
{
|
||||
if (!filter_list)
|
||||
return;
|
||||
|
||||
if (filter_list[id].next == id) { // Last element in chain?
|
||||
if (timer_chain != id) { // WTF?
|
||||
LOG(0, 0, 0, "Removed a singleton element from TBF, but tc didn't point to it!\n");
|
||||
} else
|
||||
timer_chain = -1;
|
||||
filter_list[id].next = filter_list[id].prev = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
filter_list[filter_list[id].next].prev = filter_list[id].prev;
|
||||
filter_list[filter_list[id].prev].next = filter_list[id].next;
|
||||
if (timer_chain == id)
|
||||
timer_chain = filter_list[id].next;
|
||||
|
||||
filter_list[id].next = filter_list[id].prev = 0; // Mark as off the timer chain.
|
||||
}
|
||||
|
||||
//
|
||||
// Free a token bucket filter structure for re-use.
|
||||
//
|
||||
|
||||
int free_tbf(int tid)
|
||||
{
|
||||
if (tid < 1) // Make sure we don't free id # 0
|
||||
return -1;
|
||||
|
||||
if (!filter_list) // WTF?
|
||||
return -1;
|
||||
|
||||
if (filter_list[tid].next)
|
||||
del_from_timer(tid);
|
||||
filter_list[tid].sid = 0;
|
||||
|
||||
return 0; // Done!
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate a new token bucket filter.
|
||||
//
|
||||
int new_tbf(int sid, int max_credit, int rate, void (*f)(sessionidt, uint8_t *, int))
|
||||
{
|
||||
int i;
|
||||
static int p = 0;
|
||||
|
||||
LOG(4, 0, 0, "Allocating new TBF (sess %d, rate %d, helper %p)\n", sid, rate, f);
|
||||
|
||||
if (!filter_list)
|
||||
return 0; // Couldn't alloc memory!
|
||||
|
||||
for (i = 0 ; i < filter_list_size ; ++i, p = (p+1)%filter_list_size ) {
|
||||
if (filter_list[p].sid)
|
||||
continue;
|
||||
|
||||
memset((void*) &filter_list[p], 0, sizeof(filter_list[p]) ); // Clear counters and data.
|
||||
filter_list[p].sid = sid;
|
||||
filter_list[p].credit = max_credit;
|
||||
filter_list[p].queued = 0;
|
||||
filter_list[p].max_credit = max_credit;
|
||||
filter_list[p].rate = rate;
|
||||
filter_list[p].oldest = 0;
|
||||
filter_list[p].send = f;
|
||||
return p;
|
||||
}
|
||||
|
||||
LOG(0, 0, 0, "Ran out of token bucket filters! Sess %d will be un-throttled\n", sid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Sanity check all the TBF records. This is
|
||||
// typically done when we become a master..
|
||||
//
|
||||
void fsck_tbfs(void)
|
||||
{
|
||||
int i , sid;
|
||||
|
||||
if (!filter_list)
|
||||
return;
|
||||
|
||||
for (i = 1; i < filter_list_size; ++i) {
|
||||
if (!filter_list[i].sid) // Is it used??
|
||||
continue;
|
||||
|
||||
sid = filter_list[i].sid;
|
||||
if (i != session[sid].tbf_in &&
|
||||
i != session[sid].tbf_out) { // Ooops.
|
||||
|
||||
free_tbf(i); // Mark it as free...
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < config->cluster_highest_sessionid ; ++i) {
|
||||
if (session[i].tbf_in && filter_list[session[i].tbf_in].sid != i) {
|
||||
filter_list[session[i].tbf_in].sid = i; // Ouch!? FIXME. What to do here?
|
||||
}
|
||||
if (session[i].tbf_out && filter_list[session[i].tbf_out].sid != i) {
|
||||
filter_list[session[i].tbf_out].sid = i; // Ouch!? FIXME. What to do here?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Run a packet through a token bucket filter.
|
||||
// If we can send it right away, we do. Else we
|
||||
// try and queue it to send later. Else we drop it.
|
||||
//
|
||||
int tbf_queue_packet(int tbf_id, uint8_t *data, int size)
|
||||
{
|
||||
int i;
|
||||
tbft *f;
|
||||
|
||||
if (!filter_list)
|
||||
return -1;
|
||||
|
||||
if (tbf_id > filter_list_size || tbf_id < 1) { // Out of range ID??
|
||||
// Very bad. Just drop it.
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = &filter_list[tbf_id];
|
||||
|
||||
if (!f->sid) // Is this a real structure??
|
||||
return -1;
|
||||
|
||||
tbf_run_queue(tbf_id); // Caculate credit and send any queued packets if possible..
|
||||
|
||||
f->b_queued += size;
|
||||
f->p_queued ++;
|
||||
|
||||
if (!f->queued && f->credit > size) { // If the queue is empty, and we have
|
||||
// enough credit, just send it now.
|
||||
f->credit -= size;
|
||||
if (f->send) {
|
||||
f->send(f->sid, data, size);
|
||||
f->b_sent += size;
|
||||
f->p_sent ++;
|
||||
} else {
|
||||
f->b_dropped += size;
|
||||
f->p_dropped ++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// Not enough credit. Can we have room in the queue?
|
||||
if (f->queued >= TBF_MAX_QUEUE) {
|
||||
f->p_dropped ++;
|
||||
f->b_dropped += size;
|
||||
return -1; // No, just drop it.
|
||||
}
|
||||
|
||||
// Is it too big to fit into a queue slot?
|
||||
if (size >= TBF_MAX_SIZE) {
|
||||
f->p_dropped ++;
|
||||
f->b_dropped += size;
|
||||
return -1; // Yes, just drop it.
|
||||
}
|
||||
|
||||
// Ok. We have a slot, and it's big enough to
|
||||
// contain the packet, so queue the packet!
|
||||
i = ( f->oldest + f->queued ) % TBF_MAX_QUEUE;
|
||||
memcpy(f->packets[i], data, size);
|
||||
|
||||
f->sizes[i] = size;
|
||||
f->queued ++;
|
||||
f->p_delayed ++;
|
||||
|
||||
if (!f->next) // Are we off the timer chain?
|
||||
add_to_timer(tbf_id); // Put ourselves on the timer chain.
|
||||
|
||||
return 0; // All done.
|
||||
}
|
||||
|
||||
//
|
||||
// Send queued packets from the filter if possible.
|
||||
// (We're normally only called if this is possible.. )
|
||||
static void tbf_run_queue(int tbf_id)
|
||||
{
|
||||
tbft * f;
|
||||
|
||||
if (!filter_list)
|
||||
return;
|
||||
|
||||
f = &filter_list[tbf_id];
|
||||
|
||||
// Calculate available credit...
|
||||
f->credit += (TIME - f->lasttime) * f->rate / 10; // current time is 1/10th of a second.
|
||||
if (f->credit > f->max_credit)
|
||||
f->credit = f->max_credit;
|
||||
f->lasttime = TIME;
|
||||
|
||||
while (f->queued > 0 && f->credit >= f->sizes[f->oldest]) { // While we have enough credit..
|
||||
|
||||
if (f->send) {
|
||||
f->send(f->sid, f->packets[f->oldest], f->sizes[f->oldest]);
|
||||
f->b_sent += f->sizes[f->oldest];
|
||||
f->p_sent ++;
|
||||
} else {
|
||||
f->b_dropped += f->sizes[f->oldest];
|
||||
f->p_dropped ++;
|
||||
}
|
||||
|
||||
f->credit -= f->sizes[f->oldest];
|
||||
|
||||
f->oldest = (f->oldest + 1 ) % TBF_MAX_QUEUE;
|
||||
f->queued--; // One less queued packet..
|
||||
}
|
||||
|
||||
if (f->queued) // Still more to do. Hang around on the timer list.
|
||||
return;
|
||||
|
||||
if (f->next) // Are we on the timer list??
|
||||
del_from_timer(tbf_id); // Nothing more to do. Get off the timer list.
|
||||
}
|
||||
|
||||
//
|
||||
// Periodically walk the timer list..
|
||||
//
|
||||
int tbf_run_timer(void)
|
||||
{
|
||||
int i = timer_chain;
|
||||
int count = filter_list_size + 1; // Safety check.
|
||||
int last = -1;
|
||||
int tbf_id; // structure being processed.
|
||||
|
||||
if (timer_chain < 0)
|
||||
return 0; // Nothing to do...
|
||||
|
||||
if (!filter_list) // No structures built yet.
|
||||
return 0;
|
||||
|
||||
last = filter_list[i].prev; // last element to process.
|
||||
|
||||
do {
|
||||
tbf_id = i;
|
||||
i = filter_list[i].next; // Get the next in the queue.
|
||||
|
||||
tbf_run_queue(tbf_id); // Run the timer queue..
|
||||
} while ( timer_chain > 0 && i && tbf_id != last && --count > 0);
|
||||
|
||||
|
||||
#if 0 // Debugging.
|
||||
for (i = 0; i < filter_list_size; ++i) {
|
||||
if (!filter_list[i].next)
|
||||
continue;
|
||||
if (filter_list[i].lasttime == TIME) // Did we just run it?
|
||||
continue;
|
||||
|
||||
LOG(1, 0, 0, "Missed tbf %d! Not on the timer chain?(n %d, p %d, tc %d)\n", i,
|
||||
filter_list[i].next, filter_list[i].prev, timer_chain);
|
||||
tbf_run_queue(i);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
if (CLI_HELP_REQUESTED)
|
||||
return CLI_HELP_NO_ARGS;
|
||||
|
||||
if (!config->cluster_iam_master) {
|
||||
cli_error(cli, "Can't do this on a slave. Do it on %s",
|
||||
fmtaddr(config->cluster_master_address, 0));
|
||||
|
||||
return CLI_OK;
|
||||
}
|
||||
|
||||
if (!filter_list)
|
||||
return CLI_OK;
|
||||
|
||||
cli_print(cli,"%6s %5s %5s %6s %6s | %7s %7s %8s %8s %8s %8s", "TBF#", "Sid", "Rate", "Credit", "Queued",
|
||||
"ByteIn","PackIn","ByteSent","PackSent", "PackDrop", "PackDelay");
|
||||
|
||||
for (i = 1; i < filter_list_size; ++i) {
|
||||
if (!filter_list[i].sid) // Is it used?
|
||||
continue; // No.
|
||||
|
||||
cli_print(cli, "%5d%1s %5d %5d %6d %6d | %7d %7d %8d %8d %8d %8d",
|
||||
i, (filter_list[i].next ? "*" : " "),
|
||||
filter_list[i].sid,
|
||||
filter_list[i].rate * 8,
|
||||
filter_list[i].credit,
|
||||
filter_list[i].queued,
|
||||
|
||||
filter_list[i].b_queued,
|
||||
filter_list[i].p_queued,
|
||||
filter_list[i].b_sent,
|
||||
filter_list[i].p_sent,
|
||||
filter_list[i].p_dropped,
|
||||
filter_list[i].p_delayed);
|
||||
++count;
|
||||
}
|
||||
cli_print(cli, "%d tbf entries used, %d total", count, filter_list_size);
|
||||
return CLI_OK;
|
||||
}
|
||||
45
tbf.h
Normal file
45
tbf.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef __TBF_H__
|
||||
#define __TBF_H__
|
||||
|
||||
// Need a time interval.
|
||||
|
||||
#define TBF_MAX_QUEUE 2 // Maximum of 2 queued packet per
|
||||
#define TBF_MAX_SIZE 3000 // Maxiumum queued packet size is 2048.
|
||||
|
||||
#define TBF_MAX_CREDIT 6000 // Maximum 6000 bytes of credit.
|
||||
#define TBF_RATE 360 // 360 bytes per 1/10th of a second.
|
||||
|
||||
typedef struct {
|
||||
int credit;
|
||||
int lasttime;
|
||||
int queued;
|
||||
int oldest; // Position of packet in the ring buffer.
|
||||
sessionidt sid; // associated session ID.
|
||||
int max_credit; // Maximum amount of credit available (burst size).
|
||||
int rate; // How many bytes of credit per second we get? (sustained rate)
|
||||
void (*send)(sessionidt s, uint8_t *, int); // Routine to actually send out the data.
|
||||
int prev; // Timer chain position.
|
||||
int next; // Timer chain position.
|
||||
|
||||
uint32_t b_queued; // Total bytes sent through this TBF
|
||||
uint32_t b_sent; // Total bytes sucessfully made it to the network.
|
||||
uint32_t p_queued; // ditto packets.
|
||||
uint32_t p_sent; // ditto packets.
|
||||
uint32_t b_dropped; // Total bytes dropped.
|
||||
uint32_t p_dropped; // Total packets dropped.
|
||||
uint32_t p_delayed; // Total packets not sent immediately.
|
||||
|
||||
int sizes[TBF_MAX_QUEUE];
|
||||
uint8_t packets[TBF_MAX_QUEUE][TBF_MAX_SIZE];
|
||||
} tbft;
|
||||
|
||||
void init_tbf(int num_tbfs);
|
||||
int tbf_run_timer(void);
|
||||
int tbf_queue_packet(int tbf_id, uint8_t * data, int size);
|
||||
int new_tbf(int sid, int max_credit, int rate, void (*f)(sessionidt, uint8_t *, int));
|
||||
int free_tbf(int tid);
|
||||
void fsck_tbfs(void);
|
||||
|
||||
int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc);
|
||||
|
||||
#endif /* __TBF_H__ */
|
||||
13
test/README
Normal file
13
test/README
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
generateload, bounce
|
||||
|
||||
L2TP load test. "generateload" simulates a LAC, each session
|
||||
sends UDP packets to a specified IP address, which should be
|
||||
running "bounce" to return the packets to LNS.
|
||||
|
||||
radius
|
||||
|
||||
RADIUS authentication load test.
|
||||
|
||||
ping-sweep
|
||||
|
||||
Send pings of varying sizes to a target host.
|
||||
96
test/bounce.c
Normal file
96
test/bounce.c
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#define PORT 39000
|
||||
|
||||
void sigalarm(int junk);
|
||||
unsigned long long recv_count = 0;
|
||||
unsigned long pps = 0;
|
||||
unsigned long bytes = 0;
|
||||
unsigned long dropped = 0, seq = 0;
|
||||
unsigned port = PORT;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int on = 1;
|
||||
struct sockaddr_in addr;
|
||||
int s;
|
||||
char *packet;
|
||||
|
||||
while ((s = getopt(argc, argv, "?p:")) > 0)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case 'p' :
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case '?' :
|
||||
printf("Options:\n");
|
||||
printf("\t-p port to listen on\n");
|
||||
return(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
if (bind(s, (void *) &addr, sizeof(addr)) < 0)
|
||||
{
|
||||
perror("bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
signal(SIGALRM, sigalarm);
|
||||
alarm(1);
|
||||
|
||||
printf("Waiting on port %d\n", port);
|
||||
packet = (char *)malloc(65535);
|
||||
while (1)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int alen = sizeof(addr), l;
|
||||
unsigned int iseq;
|
||||
|
||||
l = recvfrom(s, packet, 65535, 0, (void *) &addr, &alen);
|
||||
if (l < 0) continue;
|
||||
recv_count++;
|
||||
pps++;
|
||||
bytes += l;
|
||||
iseq = *((unsigned int *) packet);
|
||||
if (seq != iseq)
|
||||
dropped += (iseq - seq);
|
||||
seq = iseq + 1;
|
||||
|
||||
sendto(s, packet, l, 0, (struct sockaddr *)&addr, alen);
|
||||
}
|
||||
|
||||
free(packet);
|
||||
}
|
||||
|
||||
void sigalarm(int junk)
|
||||
{
|
||||
printf("Recv: %10llu %0.1fMbits/s (%lu pps) (%5ld dropped)\n", recv_count, (bytes / 1024.0 / 1024.0 * 8), pps, dropped);
|
||||
pps = bytes = 0;
|
||||
alarm(1);
|
||||
}
|
||||
|
||||
1288
test/generateload.c
Normal file
1288
test/generateload.c
Normal file
File diff suppressed because it is too large
Load diff
121
test/ping-sweep
Normal file
121
test/ping-sweep
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#! /usr/bin/perl -w
|
||||
|
||||
# Ping test: run through packet sizes (default: 56-3000)
|
||||
|
||||
use strict;
|
||||
use Socket;
|
||||
|
||||
use constant TRIES => 4;
|
||||
use constant TIMEOUT => 3; # 3s
|
||||
use constant MAXPACK => 16*1024;
|
||||
|
||||
use constant ICMP_TYPE_ECHOREPLY => 0; # ICMP packet types
|
||||
use constant ICMP_TYPE_ECHO => 8;
|
||||
use constant ICMP_CODE => 0; # No ICMP code for ECHO and ECHOREPLY
|
||||
|
||||
use constant SOL_IP => 0;
|
||||
use constant IP_MTU_DISCOVER => 10;
|
||||
use constant IP_PMTUDISC_DONT => 0;
|
||||
use constant IP_PMTUDISC_WANT => 1;
|
||||
use constant IP_PMTUDISC_DO => 2;
|
||||
|
||||
my $verbose = shift if @ARGV and $ARGV[0] =~ /^--?v(erbose)?$/;
|
||||
my ($host, $min, $max) = @ARGV;
|
||||
|
||||
die "Usage: $0 [-v] HOST [MIN [MAX]]\n" unless $host;
|
||||
my $addr = inet_aton $host or die "$0: invalid host $host\n";
|
||||
my $sin = sockaddr_in 0, $addr;
|
||||
|
||||
$min = 56 if @ARGV < 2;
|
||||
$max = 3000 if @ARGV < 3;
|
||||
$max = $min if $min > $max;
|
||||
|
||||
my $icmp = getprotobyname 'icmp' or die "$0: can't get ICMP proto ($!)\n";
|
||||
socket my $sock, PF_INET, SOCK_RAW, $icmp
|
||||
or die "$0: can't create ICMP socket ($!)\n";
|
||||
|
||||
setsockopt $sock, SOL_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT
|
||||
or die "$0: can't disable PMTU discovery ($!)\n";
|
||||
|
||||
{
|
||||
my $seq = 0;
|
||||
sub icmp_out
|
||||
{
|
||||
my $len = shift;
|
||||
|
||||
# fill data with the *$len*$len*$len*...
|
||||
my $d = sprintf '*%d', $len;
|
||||
my $data = $d x (int ($len / length $d) + 1);
|
||||
|
||||
my $s = 0 + $seq++;
|
||||
$seq %= 65536;
|
||||
|
||||
my $pack = pack "C C n n n a$len" =>
|
||||
ICMP_TYPE_ECHO, # icmp_type
|
||||
ICMP_CODE, # icmp_code
|
||||
0, # icmp_cksum
|
||||
$$, # icmp_id
|
||||
$s, # icmp_seq
|
||||
$data; # payload
|
||||
|
||||
my $cksum = 0;
|
||||
$cksum += $_ for unpack 'n*' => $pack . "\x00";
|
||||
my $wrap;
|
||||
$cksum = ($cksum & 0xffff) + $wrap while ($wrap = ($cksum >> 16));
|
||||
|
||||
substr $pack, 2, 2, pack n => ~$cksum;
|
||||
($s, $pack);
|
||||
}
|
||||
}
|
||||
|
||||
sub icmp_in
|
||||
{
|
||||
my ($pack, $seq) = @_;
|
||||
return unless length $pack >= 28;
|
||||
my ($type, $code, $cksum, $id, $s) = unpack 'C C n n n' => substr $pack, 20;
|
||||
return $type == ICMP_TYPE_ECHOREPLY
|
||||
and $code == ICMP_CODE
|
||||
and $id == $$
|
||||
and $s == $seq;
|
||||
}
|
||||
|
||||
$|++ if $verbose;
|
||||
|
||||
for (my $size = $min; $size <= $max; $size++)
|
||||
{
|
||||
my ($seq, $pack) = icmp_out $size;
|
||||
|
||||
print "$size: " if $verbose;
|
||||
my $res = 0;
|
||||
|
||||
for (my $t = 0; $t < TRIES; $t++)
|
||||
{
|
||||
send $sock, $pack, 0, $sin
|
||||
or die "$0: sendto failed ($!)\n";
|
||||
|
||||
my $rin = '';
|
||||
(vec $rin, fileno $sock, 1) = 1;
|
||||
select $rin, undef, undef, TIMEOUT or next;
|
||||
|
||||
my $peer = recv $sock, my $buf, MAXPACK, 0
|
||||
or die "$0: recvfrom failed ($!)\n";
|
||||
|
||||
next unless (sockaddr_in $peer)[1] eq $addr
|
||||
and icmp_in $buf, $seq;
|
||||
|
||||
# OK
|
||||
$res++;
|
||||
last;
|
||||
}
|
||||
|
||||
if ($verbose)
|
||||
{
|
||||
print +($res ? 'OK' : 'FAIL'), "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "$size\n" unless $res;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
723
test/radius.c
Normal file
723
test/radius.c
Normal file
|
|
@ -0,0 +1,723 @@
|
|||
/* RADIUS authentication load test */
|
||||
|
||||
#define _SVID_SOURCE
|
||||
#define _POSIX_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <signal.h>
|
||||
#include "../md5.h"
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
struct user {
|
||||
char *user;
|
||||
char *pass;
|
||||
int flags;
|
||||
#define F_FAKE 1
|
||||
#define F_BAD 2
|
||||
#define F_USED 4
|
||||
char *request;
|
||||
int request_len;
|
||||
struct user *next;
|
||||
};
|
||||
|
||||
typedef uint32_t u32;
|
||||
|
||||
struct user_list {
|
||||
struct user *entry;
|
||||
int attempts;
|
||||
int response;
|
||||
u32 begin;
|
||||
u32 retry;
|
||||
u32 end;
|
||||
};
|
||||
|
||||
struct stats {
|
||||
int total;
|
||||
int out;
|
||||
int in;
|
||||
int err;
|
||||
int ready;
|
||||
};
|
||||
|
||||
enum {
|
||||
AccessRequest = 1,
|
||||
AccessAccept,
|
||||
AccessReject,
|
||||
AccessFail = 99
|
||||
};
|
||||
|
||||
#define USAGE "Usage: %s [-i input] [-n instances] [-f fake] [-b bad] " \
|
||||
"[-l limit] server port secret\n"
|
||||
|
||||
#define MAX_ATTEMPTS 5
|
||||
|
||||
void *xmalloc(size_t size)
|
||||
{
|
||||
void *p = malloc(size);
|
||||
if (!p)
|
||||
{
|
||||
fprintf(stderr, "out of memory allocating %d bytes\n", size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
char *xstrdup(char *s)
|
||||
{
|
||||
int l = strlen(s);
|
||||
char *p = xmalloc(l + 1);
|
||||
return strcpy(p, s);
|
||||
}
|
||||
|
||||
void *xmmap(size_t size)
|
||||
{
|
||||
void *p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0);
|
||||
|
||||
if (p == MAP_FAILED)
|
||||
{
|
||||
fprintf(stderr, "out of memory allocating %d shared bytes\n", size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void logmsg(char *fmt, ...)
|
||||
{
|
||||
static int new = 1;
|
||||
|
||||
if (new)
|
||||
{
|
||||
static char time_s[] = "YYYY-MM-DD HH:MM:SS ";
|
||||
time_t now = time(NULL);
|
||||
|
||||
strftime(time_s, sizeof(time_s), "%Y-%m-%d %T ", localtime(&now));
|
||||
fputs(time_s, stdout);
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
new = strchr(fmt, '\n') != NULL;
|
||||
}
|
||||
|
||||
void catch(int sig __attribute__ ((unused)) ) {}
|
||||
|
||||
void child(struct user_list *users, int count, int rshift,
|
||||
struct stats *stats, in_addr_t addr, int port, int limit)
|
||||
__attribute__ ((noreturn));
|
||||
|
||||
time_t basetime;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *input = 0;
|
||||
int instances = 1;
|
||||
int fake = 0;
|
||||
int bad = 0;
|
||||
int limit = 100000;
|
||||
int o;
|
||||
|
||||
while ((o = getopt(argc, argv, "i:n:f:b:l:")) != -1)
|
||||
{
|
||||
switch (o)
|
||||
{
|
||||
case 'i': /* input file */
|
||||
input = optarg;
|
||||
break;
|
||||
|
||||
case 'n': /* parallel instances */
|
||||
instances = atoi(optarg);
|
||||
if (instances < 1 || instances > 32)
|
||||
{
|
||||
fprintf(stderr, "invalid instances value: `%s' (1-32)\n", optarg);
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f': /* percentage of additional fake users to add */
|
||||
fake = atoi(optarg);
|
||||
if (fake < 1 || fake > 100)
|
||||
{
|
||||
fprintf(stderr, "invalid fake value: `%s' (1-100)\n", optarg);
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b': /* percentage of users to use incorrect passwords for */
|
||||
bad = atoi(optarg);
|
||||
if (bad < 1 || bad > 100)
|
||||
{
|
||||
fprintf(stderr, "invalid bad value: `%s' (1-100)\n", optarg);
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l': /* limit number of messages per 1/10 sec */
|
||||
limit = atoi(optarg);
|
||||
if (limit < 1)
|
||||
{
|
||||
fprintf(stderr, "invalid limit value: `%s'\n", optarg);
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, USAGE, argv[0]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind != 3)
|
||||
{
|
||||
fprintf(stderr, USAGE, argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *server = argv[optind++];
|
||||
char *port_s = argv[optind++];
|
||||
char *secret = argv[optind];
|
||||
|
||||
int port = atoi(port_s);
|
||||
if (port < 1)
|
||||
{
|
||||
fprintf(stderr, "invalid port: `%s'\n", port_s);
|
||||
return 2;
|
||||
}
|
||||
|
||||
in_addr_t server_addr;
|
||||
{
|
||||
struct hostent *h;
|
||||
if (!(h = gethostbyname(server)) || h->h_addrtype != AF_INET)
|
||||
{
|
||||
fprintf(stderr, "invalid server `%s' (%s)\n", server,
|
||||
h ? "no address" : hstrerror(h_errno));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(&server_addr, h->h_addr, sizeof(server_addr));
|
||||
}
|
||||
|
||||
time(&basetime); /* start clock */
|
||||
|
||||
FILE *in = stdin;
|
||||
if (input && !(in = fopen(input, "r")))
|
||||
{
|
||||
fprintf(stderr, "can't open input file `%s' (%s)\n", input,
|
||||
strerror(errno));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
logmsg("Loading users from %s: ", input ? input : "stdin");
|
||||
|
||||
struct user *users = 0;
|
||||
struct user *u = 0;
|
||||
|
||||
int count = 0;
|
||||
char buf[1024];
|
||||
|
||||
while (fgets(buf, sizeof(buf), in))
|
||||
{
|
||||
count++;
|
||||
|
||||
/* format: username \t password \n */
|
||||
char *p = strchr(buf, '\t');
|
||||
if (!p)
|
||||
{
|
||||
fprintf(stderr, "invalid input line %d (no TAB)\n", count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
if (!u)
|
||||
{
|
||||
users = xmalloc(sizeof(struct user));
|
||||
u = users;
|
||||
}
|
||||
else
|
||||
{
|
||||
u->next = xmalloc(sizeof(struct user));
|
||||
u = u->next;
|
||||
}
|
||||
|
||||
u->user = xstrdup(buf);
|
||||
while (*p == '\t')
|
||||
p++;
|
||||
|
||||
char *q = strchr(p, '\n');
|
||||
if (q)
|
||||
*q = 0;
|
||||
|
||||
if (!*p)
|
||||
{
|
||||
fprintf(stderr, "invalid input line %d (no password)\n", count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
u->pass = xstrdup(p);
|
||||
u->flags = 0;
|
||||
u->next = 0;
|
||||
}
|
||||
|
||||
if (input)
|
||||
fclose(in);
|
||||
|
||||
logmsg("%d\n", count);
|
||||
|
||||
if (!count)
|
||||
return 1;
|
||||
|
||||
char *fake_pw = "__fake__";
|
||||
if (fake)
|
||||
{
|
||||
/* add f fake users to make a total of which fake% are bogus */
|
||||
int f = ((count * fake) / (100.0 - fake) + 0.5);
|
||||
char fake_user[] = "__fake_99999999";
|
||||
|
||||
logmsg("Generating %d%% extra fake users: ", fake);
|
||||
for (int i = 0; i < f; i++, count++)
|
||||
{
|
||||
snprintf(fake_user, sizeof(fake_user), "__fake_%d", i);
|
||||
u->next = xmalloc(sizeof(struct user));
|
||||
u = u->next;
|
||||
u->user = xstrdup(fake_user);
|
||||
u->pass = fake_pw;
|
||||
u->flags = F_FAKE;
|
||||
u->next = 0;
|
||||
}
|
||||
|
||||
logmsg("%d\n", f);
|
||||
}
|
||||
|
||||
if (bad)
|
||||
{
|
||||
int b = (count * bad) / 100.0 + 0.5;
|
||||
|
||||
logmsg("Setting %d%% bad passwords: ", bad);
|
||||
|
||||
u = users;
|
||||
for (int i = 0; i < b; i++, u = u->next)
|
||||
{
|
||||
if (u->pass != fake_pw)
|
||||
free(u->pass);
|
||||
|
||||
u->pass = "__bad__";
|
||||
u->flags |= F_BAD;
|
||||
}
|
||||
|
||||
logmsg("%d\n", b);
|
||||
}
|
||||
|
||||
struct user **unsorted = xmalloc(sizeof(struct user) * count);
|
||||
|
||||
u = users;
|
||||
for (int i = 0; i < count; i++, u = u->next)
|
||||
unsorted[i] = u;
|
||||
|
||||
struct user_list *random = xmmap(sizeof(struct user_list) * count);
|
||||
memset(random, 0, sizeof(struct user_list) * count);
|
||||
|
||||
logmsg("Randomising users: ");
|
||||
|
||||
srand(time(NULL) ^ getpid());
|
||||
|
||||
for (int i = 0; i < count; )
|
||||
{
|
||||
int j = 1.0 * count * rand() / RAND_MAX;
|
||||
if (unsorted[j]->flags & F_USED)
|
||||
continue;
|
||||
|
||||
random[i++].entry = unsorted[j];
|
||||
unsorted[j]->flags |= F_USED;
|
||||
}
|
||||
|
||||
logmsg("done\n");
|
||||
logmsg("Building RADIUS queries: ");
|
||||
|
||||
{
|
||||
char pass[128];
|
||||
|
||||
for (u = users; u; u = u->next)
|
||||
{
|
||||
int pw_len = strlen(u->pass);
|
||||
int len = 4 /* code, identifier, length */
|
||||
+ 16 /* authenticator */
|
||||
+ 2 + strlen(u->user) /* user */
|
||||
+ 2 + ((pw_len / 16) + ((pw_len % 16) ? 1 : 0)) * 16;
|
||||
/* encoded password */
|
||||
|
||||
char *p = xmalloc(len);
|
||||
u->request = p;
|
||||
u->request_len = len;
|
||||
|
||||
*p++ = AccessRequest;
|
||||
*p++ = 0; /* identifier set in child */
|
||||
*(uint16_t *) p = htons(len);
|
||||
p += 2;
|
||||
|
||||
/* authenticator */
|
||||
for (int j = 0; j < 16; j++)
|
||||
*p++ = rand();
|
||||
|
||||
*p = 1; /* user name */
|
||||
p[1] = strlen(u->user) + 2;
|
||||
strcpy(p + 2, u->user);
|
||||
p += p[1];
|
||||
|
||||
strcpy(pass, u->pass);
|
||||
while (pw_len % 16)
|
||||
pass[pw_len++] = 0; /* pad */
|
||||
|
||||
for (int j = 0; j < pw_len; j += 16)
|
||||
{
|
||||
MD5_CTX ctx;
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, secret, strlen(secret));
|
||||
if (j)
|
||||
MD5_Update(&ctx, pass + j - 16, 16);
|
||||
else
|
||||
/* authenticator */
|
||||
MD5_Update(&ctx, u->request + 4, 16);
|
||||
|
||||
uint8_t digest[16];
|
||||
MD5_Final(digest, &ctx);
|
||||
|
||||
for (int k = 0; k < 16; k++)
|
||||
pass[j + k] ^= digest[k];
|
||||
}
|
||||
|
||||
*p = 2; /* password */
|
||||
p[1] = pw_len + 2;
|
||||
memcpy(p + 2, pass, pw_len);
|
||||
p += p[1];
|
||||
}
|
||||
}
|
||||
|
||||
logmsg("done\n");
|
||||
|
||||
signal(SIGUSR1, catch);
|
||||
|
||||
struct stats *stats = xmmap(sizeof(struct stats) * instances);
|
||||
memset(stats, 0, sizeof(struct stats) * instances);
|
||||
|
||||
logmsg("Spawning %d processes: ", instances);
|
||||
|
||||
int per_child = count / instances;
|
||||
int rshift = 0;
|
||||
for (u32 tmp = per_child; tmp & 0xff00; tmp >>= 1)
|
||||
rshift++;
|
||||
|
||||
for (int i = 0, offset = 0; i < instances; i++)
|
||||
{
|
||||
int slack = i ? 0 : count % instances;
|
||||
|
||||
stats[i].total = per_child + slack;
|
||||
if (!fork())
|
||||
child(random + offset, per_child + slack, rshift, stats + i,
|
||||
server_addr, port, limit / instances);
|
||||
|
||||
offset += per_child + slack;
|
||||
}
|
||||
|
||||
logmsg("done\n");
|
||||
|
||||
/* wait for children to setup */
|
||||
int ready = 0;
|
||||
do {
|
||||
ready = 0;
|
||||
for (int i = 0; i < instances; i++)
|
||||
ready += stats[i].ready;
|
||||
|
||||
sleep(1);
|
||||
} while (ready < instances);
|
||||
|
||||
/* go! */
|
||||
kill(0, SIGUSR1);
|
||||
|
||||
logmsg("Processing...\n");
|
||||
logmsg(" total: ");
|
||||
|
||||
for (int i = 0; i < instances; i++)
|
||||
logmsg("[%5d %5s %5s]", stats[i].total, "", "");
|
||||
|
||||
logmsg("\n");
|
||||
logmsg(" out/in/err: ");
|
||||
|
||||
int done = 0;
|
||||
do {
|
||||
for (int i = 0; i < instances; i++)
|
||||
logmsg("[%5d %5d %5d]", stats[i].out, stats[i].in,
|
||||
stats[i].err);
|
||||
|
||||
logmsg("\n");
|
||||
|
||||
if (waitpid(-1, NULL, WNOHANG) > 0)
|
||||
done++;
|
||||
|
||||
if (done < instances)
|
||||
{
|
||||
sleep(1);
|
||||
logmsg(" ");
|
||||
}
|
||||
} while (done < instances);
|
||||
|
||||
int a_hist[MAX_ATTEMPTS + 1];
|
||||
memset(&a_hist, 0, sizeof(a_hist));
|
||||
|
||||
u32 min = 0;
|
||||
u32 max = 0;
|
||||
u32 r_hist[64];
|
||||
memset(&r_hist, 0, sizeof(r_hist));
|
||||
int hsz = sizeof(r_hist) / sizeof(*r_hist);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if ((random[i].response != AccessAccept &&
|
||||
random[i].response != AccessReject) ||
|
||||
(random[i].attempts < 1 ||
|
||||
random[i].attempts > MAX_ATTEMPTS))
|
||||
{
|
||||
a_hist[MAX_ATTEMPTS]++;
|
||||
continue;
|
||||
}
|
||||
|
||||
a_hist[random[i].attempts - 1]++;
|
||||
|
||||
u32 interval = random[i].end - random[i].begin;
|
||||
|
||||
if (!i || interval < min)
|
||||
min = interval;
|
||||
|
||||
if (interval > max)
|
||||
max = interval;
|
||||
|
||||
/* histogram in 1/10s intervals */
|
||||
int t = interval / 10 + 0.5;
|
||||
if (t > hsz - 1)
|
||||
t = hsz - 1;
|
||||
|
||||
r_hist[t]++;
|
||||
}
|
||||
|
||||
logmsg("Send attempts:\n");
|
||||
for (int i = 0; i < MAX_ATTEMPTS; i++)
|
||||
logmsg(" %6d: %d\n", i + 1, a_hist[i]);
|
||||
|
||||
logmsg(" failed: %d\n", a_hist[MAX_ATTEMPTS]);
|
||||
|
||||
logmsg("Response time in seconds (min %.2f, max %.2f)\n",
|
||||
min / 100.0, max / 100.0);
|
||||
|
||||
for (int i = 0; i < hsz; i++)
|
||||
{
|
||||
if (i < hsz - 1)
|
||||
logmsg(" %3.1f:", i / 10.0);
|
||||
else
|
||||
logmsg(" more:");
|
||||
|
||||
logmsg(" %6d\n", r_hist[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* time in sec/100 since program commenced */
|
||||
u32 now(void)
|
||||
{
|
||||
struct timeval t;
|
||||
gettimeofday(&t, 0);
|
||||
return (t.tv_sec - basetime) * 100 + t.tv_usec / 10000 + 1;
|
||||
}
|
||||
|
||||
void child(struct user_list *users, int count, int rshift,
|
||||
struct stats *stats, in_addr_t addr, int port, int limit)
|
||||
{
|
||||
int sockets = 1 << rshift;
|
||||
unsigned rmask = sockets - 1;
|
||||
|
||||
int *sock = xmalloc(sizeof(int) * sockets);
|
||||
|
||||
fd_set r_in;
|
||||
int nfd = 0;
|
||||
|
||||
FD_ZERO(&r_in);
|
||||
|
||||
for (int s = 0; s < sockets; s++)
|
||||
{
|
||||
if ((sock[s] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
||||
{
|
||||
fprintf(stderr, "can't create a UDP socket (%s)\n",
|
||||
strerror(errno));
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int flags = fcntl(sock[s], F_GETFL, 0);
|
||||
fcntl(sock[s], F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
struct sockaddr_in svr;
|
||||
memset(&svr, 0, sizeof(svr));
|
||||
svr.sin_family = AF_INET;
|
||||
svr.sin_port = htons(port);
|
||||
svr.sin_addr.s_addr = addr;
|
||||
|
||||
connect(sock[s], (struct sockaddr *) &svr, sizeof(svr));
|
||||
|
||||
FD_SET(sock[s], &r_in);
|
||||
if (sock[s] + 1 > nfd)
|
||||
nfd = sock[s] + 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
/* set identifier */
|
||||
*((unsigned char *) users[i].entry->request + 1) = i >> rshift;
|
||||
|
||||
stats->ready = 1;
|
||||
pause();
|
||||
|
||||
u32 out_timer = now();
|
||||
int out_count = 0;
|
||||
|
||||
while ((stats->in + stats->err) < count)
|
||||
{
|
||||
u32 time_now = now();
|
||||
|
||||
while (out_timer + 10 < time_now)
|
||||
{
|
||||
out_timer += 10;
|
||||
if (out_count > 0)
|
||||
out_count -= limit;
|
||||
}
|
||||
|
||||
for (int pass = 1; pass <= 2; pass++)
|
||||
{
|
||||
for (int i = 0; i < count && out_count < limit; i++)
|
||||
{
|
||||
if (users[i].response)
|
||||
continue;
|
||||
|
||||
if (users[i].attempts)
|
||||
{
|
||||
if (users[i].retry > time_now)
|
||||
continue;
|
||||
}
|
||||
else if (pass == 1)
|
||||
{
|
||||
/* retries only on the first pass */
|
||||
continue;
|
||||
}
|
||||
|
||||
struct user *e = users[i].entry;
|
||||
if (write(sock[i & rmask], e->request, e->request_len)
|
||||
!= e->request_len)
|
||||
break;
|
||||
|
||||
time_now = now();
|
||||
out_count++;
|
||||
|
||||
if (!users[i].attempts)
|
||||
{
|
||||
users[i].begin = time_now;
|
||||
stats->out++;
|
||||
}
|
||||
|
||||
if (++users[i].attempts > MAX_ATTEMPTS)
|
||||
{
|
||||
users[i].response = AccessFail;
|
||||
stats->err++;
|
||||
continue;
|
||||
}
|
||||
|
||||
users[i].retry = time_now + 200 + 100 * (1 << users[i].attempts);
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval tv = { 0, 100000 };
|
||||
|
||||
fd_set r;
|
||||
memcpy(&r, &r_in, sizeof(r));
|
||||
|
||||
if (select(nfd, &r, NULL, NULL, &tv) < 1)
|
||||
continue;
|
||||
|
||||
char buf[4096];
|
||||
|
||||
for (int s = 0; s < sockets; s++)
|
||||
{
|
||||
if (!FD_ISSET(sock[s], &r))
|
||||
continue;
|
||||
|
||||
int sz;
|
||||
|
||||
while ((sz = read(sock[s], buf, sizeof(buf))) > 0)
|
||||
{
|
||||
if (sz < 2)
|
||||
{
|
||||
fprintf(stderr, "short packet returned\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[0] != AccessAccept && buf[0] != AccessReject)
|
||||
{
|
||||
fprintf(stderr, "unrecognised response type %d\n",
|
||||
(int) buf[0]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int i = s | (((unsigned char) buf[1]) << rshift);
|
||||
if (i < 0 || i > count)
|
||||
{
|
||||
fprintf(stderr, "bogus identifier returned %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!users[i].attempts)
|
||||
{
|
||||
fprintf(stderr, "unexpected identifier returned %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (users[i].response)
|
||||
continue;
|
||||
|
||||
int expect = (users[i].entry->flags & (F_FAKE|F_BAD))
|
||||
? AccessReject : AccessAccept;
|
||||
|
||||
if (buf[0] != expect)
|
||||
fprintf(stderr, "unexpected response %d for user %s "
|
||||
"(expected %d)\n", (int) buf[0], users[i].entry->user,
|
||||
expect);
|
||||
|
||||
users[i].response = buf[0];
|
||||
users[i].end = now();
|
||||
stats->in++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
135
throttlectl.c
Normal file
135
throttlectl.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#include <string.h>
|
||||
#include "l2tpns.h"
|
||||
#include "plugin.h"
|
||||
#include "control.h"
|
||||
|
||||
/* throttle control */
|
||||
|
||||
char const *cvs_id = "$Id: throttlectl.c,v 1.9 2005/10/11 09:04:53 bodea Exp $";
|
||||
|
||||
int plugin_api_version = PLUGIN_API_VERSION;
|
||||
static struct pluginfuncs *f = 0;
|
||||
|
||||
char *plugin_control_help[] = {
|
||||
" throttle USER|SID [RATE|[in|out] RATE ...] Throttle user traffic",
|
||||
" unthrottle USER|SID Stop throttling user",
|
||||
0
|
||||
};
|
||||
|
||||
int plugin_control(struct param_control *data)
|
||||
{
|
||||
sessionidt session;
|
||||
sessiont *s = 0;
|
||||
int flag;
|
||||
char *end;
|
||||
int rate_in = 0;
|
||||
int rate_out = 0;
|
||||
|
||||
if (data->argc < 1)
|
||||
return PLUGIN_RET_OK;
|
||||
|
||||
if (strcmp(data->argv[0], "throttle") &&
|
||||
strcmp(data->argv[0], "unthrottle"))
|
||||
return PLUGIN_RET_OK; // not for us
|
||||
|
||||
if (!data->iam_master)
|
||||
return PLUGIN_RET_NOTMASTER;
|
||||
|
||||
flag = data->argv[0][0] == 't';
|
||||
|
||||
if (flag)
|
||||
{
|
||||
if (data->argc < 2 || data->argc > 6)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "requires username or session id and optional rate(s)";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->argc != 2)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "requires username or session id";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(session = strtol(data->argv[1], &end, 10)) || *end)
|
||||
session = f->get_session_by_username(data->argv[1]);
|
||||
|
||||
if (session)
|
||||
s = f->get_session_by_id(session);
|
||||
|
||||
if (!s || !s->ip)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "session not found";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
rate_in = rate_out = -1;
|
||||
if (data->argc == 2)
|
||||
{
|
||||
unsigned long *rate = f->getconfig("throttle_speed", UNSIGNED_LONG);
|
||||
rate_in = rate_out = *rate;
|
||||
}
|
||||
else if (data->argc == 3)
|
||||
{
|
||||
rate_in = rate_out = atoi(data->argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = 2; i < data->argc - 1; i += 2)
|
||||
{
|
||||
int len = strlen(data->argv[i]);
|
||||
if (!strncmp(data->argv[i], "in", len))
|
||||
{
|
||||
rate_in = atoi(data->argv[i+1]);
|
||||
}
|
||||
else if (!strncmp(data->argv[i], "out", len))
|
||||
{
|
||||
rate_out = atoi(data->argv[i+1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "invalid rate";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rate_in || !rate_out)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = "invalid rate";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (rate_in != -1 && rate_in == s->throttle_in &&
|
||||
rate_out != -1 && rate_out == s->throttle_out)
|
||||
{
|
||||
data->response = NSCTL_RES_ERR;
|
||||
data->additional = flag ? "already throttled" : "not throttled";
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
f->throttle(session, rate_in, rate_out);
|
||||
f->session_changed(session);
|
||||
|
||||
data->response = NSCTL_RES_OK;
|
||||
data->additional = 0;
|
||||
|
||||
return PLUGIN_RET_STOP;
|
||||
}
|
||||
|
||||
int plugin_init(struct pluginfuncs *funcs)
|
||||
{
|
||||
return ((f = funcs)) ? 1 : 0;
|
||||
}
|
||||
175
util.c
Normal file
175
util.c
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/* Misc util functions */
|
||||
|
||||
char const *cvs_id_util = "$Id: util.c,v 1.14 2006/04/05 01:45:57 bodea Exp $";
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "l2tpns.h"
|
||||
#ifdef BGP
|
||||
#include "bgp.h"
|
||||
#endif
|
||||
|
||||
// format ipv4 addr as a dotted-quad; n chooses one of 4 static buffers
|
||||
// to use
|
||||
char *fmtaddr(in_addr_t addr, int n)
|
||||
{
|
||||
static char addrs[4][16];
|
||||
struct in_addr in;
|
||||
|
||||
if (n < 0 || n >= 4)
|
||||
return "";
|
||||
|
||||
in.s_addr = addr;
|
||||
return strcpy(addrs[n], inet_ntoa(in));
|
||||
}
|
||||
|
||||
void *shared_malloc(unsigned int size)
|
||||
{
|
||||
void * p;
|
||||
p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
|
||||
|
||||
if (p == MAP_FAILED)
|
||||
p = NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
extern int forked;
|
||||
extern int cluster_sockfd, tunfd, udpfd, controlfd, daefd, snoopfd, ifrfd, ifr6fd, rand_fd;
|
||||
extern int *radfds;
|
||||
|
||||
pid_t fork_and_close()
|
||||
{
|
||||
pid_t pid = fork();
|
||||
int i;
|
||||
|
||||
if (pid)
|
||||
return pid;
|
||||
|
||||
forked++;
|
||||
if (config->scheduler_fifo)
|
||||
{
|
||||
struct sched_param params = {0};
|
||||
params.sched_priority = 0;
|
||||
if (sched_setscheduler(0, SCHED_OTHER, ¶ms))
|
||||
{
|
||||
LOG(0, 0, 0, "Error setting scheduler to OTHER after fork: %s\n", strerror(errno));
|
||||
LOG(0, 0, 0, "This is probably really really bad.\n");
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGUSR1, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGKILL, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
|
||||
// Close sockets
|
||||
if (clifd != -1) close(clifd);
|
||||
if (cluster_sockfd != -1) close(cluster_sockfd);
|
||||
if (tunfd != -1) close(tunfd);
|
||||
if (udpfd != -1) close(udpfd);
|
||||
if (controlfd != -1) close(controlfd);
|
||||
if (daefd != -1) close(daefd);
|
||||
if (snoopfd != -1) close(snoopfd);
|
||||
if (ifrfd != -1) close(ifrfd);
|
||||
if (ifr6fd != -1) close(ifr6fd);
|
||||
if (rand_fd != -1) close(rand_fd);
|
||||
if (epollfd != -1) close(epollfd);
|
||||
|
||||
for (i = 0; radfds && i < RADIUS_FDS; i++)
|
||||
close(radfds[i]);
|
||||
|
||||
#ifdef BGP
|
||||
for (i = 0; i < BGP_NUM_PEERS; i++)
|
||||
if (bgp_peers[i].sock != -1)
|
||||
close(bgp_peers[i].sock);
|
||||
#endif /* BGP */
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
ssize_t recvfromto(int s, void *buf, size_t len, int flags,
|
||||
struct sockaddr *from, socklen_t *fromlen, struct in_addr *toaddr)
|
||||
{
|
||||
ssize_t r;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec vec;
|
||||
char cbuf[128];
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_name = from;
|
||||
msg.msg_namelen = *fromlen;
|
||||
|
||||
vec.iov_base = buf;
|
||||
vec.iov_len = len;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
msg.msg_control = cbuf;
|
||||
msg.msg_controllen = sizeof(cbuf);
|
||||
|
||||
if ((r = recvmsg(s, &msg, flags)) < 0)
|
||||
return r;
|
||||
|
||||
if (fromlen)
|
||||
*fromlen = msg.msg_namelen;
|
||||
|
||||
memset(toaddr, 0, sizeof(*toaddr));
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
|
||||
{
|
||||
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO)
|
||||
{
|
||||
struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
|
||||
memcpy(toaddr, &i->ipi_addr, sizeof(*toaddr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t sendtofrom(int s, void const *buf, size_t len, int flags,
|
||||
struct sockaddr const *to, socklen_t tolen, struct in_addr const *from)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec vec;
|
||||
struct in_pktinfo pktinfo;
|
||||
char cbuf[CMSG_SPACE(sizeof(pktinfo))];
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_name = (struct sockaddr *) to;
|
||||
msg.msg_namelen = tolen;
|
||||
|
||||
vec.iov_base = (void *) buf;
|
||||
vec.iov_len = len;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
msg.msg_control = cbuf;
|
||||
msg.msg_controllen = sizeof(cbuf);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_IP;
|
||||
cmsg->cmsg_type = IP_PKTINFO;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(pktinfo));
|
||||
|
||||
memset(&pktinfo, 0, sizeof(pktinfo));
|
||||
memcpy(&pktinfo.ipi_spec_dst, from, sizeof(*from));
|
||||
memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
|
||||
|
||||
return sendmsg(s, &msg, flags);
|
||||
}
|
||||
13
util.h
Normal file
13
util.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
char *fmtaddr(in_addr_t addr, int n);
|
||||
void *shared_malloc(unsigned int size);
|
||||
pid_t fork_and_close(void);
|
||||
ssize_t sendtofrom(int s, void const *buf, size_t len, int flags,
|
||||
struct sockaddr const *to, socklen_t tolen, struct in_addr const *from);
|
||||
|
||||
ssize_t recvfromto(int s, void *buf, size_t len, int flags,
|
||||
struct sockaddr *from, socklen_t *fromlen, struct in_addr *toaddr);
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue