Imported Upstream version 2.1.21

This commit is contained in:
Benjamin Cama 2011-07-07 12:45:05 +02:00
commit f2a3180cc0
57 changed files with 24656 additions and 0 deletions

340
COPYING Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

69
Docs/nsctl.8 Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

1244
bgp.c Normal file

File diff suppressed because it is too large Load diff

205
bgp.h Normal file
View 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__ */

3066
cli.c Normal file

File diff suppressed because it is too large Load diff

1870
cluster.c Normal file

File diff suppressed because it is too large Load diff

89
cluster.h Normal file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,2 @@
10.10.10.0/24
10.13.10.0/24

9
etc/l2tpns.logrotate Normal file
View 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
View 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
View file

@ -0,0 +1 @@
# List username:password combinations here for cli users

179
fake_epoll.h Normal file
View 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
View 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
View 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;
}

5489
l2tpns.c Normal file

File diff suppressed because it is too large Load diff

859
l2tpns.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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__ */

2000
ppp.c Normal file

File diff suppressed because it is too large Load diff

1093
radius.c Normal file

File diff suppressed because it is too large Load diff

68
scripts/l2tpns-capture Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

121
test/ping-sweep Normal file
View 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
View 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
View 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
View 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, &params))
{
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
View 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__ */