pax_global_header00006660000000000000000000000064140072455060014515gustar00rootroot0000000000000052 comment=a8694ed0bc261f28ce0f65fcd242a1bc8eb3f52c l2tpns-2.3.3/000077500000000000000000000000001400724550600127445ustar00rootroot00000000000000l2tpns-2.3.3/COPYING000066400000000000000000000431101400724550600137760ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. l2tpns-2.3.3/Changes000066400000000000000000000643231400724550600142470ustar00rootroot00000000000000* 2021-02-05 Julien Rabier 2.3.3 - docs: fix manpages (cosmetic changes, generation and Makefile) * 2021-01-31 Julien Rabier 2.3.2 - docs: Convert from docbook to markdown and pandoc - Fix: infinite loop error 'Unknown AVP vendor' - Fix: adjust Makefile in order to cross build from source in Debian * 2020-11-07 Julien Rabier 2.3.1 - (main): Fix typo and misspellings errors - docs: Fix wrong manual section - Add systemd startup script * 2020 Julien Rabier 2.3.0 - Disable accounting by default - (maint) Fixes common spelling error - (maint) remove debian packaging files (managed here https://salsa.debian.org/debian/l2tpns.git ) * 2014 Fernando Alves 2.2.1-2fdn x - new feature: If the user attribute "Framed-IPv6-Address" is defined then the ICMPv6_RA advertise this IPv6 address. - Fix: Incorrect delegation of IPv6 prefixes when multiple of 4 bits (nibble) (eg: /44, /52 ...). - Added ability to define up to 5 IPv6 prefix delegation by user. - Fix: IPv6 prefix routing on slave cluster - Add of the RDNSS option to ICMPv6 Router Advertisement (RA). - Add of the ppp_keepalive option. * 2013 Fernando Alves 2.2.1-2fdn x - Add tundevicename configuration option - Fix: last_packet no updated in cluster mode - Authorize to change the source IP of the tunnels l2tp - Add pppoe server functionality - Add parameter to disable the send of the L2TP HELLO message (Apple compatibility) - Fix: Tunnel creation does not work when the length of the hostname is odd - Adding the possibility to listening multiple IP L2TP Tunnels - Removing LAC flag - Fix: send SCCCN requested challenge response - add accounting parameter account_all_origin - Fix service_name management and add pppoe_only_equal_svc_name parameter - Adding the possibility to set multiple hostname - Fix: authentication success was sent 2 times - Fix: throttle ipv6 out. - Fix: remove old IPV6 routes on slave - Fix: compiling Warning, dpkg-buildflags ... - Enabled hardened build flags, thanks Moritz Muehlenhoff (closes: #657846) - Packaging updates - Move to 3.0 (native) source format - Bump DH compat level to 8 - Fix ordering of stdio.h/syslog.h includes (closes: #707385) - Create accounting_dir in init script if necessary (closes: #418156) - Bump Standards-Version to 3.9.4.0 - Add build-arch/build-indep targets to debian/rules - Fix: compiling Warning - Fix: remove old IPV6 routes on master - Add DHCPv6 functionality - Fix cluster slave; no add the ipv6 route address (/128) if included in the delegated prefix. - Fix cluster slave; reset to 0, the end of the session when the master version < slave version. * 2012 Fernando Alves 2.2.1-2fdn x - Fix MLPPP functionality. - Fix the inverted "delete/add" of the routes, in cluster mode. - Add a echo_timeout configuration option. - Add a idle_echo_timeout configuration option. - Add LAC functionality, possibility to forward ppp to Remote LNS. * Sun Sep 11 2011 Brendan O'Dea 2.2.x - Apply MLPPP patch from Muhammad Tayseer Alquoatli. - Apply patch from Michael O to avoid sending multiple CDNs. - Apply patch from Cyril Elkaim to fix an issue with MacOS. - Apply patch from Geoffrey D. Bennett to fix retry of control packets. - Apply patch from Geoffrey D. Bennett to handle RADIUS Class attribute. - Bump heartbeat version to handle Class entry in session (v6). - Re-arrange session struct to remove padding. - Update cluster code to handle v6 packets. Drop compatability for pre-v5. - Clean up some compiler errors. * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. - Remove non-working setuid option. - Convert manual.html to Docbook. - Kludge around problem with Netgear DM602 authentication. - Add session/idle timeouts (Graham Maltby). - Use result code AVP to set Acct-Terminate-Cause is disconnect cause AVP is not present. - Add radius_bind_{min,max} to simplify firewalling of RADIUS ports. - Fix sign problem with reporting of unknown RADIUS VSAs. - Allow DNS servers to be specified either using the old or new vendor-specific Ascend formats. - Security [CVE-2006-5873]: Rhys Kidd identified a vulnerability in the handling of heartbeat packets. Drop oversize heartbeat packets. - Don't send interim records before session start (Daryl Tester). - Add "shutdown" and "reload" CLI commands (Daryl Tester). * Tue Apr 18 2006 Brendan O'Dea 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 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 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 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 2.1.14 - Run PLUGIN_RADIUS_ACCOUNT for Start records. * Wed Dec 7 2005 Brendan O'Dea 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 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 2.1.11 - Fix fragment handling in ip_filter. - Exclude counter when comparing filter rules. * Sat Nov 5 2005 Brendan O'Dea 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 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 2.1.8 - Move code from signal handlers into mainloop, avoiding a race condition when forking CLI. * Fri Sep 16 2005 Brendan O'Dea 2.1.7 - This time, for sure: really fix Protocol-Reject. * Fri Sep 16 2005 Brendan O'Dea 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 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 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 2.1.3 - Fail IPCP negotiation only on ConfigRej of IP-Address. * Wed Aug 10 2005 Brendan O'Dea 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 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 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 minimum 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 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 2.0.12 - The "This time, for sure!" release. - Fix throttlectl plugin argument parsing. * Wed Dec 1 2004 Brendan O'Dea 2.0.11 - Don't send a RADIUS start record when ungardening on shutdown. * Wed Dec 1 2004 Brendan O'Dea 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 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 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 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 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 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 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 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 separate u16 values for throttle rate in/out - Defer adding radius fds to the select loop until become_master * Thu Sep 02 2004 David Parrish 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 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 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 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 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 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 l2tpns-2.3.3/INSTALL000066400000000000000000000046411400724550600140020ustar00rootroot00000000000000Brief 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 l2tpns-2.3.3/INTERNALS000066400000000000000000000230561400724550600142340ustar00rootroot00000000000000Documentation 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 , 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. l2tpns-2.3.3/Makefile000066400000000000000000000105161400724550600144070ustar00rootroot00000000000000DESTDIR = 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 = $(CC) INCLUDES = -I. CPPFLAGS = $(INCLUDES) $(DEFINES) CFLAGS = -Wall -Wformat-security $(OPTIM) LDFLAGS = LDLIBS = INSTALL = install -c -D -o root -g root l2tpns.LIBS = -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 pppoe.o l2tplac.o dhcp6.o ipv6_u.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 && \ $(CC) -MM $(CPPFLAGS) $(OBJS:.o=.c) && \ $(CC) -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/manpages/startup-config.5 $(DESTDIR)$(man5dir)/startup-config.5 $(INSTALL) -m 0644 docs/manpages/l2tpns.8 $(DESTDIR)$(man8dir)/l2tpns.8 $(INSTALL) -m 0644 docs/manpages/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 dhcp6.h l2tpns.h cli.o: cli.c dhcp6.h l2tpns.h constants.h util.h cluster.h tbf.h ll.h \ bgp.h l2tplac.h cluster.o: cluster.c dhcp6.h l2tpns.h cluster.h util.h tbf.h pppoe.h \ bgp.h constants.o: constants.c constants.h control.o: control.c dhcp6.h l2tpns.h control.h icmp.o: icmp.c dhcp6.h l2tpns.h ipv6_u.h l2tpns.o: l2tpns.c md5.h dhcp6.h l2tpns.h cluster.h plugin.h ll.h \ constants.h control.h util.h tbf.h bgp.h l2tplac.h pppoe.h ll.o: ll.c ll.h md5.o: md5.c md5.h ppp.o: ppp.c dhcp6.h l2tpns.h constants.h plugin.h util.h tbf.h cluster.h \ l2tplac.h pppoe.h radius.o: radius.c md5.h constants.h dhcp6.h l2tpns.h plugin.h util.h \ cluster.h l2tplac.h pppoe.h tbf.o: tbf.c dhcp6.h l2tpns.h util.h tbf.h util.o: util.c dhcp6.h l2tpns.h bgp.h pppoe.o: pppoe.c dhcp6.h l2tpns.h cluster.h constants.h md5.h util.h l2tplac.o: l2tplac.c md5.h dhcp6.h l2tpns.h util.h cluster.h l2tplac.h \ pppoe.h dhcp6.o: dhcp6.c dhcp6.h l2tpns.h ipv6_u.h ipv6_u.o: ipv6_u.c ipv6_u.h bgp.o: bgp.c dhcp6.h l2tpns.h bgp.h util.h autosnoop.so: autosnoop.c dhcp6.h l2tpns.h plugin.h autothrottle.so: autothrottle.c dhcp6.h l2tpns.h plugin.h garden.so: garden.c dhcp6.h l2tpns.h plugin.h control.h sessionctl.so: sessionctl.c dhcp6.h l2tpns.h plugin.h control.h setrxspeed.so: setrxspeed.c dhcp6.h l2tpns.h plugin.h snoopctl.so: snoopctl.c dhcp6.h l2tpns.h plugin.h control.h stripdomain.so: stripdomain.c dhcp6.h l2tpns.h plugin.h throttlectl.so: throttlectl.c dhcp6.h l2tpns.h plugin.h control.h l2tpns-2.3.3/README.md000066400000000000000000000002321400724550600142200ustar00rootroot00000000000000# Layer 2 tunneling protocol network server (LNS) * Mailing list: * Code: l2tpns-2.3.3/THANKS000066400000000000000000000036101400724550600136570ustar00rootroot00000000000000A list of people who have contributed to the development of L2TPNS by reporting problems, suggesting various improvements or submitting actual code follows. Adrian Kennard David Parrish Michael O'Reilly Brendan O'Dea Bradley Baetz Iain Wade Yuri Juergen Kammer Simon Talbot Jonathan McDowell Bjørn Augestad Roberto Chostakovis Jordan Hrycaj Vladislav Bjelic Alex Kiernan Dominique Rousseau Tim Devries Slobodan Tomic Michael Chapman Charlie Brady Jon Morby Paul Martin Jonathan Yarden Patrick Cole Khaled Al Hamwi Graham Maltby Rhys Kidd Muhammad Tayseer Alquoatli Cyril Elkaim Geoffry D. Bennett Fernando Alves Benjamin Cama Sebastien Badia Julien Rabier Baptiste Jonglez l2tpns-2.3.3/arp.c000066400000000000000000000031551400724550600136760ustar00rootroot00000000000000// L2TPNS: arp #include #include #include #include #include #include #include "dhcp6.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); } l2tpns-2.3.3/autosnoop.c000066400000000000000000000032311400724550600151360ustar00rootroot00000000000000#include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" /* set up intercept based on RADIUS reply */ 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; } l2tpns-2.3.3/autothrottle.c000066400000000000000000000070411400724550600156500ustar00rootroot00000000000000#include #include #include "dhcp6.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_speed from config) * throttle=no */ 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; } l2tpns-2.3.3/bgp.c000066400000000000000000001247161400724550600136730ustar00rootroot00000000000000/* * BGPv4 * Used to advertise routes for upstream (l2tp port, rather than gratiutious * arp) and downstream--allowing routers to load-balance both. * * Implementation limitations: * - We never listen for incoming connections (session always initiated by us). * - Any routes advertised by the peer are accepted, but ignored. * - No password support; neither RFC1771 (which no-one seems to do anyway) * nor RFC2385 (which requires a kernel patch on 2.4 kernels). */ #include #include #include #include #include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "bgp.h" #include "util.h" static void bgp_clear(struct bgp_peer *peer); static void bgp_set_retry(struct bgp_peer *peer); static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head, struct bgp_route_list *new); static struct bgp_route6_list *bgp_insert_route6(struct bgp_route6_list *head, struct bgp_route6_list *new); static void bgp_process_timers(struct bgp_peer *peer); static void bgp_free_routes(struct bgp_route_list *routes); static void bgp_free_routes6(struct bgp_route6_list *routes); static char const *bgp_msg_type_str(uint8_t type); static int bgp_connect(struct bgp_peer *peer); static int bgp_handle_connect(struct bgp_peer *peer); static int bgp_write(struct bgp_peer *peer); static int bgp_read(struct bgp_peer *peer); static int bgp_handle_input(struct bgp_peer *peer); static int bgp_send_open(struct bgp_peer *peer); static int bgp_send_keepalive(struct bgp_peer *peer); static int bgp_send_update(struct bgp_peer *peer); static int bgp_send_update6(struct bgp_peer *peer); static int bgp_send_notification(struct bgp_peer *peer, uint8_t code, uint8_t subcode); static uint16_t our_as; static struct bgp_route_list *bgp_routes = 0; static struct bgp_route6_list *bgp_routes6 = 0; int bgp_configured = 0; struct bgp_peer *bgp_peers = 0; /* prepare peer structure, globals */ int bgp_setup(int as) { int i; struct bgp_peer *peer; for (i = 0; i < BGP_NUM_PEERS; i++) { peer = &bgp_peers[i]; memset(peer, 0, sizeof(*peer)); peer->addr = INADDR_NONE; peer->sock = -1; peer->state = peer->next_state = Disabled; if (!((peer->outbuf = malloc(sizeof(*peer->outbuf))) && (peer->inbuf = malloc(sizeof(*peer->inbuf))))) { LOG(0, 0, 0, "Can't allocate buffers for bgp peer (%s)\n", strerror(errno)); return 0; } peer->edata.type = FD_TYPE_BGP; peer->edata.index = i; peer->events = 0; } if (as < 1) as = 0; if ((our_as = as)) return 0; bgp_routes = 0; bgp_routes6 = 0; bgp_configured = 0; /* set by bgp_start */ return 1; } /* start connection with a peer */ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive, int hold, struct in_addr update_source, int enable) { struct hostent *h; int ibgp; int i; struct bgp_path_attr a; char path_attrs[64]; char *p = path_attrs; in_addr_t ip; uint32_t metric = htonl(BGP_METRIC); uint32_t no_export = htonl(BGP_COMMUNITY_NO_EXPORT); if (!our_as) return 0; if (peer->state != Disabled) bgp_halt(peer); snprintf(peer->name, sizeof(peer->name), "%s", name); if (!(h = gethostbyname(name)) || h->h_addrtype != AF_INET) { LOG(0, 0, 0, "Can't get address for BGP peer %s (%s)\n", name, h ? "no address" : hstrerror(h_errno)); return 0; } memcpy(&peer->addr, h->h_addr, sizeof(peer->addr)); peer->source_addr = update_source.s_addr; peer->as = as > 0 ? as : our_as; ibgp = peer->as == our_as; /* set initial timer values */ peer->init_keepalive = keepalive == -1 ? BGP_KEEPALIVE_TIME : keepalive; peer->init_hold = hold == -1 ? BGP_HOLD_TIME : hold; if (peer->init_hold < 3) peer->init_hold = 3; if (peer->init_keepalive * 3 > peer->init_hold) peer->init_keepalive = peer->init_hold / 3; /* clear buffers, go to Idle state */ peer->next_state = Idle; bgp_clear(peer); /* set initial routing state */ peer->routing = enable; /* all our routes use the same attributes, so prepare it in advance */ if (peer->path_attrs) free(peer->path_attrs); peer->path_attr_len = 0; /* ORIGIN */ a.flags = BGP_PATH_ATTR_FLAG_TRANS; a.code = BGP_PATH_ATTR_CODE_ORIGIN; a.data.s.len = 1; a.data.s.value[0] = BGP_PATH_ATTR_CODE_ORIGIN_IGP; #define ADD_ATTRIBUTE() do { \ i = BGP_PATH_ATTR_SIZE(a); \ memcpy(p, &a, i); \ p += i; \ peer->path_attr_len += i; } while (0) ADD_ATTRIBUTE(); /* AS_PATH */ a.flags = BGP_PATH_ATTR_FLAG_TRANS; a.code = BGP_PATH_ATTR_CODE_AS_PATH; if (ibgp) { /* empty path */ a.data.s.len = 0; } else { /* just our AS */ struct { uint8_t type; uint8_t len; uint16_t value; } as_path = { BGP_PATH_ATTR_CODE_AS_PATH_AS_SEQUENCE, 1, htons(our_as), }; a.data.s.len = sizeof(as_path); memcpy(&a.data.s.value, &as_path, sizeof(as_path)); } ADD_ATTRIBUTE(); /* MULTI_EXIT_DISC */ a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL; a.code = BGP_PATH_ATTR_CODE_MULTI_EXIT_DISC; a.data.s.len = sizeof(metric); memcpy(a.data.s.value, &metric, sizeof(metric)); ADD_ATTRIBUTE(); if (ibgp) { uint32_t local_pref = htonl(BGP_LOCAL_PREF); /* LOCAL_PREF */ a.flags = BGP_PATH_ATTR_FLAG_TRANS; a.code = BGP_PATH_ATTR_CODE_LOCAL_PREF; a.data.s.len = sizeof(local_pref); memcpy(a.data.s.value, &local_pref, sizeof(local_pref)); ADD_ATTRIBUTE(); } /* COMMUNITIES */ a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL | BGP_PATH_ATTR_FLAG_TRANS; a.code = BGP_PATH_ATTR_CODE_COMMUNITIES; a.data.s.len = sizeof(no_export); memcpy(a.data.s.value, &no_export, sizeof(no_export)); ADD_ATTRIBUTE(); /* remember the len before adding NEXT_HOP */ peer->path_attr_len_without_nexthop = peer->path_attr_len; /* NEXT_HOP */ a.flags = BGP_PATH_ATTR_FLAG_TRANS; a.code = BGP_PATH_ATTR_CODE_NEXT_HOP; if (config->nexthop_address) { ip = config->nexthop_address; } else { ip = my_address; /* we're it */ } a.data.s.len = sizeof(ip); memcpy(a.data.s.value, &ip, sizeof(ip)); ADD_ATTRIBUTE(); if (!(peer->path_attrs = malloc(peer->path_attr_len))) { LOG(0, 0, 0, "Can't allocate path_attrs for %s (%s)\n", name, strerror(errno)); return 0; } memcpy(peer->path_attrs, path_attrs, peer->path_attr_len); /* multiprotocol attributes initialization */ if (config->ipv6_prefix.s6_addr[0]) { struct bgp_attr_mp_reach_nlri_partial mp_reach_nlri_partial; struct bgp_attr_mp_unreach_nlri_partial mp_unreach_nlri_partial; a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL; a.code = BGP_PATH_ATTR_CODE_MP_REACH_NLRI; a.data.s.len = 0; /* will be set on UPDATE */ mp_reach_nlri_partial.afi = htons(BGP_MP_AFI_IPv6); mp_reach_nlri_partial.safi = BGP_MP_SAFI_UNICAST; mp_reach_nlri_partial.reserved = 0; mp_reach_nlri_partial.next_hop_len = 16; /* use the defined nexthop6, or our address in ipv6_prefix */ if (config->nexthop6_address.s6_addr[0]) memcpy(&mp_reach_nlri_partial.next_hop, &config->nexthop6_address.s6_addr, 16); else { /* our address is ipv6prefix::1 */ memcpy(&mp_reach_nlri_partial.next_hop, &config->ipv6_prefix.s6_addr, 16); mp_reach_nlri_partial.next_hop[15] = 1; } memcpy(&a.data.s.value, &mp_reach_nlri_partial, sizeof(struct bgp_attr_mp_reach_nlri_partial)); memcpy(&peer->mp_reach_nlri_partial, &a, BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE); a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL | BGP_PATH_ATTR_FLAG_EXTLEN; a.code = BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI; a.data.e.len = 0; /* will be set on UPDATE */ mp_unreach_nlri_partial.afi = htons(BGP_MP_AFI_IPv6); mp_unreach_nlri_partial.safi = BGP_MP_SAFI_UNICAST; memcpy(&a.data.e.value, &mp_unreach_nlri_partial, sizeof(struct bgp_attr_mp_unreach_nlri_partial)); memcpy(&peer->mp_unreach_nlri_partial, &a, BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE); } peer->mp_handling = HandlingUnknown; LOG(4, 0, 0, "Initiating BGP connection to %s (routing %s)\n", name, enable ? "enabled" : "suspended"); /* we have at least one peer configured */ bgp_configured = 1; /* connect */ return bgp_connect(peer); } /* clear counters, timers, routes and buffers; close socket; move to next_state, which may be Disabled or Idle */ static void bgp_clear(struct bgp_peer *peer) { if (peer->sock != -1) { close(peer->sock); peer->sock = -1; } peer->keepalive_time = 0; peer->expire_time = 0; peer->keepalive = peer->init_keepalive; peer->hold = peer->init_hold; bgp_free_routes(peer->routes); peer->routes = 0; bgp_free_routes6(peer->routes6); peer->routes6 = 0; peer->outbuf->packet.header.len = 0; peer->outbuf->done = 0; peer->inbuf->packet.header.len = 0; peer->inbuf->done = 0; peer->cli_flag = 0; peer->events = 0; if (peer->state != peer->next_state) { peer->state = peer->next_state; peer->state_time = time_now; LOG(4, 0, 0, "BGP peer %s: state %s\n", peer->name, bgp_state_str(peer->next_state)); } } /* initiate a clean shutdown */ void bgp_stop(struct bgp_peer *peer) { LOG(4, 0, 0, "Terminating BGP connection to %s\n", peer->name); bgp_send_notification(peer, BGP_ERR_CEASE, 0); } /* drop connection (if any) and set state to Disabled */ void bgp_halt(struct bgp_peer *peer) { LOG(4, 0, 0, "Aborting BGP connection to %s\n", peer->name); peer->next_state = Disabled; bgp_clear(peer); } /* drop connection (if any) and set to Idle for connection retry */ int bgp_restart(struct bgp_peer *peer) { peer->next_state = Idle; bgp_clear(peer); /* restart now */ peer->retry_time = time_now; peer->retry_count = 0; /* connect */ return bgp_connect(peer); } static void bgp_set_retry(struct bgp_peer *peer) { if (peer->retry_count++ < BGP_MAX_RETRY) { peer->retry_time = time_now + (BGP_RETRY_BACKOFF * peer->retry_count); peer->next_state = Idle; bgp_clear(peer); } else bgp_halt(peer); /* give up */ } /* insert route into list; sorted */ static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head, struct bgp_route_list *new) { struct bgp_route_list *p = head; struct bgp_route_list *e = 0; while (p && memcmp(&p->dest, &new->dest, sizeof(p->dest)) < 0) { e = p; p = p->next; } if (e) { new->next = e->next; e->next = new; } else { new->next = head; head = new; } return head; } /* insert route6 into list; sorted */ static struct bgp_route6_list *bgp_insert_route6(struct bgp_route6_list *head, struct bgp_route6_list *new) { struct bgp_route6_list *p = head; struct bgp_route6_list *e = 0; while (p && memcmp(&p->dest, &new->dest, sizeof(p->dest)) < 0) { e = p; p = p->next; } if (e) { new->next = e->next; e->next = new; } else { new->next = head; head = new; } return head; } /* add route to list for peers */ /* * Note: this doesn't do route aggregation, nor drop routes if a less * specific match already exists (partly because I'm lazy, but also so * that if that route is later deleted we don't have to be concerned * about adding back the more specific one). */ int bgp_add_route(in_addr_t ip, int prefixlen) { struct bgp_route_list *r = bgp_routes; struct bgp_route_list add; int i; add.dest.prefix = ip; add.dest.len = prefixlen; add.next = 0; /* check for duplicate */ while (r) { i = memcmp(&r->dest, &add.dest, sizeof(r->dest)); if (!i) return 1; /* already covered */ if (i > 0) break; r = r->next; } /* insert into route list; sorted */ if (!(r = malloc(sizeof(*r)))) { LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n", fmtaddr(add.dest.prefix, 0), add.dest.len, strerror(errno)); return 0; } memcpy(r, &add, sizeof(*r)); bgp_routes = bgp_insert_route(bgp_routes, r); /* flag established peers for update */ for (i = 0; i < BGP_NUM_PEERS; i++) if (bgp_peers[i].state == Established) bgp_peers[i].update_routes = 1; LOG(4, 0, 0, "Registered BGP route %s/%d\n", fmtaddr(add.dest.prefix, 0), add.dest.len); return 1; } /* add route to list for peers */ /* * Note: same provisions as above */ int bgp_add_route6(struct in6_addr ip, int prefixlen) { struct bgp_route6_list *r = bgp_routes6; struct bgp_route6_list add; int i; char ipv6addr[INET6_ADDRSTRLEN]; memcpy(&add.dest.prefix, &ip.s6_addr, 16); add.dest.len = prefixlen; add.next = 0; /* check for duplicate */ while (r) { i = memcmp(&r->dest, &add.dest, sizeof(r->dest)); if (!i) return 1; /* already covered */ if (i > 0) break; r = r->next; } /* insert into route list; sorted */ if (!(r = malloc(sizeof(*r)))) { LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), add.dest.len, strerror(errno)); return 0; } memcpy(r, &add, sizeof(*r)); bgp_routes6 = bgp_insert_route6(bgp_routes6, r); /* flag established peers for update */ for (i = 0; i < BGP_NUM_PEERS; i++) if (bgp_peers[i].state == Established && bgp_peers[i].mp_handling == HandleIPv6Routes) bgp_peers[i].update_routes6 = 1; LOG(4, 0, 0, "Registered BGP route %s/%d\n", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), add.dest.len); return 1; } /* remove route from list for peers */ int bgp_del_route(in_addr_t ip, int prefixlen) { struct bgp_route_list *r = bgp_routes; struct bgp_route_list *e = 0; struct bgp_route_list del; int i; del.dest.prefix = ip; del.dest.len = prefixlen; del.next = 0; /* find entry in routes list and remove */ while (r) { i = memcmp(&r->dest, &del.dest, sizeof(r->dest)); if (!i) { if (e) e->next = r->next; else bgp_routes = r->next; free(r); break; } e = r; if (i > 0) r = 0; /* stop */ else r = r->next; } /* not found */ if (!r) return 1; /* flag established peers for update */ for (i = 0; i < BGP_NUM_PEERS; i++) if (bgp_peers[i].state == Established) bgp_peers[i].update_routes = 1; LOG(4, 0, 0, "Removed BGP route %s/%d\n", fmtaddr(del.dest.prefix, 0), del.dest.len); return 1; } /* remove route from list for peers */ int bgp_del_route6(struct in6_addr ip, int prefixlen) { struct bgp_route6_list *r = bgp_routes6; struct bgp_route6_list *e = 0; struct bgp_route6_list del; int i; char ipv6addr[INET6_ADDRSTRLEN]; memcpy(&del.dest.prefix, &ip.s6_addr, 16); del.dest.len = prefixlen; del.next = 0; /* find entry in routes list and remove */ while (r) { i = memcmp(&r->dest, &del.dest, sizeof(r->dest)); if (!i) { if (e) e->next = r->next; else bgp_routes6 = r->next; free(r); break; } e = r; if (i > 0) r = 0; /* stop */ else r = r->next; } /* not found */ if (!r) return 1; /* flag established peers for update */ for (i = 0; i < BGP_NUM_PEERS; i++) if (bgp_peers[i].state == Established && bgp_peers[i].mp_handling == HandleIPv6Routes) bgp_peers[i].update_routes6 = 1; LOG(4, 0, 0, "Removed BGP route %s/%d\n", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), del.dest.len); return 1; } /* enable or disable routing */ void bgp_enable_routing(int enable) { int i; for (i = 0; i < BGP_NUM_PEERS; i++) { bgp_peers[i].routing = enable; /* flag established peers for update */ if (bgp_peers[i].state == Established) bgp_peers[i].update_routes = 1; } LOG(4, 0, 0, "%s BGP routing\n", enable ? "Enabled" : "Suspended"); } #ifdef HAVE_EPOLL # include #else # include "fake_epoll.h" #endif /* return a bitmask of the events required to poll this peer's fd */ int bgp_set_poll() { int i; if (!bgp_configured) return 0; for (i = 0; i < BGP_NUM_PEERS; i++) { struct bgp_peer *peer = &bgp_peers[i]; int events = 0; if (peer->state == Disabled || peer->state == Idle) continue; if (peer->inbuf->done < BGP_MAX_PACKET_SIZE) events |= EPOLLIN; if (peer->state == Connect || /* connection in progress */ peer->update_routes || /* routing updates */ peer->outbuf->packet.header.len) /* pending output */ events |= EPOLLOUT; if (peer->events != events) { struct epoll_event ev; ev.events = peer->events = events; ev.data.ptr = &peer->edata; epoll_ctl(epollfd, EPOLL_CTL_MOD, peer->sock, &ev); } } return 1; } /* process bgp events/timers */ int bgp_process(uint32_t events[]) { int i; if (!bgp_configured) return 0; for (i = 0; i < BGP_NUM_PEERS; i++) { struct bgp_peer *peer = &bgp_peers[i]; if (*peer->name && peer->cli_flag == BGP_CLI_RESTART) { bgp_restart(peer); continue; } if (peer->state == Disabled) continue; if (peer->cli_flag) { switch (peer->cli_flag) { case BGP_CLI_SUSPEND: if (peer->routing) { peer->routing = 0; if (peer->state == Established) peer->update_routes = 1; } break; case BGP_CLI_ENABLE: if (!peer->routing) { peer->routing = 1; if (peer->state == Established) peer->update_routes = 1; } break; } peer->cli_flag = 0; } /* handle empty/fill of buffers */ if (events[i] & EPOLLOUT) { int r = 1; if (peer->state == Connect) r = bgp_handle_connect(peer); else if (peer->outbuf->packet.header.len) r = bgp_write(peer); if (!r) continue; } if (events[i] & (EPOLLIN|EPOLLHUP)) { if (!bgp_read(peer)) continue; } /* process input buffer contents */ while (peer->inbuf->done >= sizeof(peer->inbuf->packet.header) && !peer->outbuf->packet.header.len) /* may need to queue a response */ { if (bgp_handle_input(peer) < 0) continue; } /* process pending updates */ if (peer->update_routes && !peer->outbuf->packet.header.len) /* ditto */ { if (!bgp_send_update(peer)) continue; } /* process pending IPv6 updates */ if (peer->update_routes6 && !peer->outbuf->packet.header.len) /* ditto */ { if (!bgp_send_update6(peer)) continue; } /* process timers */ bgp_process_timers(peer); } return 1; } /* process bgp timers only */ void bgp_process_peers_timers() { int i; if (!bgp_configured) return; for (i = 0; i < BGP_NUM_PEERS; i++) { struct bgp_peer *peer = &bgp_peers[i]; if (peer->state == Disabled) continue; bgp_process_timers(peer); } } static void bgp_process_timers(struct bgp_peer *peer) { if (peer->state == Established) { if (time_now > peer->expire_time) { LOG(1, 0, 0, "No message from BGP peer %s in %ds\n", peer->name, peer->hold); bgp_send_notification(peer, BGP_ERR_HOLD_TIMER_EXP, 0); return; } if (time_now > peer->keepalive_time && !peer->outbuf->packet.header.len) bgp_send_keepalive(peer); } else if (peer->state == Idle) { if (time_now > peer->retry_time) bgp_connect(peer); } else if (time_now > peer->state_time + BGP_STATE_TIME) { LOG(1, 0, 0, "%s timer expired for BGP peer %s\n", bgp_state_str(peer->state), peer->name); bgp_restart(peer); } } static void bgp_free_routes(struct bgp_route_list *routes) { struct bgp_route_list *tmp; while ((tmp = routes)) { routes = tmp->next; free(tmp); } } static void bgp_free_routes6(struct bgp_route6_list *routes) { struct bgp_route6_list *tmp; while ((tmp = routes)) { routes = tmp->next; free(tmp); } } char const *bgp_state_str(enum bgp_state state) { switch (state) { case Disabled: return "Disabled"; case Idle: return "Idle"; case Connect: return "Connect"; case Active: return "Active"; case OpenSent: return "OpenSent"; case OpenConfirm: return "OpenConfirm"; case Established: return "Established"; } return "?"; } static char const *bgp_msg_type_str(uint8_t type) { switch (type) { case BGP_MSG_OPEN: return "OPEN"; case BGP_MSG_UPDATE: return "UPDATE"; case BGP_MSG_NOTIFICATION: return "NOTIFICATION"; case BGP_MSG_KEEPALIVE: return "KEEPALIVE"; } return "?"; } /* attempt to connect to peer */ static int bgp_connect(struct bgp_peer *peer) { static int bgp_port = 0; struct sockaddr_in addr; struct sockaddr_in source_addr; struct epoll_event ev; if (!bgp_port) { struct servent *serv; if (!(serv = getservbyname("bgp", "tcp"))) { LOG(0, 0, 0, "Can't get bgp service (%s)\n", strerror(errno)); return 0; } bgp_port = serv->s_port; } if ((peer->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { LOG(0, 0, 0, "Can't create a socket for BGP peer %s (%s)\n", peer->name, strerror(errno)); peer->state = peer->next_state = Disabled; return 0; } /* add to poll set */ ev.events = peer->events = EPOLLOUT; ev.data.ptr = &peer->edata; epoll_ctl(epollfd, EPOLL_CTL_ADD, peer->sock, &ev); /* set to non-blocking */ fcntl(peer->sock, F_SETFL, fcntl(peer->sock, F_GETFL, 0) | O_NONBLOCK); /* set source address */ memset(&source_addr, 0, sizeof(source_addr)); source_addr.sin_family = AF_INET; source_addr.sin_addr.s_addr = peer->source_addr; /* defaults to INADDR_ANY */ if (bind(peer->sock, (struct sockaddr *) &source_addr, sizeof(source_addr)) < 0) { LOG(1, 0, 0, "Can't set source address to %s: %s\n", inet_ntoa(source_addr.sin_addr), strerror(errno)); bgp_set_retry(peer); return 0; } /* try connect */ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = bgp_port; addr.sin_addr.s_addr = peer->addr; while (connect(peer->sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { if (errno == EINTR) /* SIGALARM handler */ continue; if (errno != EINPROGRESS) { LOG(1, 0, 0, "Can't connect to BGP peer %s (%s)\n", inet_ntoa(addr.sin_addr), strerror(errno)); bgp_set_retry(peer); return 0; } peer->state = Connect; peer->state_time = time_now; LOG(4, 0, 0, "BGP peer %s: state Connect\n", peer->name); return 1; } peer->state = Active; peer->state_time = time_now; peer->retry_time = peer->retry_count = 0; LOG(4, 0, 0, "BGP peer %s: state Active\n", inet_ntoa(addr.sin_addr)); return bgp_send_open(peer); } /* complete partial connection (state = Connect) */ static int bgp_handle_connect(struct bgp_peer *peer) { int err = 0; socklen_t len = sizeof(int); getsockopt(peer->sock, SOL_SOCKET, SO_ERROR, &err, &len); if (err) { LOG(1, 0, 0, "Can't connect to BGP peer %s (%s)\n", peer->name, strerror(err)); bgp_set_retry(peer); return 0; } peer->state = Active; peer->state_time = time_now; LOG(4, 0, 0, "BGP peer %s: state Active\n", peer->name); return bgp_send_open(peer); } /* initiate a write */ static int bgp_write(struct bgp_peer *peer) { int len = htons(peer->outbuf->packet.header.len); int r; while ((r = write(peer->sock, &peer->outbuf->packet + peer->outbuf->done, len - peer->outbuf->done)) == -1) { if (errno == EINTR) continue; if (errno == EAGAIN) return 1; if (errno == EPIPE) LOG(1, 0, 0, "Connection to BGP peer %s closed\n", peer->name); else LOG(1, 0, 0, "Can't write to BGP peer %s (%s)\n", peer->name, strerror(errno)); bgp_set_retry(peer); return 0; } if (r < len) { peer->outbuf->done += r; return 1; } LOG(4, 0, 0, "Sent %s to BGP peer %s\n", bgp_msg_type_str(peer->outbuf->packet.header.type), peer->name); peer->outbuf->packet.header.len = 0; peer->outbuf->done = 0; if (peer->state == Established) peer->keepalive_time = time_now + peer->keepalive; if (peer->state != peer->next_state) { if (peer->next_state == Disabled || peer->next_state == Idle) { bgp_clear(peer); return 0; } peer->state = peer->next_state; peer->state_time = time_now; LOG(4, 0, 0, "BGP peer %s: state %s\n", peer->name, bgp_state_str(peer->state)); } return 1; } /* initiate a read */ static int bgp_read(struct bgp_peer *peer) { int r; while ((r = read(peer->sock, &peer->inbuf->packet + peer->inbuf->done, BGP_MAX_PACKET_SIZE - peer->inbuf->done)) < 1) { if (!r) { LOG(1, 0, 0, "Connection to BGP peer %s closed\n", peer->name); } else { if (errno == EINTR) continue; if (errno == EAGAIN) return 1; LOG(1, 0, 0, "Can't read from BGP peer %s (%s)\n", peer->name, strerror(errno)); } bgp_set_retry(peer); return 0; } peer->inbuf->done += r; return 1; } /* process buffered packets */ static int bgp_handle_input(struct bgp_peer *peer) { struct bgp_packet *p = &peer->inbuf->packet; int len = ntohs(p->header.len); if (len > BGP_MAX_PACKET_SIZE) { LOG(1, 0, 0, "Bad header length from BGP %s\n", peer->name); bgp_send_notification(peer, BGP_ERR_HEADER, BGP_ERR_HDR_BAD_LEN); return 0; } if (peer->inbuf->done < len) return 0; LOG(4, 0, 0, "Received %s from BGP peer %s\n", bgp_msg_type_str(p->header.type), peer->name); switch (p->header.type) { case BGP_MSG_OPEN: { struct bgp_data_open data; int hold; int i; off_t param_offset, capability_offset; struct bgp_opt_param *param; uint8_t capabilities_len; char *capabilities = NULL; struct bgp_capability *capability; struct bgp_mp_cap_param *mp_cap; for (i = 0; i < sizeof(p->header.marker); i++) { if ((unsigned char) p->header.marker[i] != 0xff) { LOG(1, 0, 0, "Invalid marker from BGP peer %s\n", peer->name); bgp_send_notification(peer, BGP_ERR_HEADER, BGP_ERR_HDR_NOT_SYNC); return 0; } } if (peer->state != OpenSent) { LOG(1, 0, 0, "OPEN from BGP peer %s in %s state\n", peer->name, bgp_state_str(peer->state)); bgp_send_notification(peer, BGP_ERR_FSM, 0); return 0; } memcpy(&data, p->data, len - sizeof(p->header)); if (data.version != BGP_VERSION) { LOG(1, 0, 0, "Bad version (%d) sent by BGP peer %s\n", (int) data.version, peer->name); bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_VERSION); return 0; } if (ntohs(data.as) != peer->as) { LOG(1, 0, 0, "Bad AS sent by BGP peer %s (got %d, " "expected %d)\n", peer->name, (int) htons(data.as), (int) peer->as); bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_BAD_AS); return 0; } if ((hold = ntohs(data.hold_time)) < 3) { LOG(1, 0, 0, "Bad hold time (%d) from BGP peer %s\n", hold, peer->name); bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_HOLD_TIME); return 0; } /* pick lowest hold time */ if (hold < peer->hold) peer->hold = hold; /* adjust our keepalive based on negotiated hold value */ if (peer->keepalive * 3 > peer->hold) peer->keepalive = peer->hold / 3; /* check for optional parameters */ /* 2 is for the size of type + len (both uint8_t) */ for (param_offset = 0; param_offset < data.opt_len; param_offset += 2 + param->len) { param = (struct bgp_opt_param *)((char *)&data.opt_params + param_offset); /* sensible check */ if (data.opt_len - param_offset < 2 || param->len > data.opt_len - param_offset - 2) { LOG(1, 0, 0, "Malformed Optional Parameter list from BGP peer %s\n", peer->name); bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_UNSPEC); return 0; } /* we know only one parameter type */ if (param->type != BGP_PARAM_TYPE_CAPABILITY) { LOG(1, 0, 0, "Unsupported Optional Parameter type %d from BGP peer %s\n", param->type, peer->name); bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_UNSUP_PARAM); return 0; } capabilities_len = param->len; capabilities = (char *)¶m->value; /* look for BGP multiprotocol capability */ for (capability_offset = 0; capability_offset < capabilities_len; capability_offset += 2 + capability->len) { capability = (struct bgp_capability *)(capabilities + capability_offset); /* sensible check */ if (capabilities_len - capability_offset < 2 || capability->len > capabilities_len - capability_offset - 2) { LOG(1, 0, 0, "Malformed Capabilities list from BGP peer %s\n", peer->name); bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_UNSPEC); return 0; } /* we only know one capability code */ if (capability->code != BGP_CAP_CODE_MP && capability->len != sizeof(struct bgp_mp_cap_param)) { LOG(4, 0, 0, "Unsupported Capability code %d from BGP peer %s\n", capability->code, peer->name); /* we don't terminate, still; we just jump to the next one */ continue; } mp_cap = (struct bgp_mp_cap_param *)&capability->value; /* the only tuple we support */ if (ntohs(mp_cap->afi) != BGP_MP_AFI_IPv6 && mp_cap->safi != BGP_MP_SAFI_UNICAST) { LOG(4, 0, 0, "Unsupported multiprotocol AFI %d and SAFI %d from BGP peer %s\n", mp_cap->afi, mp_cap->safi, peer->name); /* we don't terminate, still; we just jump to the next one */ continue; } /* yes it can! */ peer->mp_handling = HandleIPv6Routes; } } if (peer->mp_handling != HandleIPv6Routes) { peer->mp_handling = DoesntHandleIPv6Routes; if (config->ipv6_prefix.s6_addr[0]) LOG(1, 0, 0, "Warning: BGP peer %s doesn't handle IPv6 prefixes updates\n", peer->name); } /* next transition requires an exchange of keepalives */ bgp_send_keepalive(peer); } break; case BGP_MSG_KEEPALIVE: if (peer->state == OpenConfirm) { peer->state = peer->next_state = Established; peer->state_time = time_now; peer->keepalive_time = time_now + peer->keepalive; peer->update_routes = 1; peer->retry_count = 0; peer->retry_time = 0; LOG(4, 0, 0, "BGP peer %s: state Established\n", peer->name); } break; case BGP_MSG_NOTIFICATION: if (len > sizeof(p->header)) { struct bgp_data_notification *notification = (struct bgp_data_notification *) p->data; if (notification->error_code == BGP_ERR_CEASE) { LOG(4, 0, 0, "BGP peer %s sent CEASE\n", peer->name); bgp_set_retry(peer); return 0; } if (notification->error_code == BGP_ERR_OPEN && notification->error_subcode == BGP_ERR_OPN_UNSUP_PARAM) { LOG(4, 0, 0, "BGP peer %s doesn't support BGP Capabilities\n", peer->name); peer->mp_handling = DoesntHandleIPv6Routes; bgp_set_retry(peer); return 0; } if (notification->error_code == BGP_ERR_OPEN && notification->error_subcode == BGP_ERR_OPN_UNSUP_CAP) { /* the only capability we advertise is this one, so upon receiving an "unsupported capability" message, we disable IPv6 routes for this peer */ LOG(4, 0, 0, "BGP peer %s doesn't support IPv6 routes advertisement\n", peer->name); peer->mp_handling = DoesntHandleIPv6Routes; break; } /* FIXME: should handle more notifications */ LOG(4, 0, 0, "BGP peer %s sent unhandled NOTIFICATION %d\n", peer->name, (int) notification->error_code); } break; } /* reset timer */ peer->expire_time = time_now + peer->hold; /* see if there's another message in the same packet/buffer */ if (peer->inbuf->done > len) { peer->inbuf->done -= len; memmove(p, (char *) p + len, peer->inbuf->done); } else { peer->inbuf->packet.header.len = 0; peer->inbuf->done = 0; } return peer->inbuf->done; } /* send/buffer OPEN message */ static int bgp_send_open(struct bgp_peer *peer) { struct bgp_data_open data; struct bgp_mp_cap_param mp_ipv6 = { htons(BGP_MP_AFI_IPv6), 0, BGP_MP_SAFI_UNICAST }; struct bgp_capability cap_mp_ipv6; struct bgp_opt_param param_cap_mp_ipv6; uint16_t len = sizeof(peer->outbuf->packet.header); memset(peer->outbuf->packet.header.marker, 0xff, sizeof(peer->outbuf->packet.header.marker)); peer->outbuf->packet.header.type = BGP_MSG_OPEN; data.version = BGP_VERSION; data.as = htons(our_as); data.hold_time = htons(peer->hold); /* use the source IP we use as identifier, if available */ if (peer->source_addr != INADDR_ANY) data.identifier = peer->source_addr; else data.identifier = my_address; /* if we know peer doesn't support MP (mp_handling == DoesntHandleIPv6Routes) then don't add this parameter */ if (config->ipv6_prefix.s6_addr[0] && (peer->mp_handling == HandlingUnknown || peer->mp_handling == HandleIPv6Routes)) { /* construct the param and capability */ cap_mp_ipv6.code = BGP_CAP_CODE_MP; cap_mp_ipv6.len = sizeof(mp_ipv6); memcpy(&cap_mp_ipv6.value, &mp_ipv6, cap_mp_ipv6.len); param_cap_mp_ipv6.type = BGP_PARAM_TYPE_CAPABILITY; param_cap_mp_ipv6.len = 2 + sizeof(mp_ipv6); memcpy(¶m_cap_mp_ipv6.value, &cap_mp_ipv6, param_cap_mp_ipv6.len); data.opt_len = 2 + param_cap_mp_ipv6.len; memcpy(&data.opt_params, ¶m_cap_mp_ipv6, data.opt_len); } else data.opt_len = 0; memcpy(peer->outbuf->packet.data, &data, BGP_DATA_OPEN_SIZE + data.opt_len); len += BGP_DATA_OPEN_SIZE + data.opt_len; peer->outbuf->packet.header.len = htons(len); peer->outbuf->done = 0; peer->next_state = OpenSent; return bgp_write(peer); } /* send/buffer KEEPALIVE message */ static int bgp_send_keepalive(struct bgp_peer *peer) { memset(peer->outbuf->packet.header.marker, 0xff, sizeof(peer->outbuf->packet.header.marker)); peer->outbuf->packet.header.type = BGP_MSG_KEEPALIVE; peer->outbuf->packet.header.len = htons(sizeof(peer->outbuf->packet.header)); peer->outbuf->done = 0; peer->next_state = (peer->state == OpenSent) ? OpenConfirm : peer->state; return bgp_write(peer); } /* send/buffer UPDATE message */ static int bgp_send_update(struct bgp_peer *peer) { uint16_t unf_len = 0; uint16_t attr_len; uint16_t len = sizeof(peer->outbuf->packet.header); struct bgp_route_list *have = peer->routes; struct bgp_route_list *want = peer->routing ? bgp_routes : 0; struct bgp_route_list *e = 0; struct bgp_route_list *add = 0; int s; char *data = (char *) &peer->outbuf->packet.data; /* need leave room for attr_len, bgp_path_attrs and one prefix */ char *max = (char *) &peer->outbuf->packet.data + sizeof(peer->outbuf->packet.data) - sizeof(attr_len) - peer->path_attr_len - sizeof(struct bgp_ip_prefix); /* skip over unf_len */ data += sizeof(unf_len); len += sizeof(unf_len); memset(peer->outbuf->packet.header.marker, 0xff, sizeof(peer->outbuf->packet.header.marker)); peer->outbuf->packet.header.type = BGP_MSG_UPDATE; peer->update_routes = 0; /* tentatively clear */ /* find differences */ while ((have || want) && data < (max - sizeof(struct bgp_ip_prefix))) { if (have) s = want ? memcmp(&have->dest, &want->dest, sizeof(have->dest)) : -1; else s = 1; if (s < 0) /* found one to delete */ { struct bgp_route_list *tmp = have; have = have->next; s = BGP_IP_PREFIX_SIZE(tmp->dest); memcpy(data, &tmp->dest, s); data += s; unf_len += s; len += s; LOG(5, 0, 0, "Withdrawing route %s/%d from BGP peer %s\n", fmtaddr(tmp->dest.prefix, 0), tmp->dest.len, peer->name); free(tmp); if (e) e->next = have; else peer->routes = have; } else { if (!s) /* same */ { e = have; /* stash the last found to relink above */ have = have->next; want = want->next; } else if (s > 0) /* addition reqd. */ { if (add) { peer->update_routes = 1; /* only one add per packet */ if (!have) break; } else add = want; if (want) want = want->next; } } } if (have || want) peer->update_routes = 1; /* more to do */ /* anything changed? */ if (!(unf_len || add)) return 1; /* go back and insert unf_len */ unf_len = htons(unf_len); memcpy(&peer->outbuf->packet.data, &unf_len, sizeof(unf_len)); if (add) { if (!(e = malloc(sizeof(*e)))) { LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n", fmtaddr(add->dest.prefix, 0), add->dest.len, strerror(errno)); return 0; } memcpy(e, add, sizeof(*e)); e->next = 0; peer->routes = bgp_insert_route(peer->routes, e); attr_len = htons(peer->path_attr_len); memcpy(data, &attr_len, sizeof(attr_len)); data += sizeof(attr_len); len += sizeof(attr_len); memcpy(data, peer->path_attrs, peer->path_attr_len); data += peer->path_attr_len; len += peer->path_attr_len; s = BGP_IP_PREFIX_SIZE(add->dest); memcpy(data, &add->dest, s); data += s; len += s; LOG(5, 0, 0, "Advertising route %s/%d to BGP peer %s\n", fmtaddr(add->dest.prefix, 0), add->dest.len, peer->name); } else { attr_len = 0; memcpy(data, &attr_len, sizeof(attr_len)); data += sizeof(attr_len); len += sizeof(attr_len); } peer->outbuf->packet.header.len = htons(len); peer->outbuf->done = 0; return bgp_write(peer); } /* send/buffer UPDATE message for IPv6 routes */ static int bgp_send_update6(struct bgp_peer *peer) { uint16_t attr_len; uint16_t unreach_len = 0; char *unreach_len_pos; uint8_t reach_len; uint16_t len = sizeof(peer->outbuf->packet.header); struct bgp_route6_list *have = peer->routes6; struct bgp_route6_list *want = peer->routing ? bgp_routes6 : 0; struct bgp_route6_list *e = 0; struct bgp_route6_list *add = 0; int s; char ipv6addr[INET6_ADDRSTRLEN]; char *data = (char *) &peer->outbuf->packet.data; /* need leave room for attr_len, bgp_path_attrs and one prefix */ char *max = (char *) &peer->outbuf->packet.data + sizeof(peer->outbuf->packet.data) - sizeof(attr_len) - peer->path_attr_len_without_nexthop - BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE - sizeof(struct bgp_ip6_prefix); memset(peer->outbuf->packet.header.marker, 0xff, sizeof(peer->outbuf->packet.header.marker)); peer->outbuf->packet.header.type = BGP_MSG_UPDATE; /* insert non-MP unfeasible routes length */ memcpy(data, &unreach_len, sizeof(unreach_len)); /* skip over it and attr_len too; it will be filled when known */ data += sizeof(unreach_len) + sizeof(attr_len); len += sizeof(unreach_len) + sizeof(attr_len); /* copy usual attributes */ memcpy(data, peer->path_attrs, peer->path_attr_len_without_nexthop); data += peer->path_attr_len_without_nexthop; attr_len = peer->path_attr_len_without_nexthop; /* copy MP unreachable NLRI heading */ memcpy(data, peer->mp_unreach_nlri_partial, BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE); /* remember where to update this attr len */ unreach_len_pos = data + 2; data += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; attr_len += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; peer->update_routes6 = 0; /* tentatively clear */ /* find differences */ while ((have || want) && data < (max - sizeof(struct bgp_ip6_prefix))) { if (have) s = want ? memcmp(&have->dest, &want->dest, sizeof(have->dest)) : -1; else s = 1; if (s < 0) /* found one to delete */ { struct bgp_route6_list *tmp = have; have = have->next; s = BGP_IP_PREFIX_SIZE(tmp->dest); memcpy(data, &tmp->dest, s); data += s; unreach_len += s; attr_len += s; LOG(5, 0, 0, "Withdrawing route %s/%d from BGP peer %s\n", inet_ntop(AF_INET6, &tmp->dest.prefix, ipv6addr, INET6_ADDRSTRLEN), tmp->dest.len, peer->name); free(tmp); if (e) e->next = have; else peer->routes6 = have; } else { if (!s) /* same */ { e = have; /* stash the last found to relink above */ have = have->next; want = want->next; } else if (s > 0) /* addition reqd. */ { if (add) { peer->update_routes6 = 1; /* only one add per packet */ if (!have) break; } else add = want; if (want) want = want->next; } } } if (have || want) peer->update_routes6 = 1; /* more to do */ /* anything changed? */ if (!(unreach_len || add)) return 1; if (unreach_len) { /* go back and insert MP unreach_len */ unreach_len += sizeof(struct bgp_attr_mp_unreach_nlri_partial); unreach_len = htons(unreach_len); memcpy(unreach_len_pos, &unreach_len, sizeof(unreach_len)); } else { /* we can remove this attribute, then */ data -= BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; attr_len -= BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE; } if (add) { if (!(e = malloc(sizeof(*e)))) { LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n", inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN), add->dest.len, strerror(errno)); return 0; } memcpy(e, add, sizeof(*e)); e->next = 0; peer->routes6 = bgp_insert_route6(peer->routes6, e); /* copy MP reachable NLRI heading */ memcpy(data, peer->mp_reach_nlri_partial, BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE); /* with proper len */ reach_len = BGP_IP_PREFIX_SIZE(add->dest); data[2] = sizeof(struct bgp_attr_mp_reach_nlri_partial) + reach_len; data += BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE; attr_len += BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE; memcpy(data, &add->dest, reach_len); data += reach_len; attr_len += reach_len; LOG(5, 0, 0, "Advertising route %s/%d to BGP peer %s\n", inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN), add->dest.len, peer->name); } /* update len with attributes we added */ len += attr_len; /* go back and insert attr_len */ attr_len = htons(attr_len); memcpy((char *)&peer->outbuf->packet.data + 2, &attr_len, sizeof(attr_len)); peer->outbuf->packet.header.len = htons(len); peer->outbuf->done = 0; return bgp_write(peer); } /* send/buffer NOTIFICATION message */ static int bgp_send_notification(struct bgp_peer *peer, uint8_t code, uint8_t subcode) { struct bgp_data_notification data; uint16_t len = 0; data.error_code = code; len += sizeof(data.error_code); data.error_subcode = subcode; len += sizeof(data.error_code); memset(peer->outbuf->packet.header.marker, 0xff, sizeof(peer->outbuf->packet.header.marker)); peer->outbuf->packet.header.type = BGP_MSG_NOTIFICATION; peer->outbuf->packet.header.len = htons(sizeof(peer->outbuf->packet.header) + len); memcpy(peer->outbuf->packet.data, &data, len); peer->outbuf->done = 0; peer->next_state = code == BGP_ERR_CEASE ? Disabled : Idle; /* we're dying; ignore any pending input */ peer->inbuf->packet.header.len = 0; peer->inbuf->done = 0; return bgp_write(peer); } l2tpns-2.3.3/bgp.h000066400000000000000000000227421400724550600136740ustar00rootroot00000000000000/* 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_opt_param { uint8_t type; uint8_t len; #define BGP_MAX_OPT_PARAM_SIZE 256 char value[BGP_MAX_OPT_PARAM_SIZE]; } __attribute__ ((packed)); #define BGP_PARAM_TYPE_CAPABILITY 2 struct bgp_capability { uint8_t code; uint8_t len; #define BGP_MAX_CAPABILITY_SIZE 256 char value[BGP_MAX_CAPABILITY_SIZE]; } __attribute__ ((packed)); /* RFC4760 Multiprotocol extension */ #define BGP_CAP_CODE_MP 1 struct bgp_mp_cap_param { uint16_t afi; /* sa_family_t */ uint8_t reserved; /* SHOULD be 0 */ uint8_t safi; } __attribute__ ((packed)); /* bgp_mp_cap_param.afi */ #define BGP_MP_AFI_RESERVED 0 #define BGP_MP_AFI_IPv4 1 #define BGP_MP_AFI_IPv6 2 /* bgp_mp_cap_param.safi */ #define BGP_MP_SAFI_UNICAST 1 #define BGP_MP_SAFI_MULTICAST 2 struct bgp_ip6_prefix { uint8_t len; uint8_t prefix[16]; /* variable */ } __attribute__ ((packed)); /* end of RFC4760 specific definitions */ struct bgp_ip_prefix { uint8_t len; uint32_t prefix; /* variable */ } __attribute__ ((packed)); /* works for both IPv4 and IPv6 prefixes */ #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)); struct bgp_attr_mp_reach_nlri_partial { uint16_t afi; /* sa_family_t */ uint8_t safi; uint8_t next_hop_len; uint8_t next_hop[16]; uint8_t reserved; } __attribute__ ((packed)); #define BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE (3 + sizeof(struct bgp_attr_mp_reach_nlri_partial)) struct bgp_attr_mp_unreach_nlri_partial { uint16_t afi; /* sa_family_t */ uint8_t safi; } __attribute__ ((packed)); /* we use it as an extended attribute */ #define BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE (4 + sizeof(struct bgp_attr_mp_unreach_nlri_partial)) /* 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_CODE_MP_REACH_NLRI 14 /* optional, non-transitive (RFC4760) */ #define BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI 15 /* optional, non-transitive (RFC4760) */ #define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \ ? ((p).data.e.len + 4) : (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_UNSPEC 0 #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_OPN_UNSUP_CAP 7 #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_route6_list { struct bgp_ip6_prefix dest; struct bgp_route6_list *next; }; 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 */ }; enum bgp_mp_handling { HandleIPv6Routes, DoesntHandleIPv6Routes, HandlingUnknown, }; /* state */ struct bgp_peer { char name[32]; /* peer name */ in_addr_t addr; /* peer address */ in_addr_t source_addr; /* our source 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 */ int path_attr_len_without_nexthop; /* length of path attrs without NEXT_HOP */ uint32_t events; /* events to poll */ struct event_data edata; /* poll data */ enum bgp_mp_handling mp_handling; /* how it handles IPv6 routes advertisements */ int update_routes6; /* UPDATE required for IPv6 routes */ struct bgp_route6_list *routes6; /* IPv6 routes known by this peer */ char mp_reach_nlri_partial[BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE]; char mp_unreach_nlri_partial[BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE]; }; /* 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, struct in_addr update_source, 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, int prefixlen); int bgp_add_route6(struct in6_addr ip, int prefixlen); int bgp_del_route(in_addr_t ip, int prefixlen); int bgp_del_route6(struct in6_addr ip, int prefixlen); void bgp_enable_routing(int enable); int bgp_set_poll(void); int bgp_process(uint32_t events[]); void bgp_process_peers_timers(); char const *bgp_state_str(enum bgp_state state); #endif /* __BGP_H__ */ l2tpns-2.3.3/cli.c000066400000000000000000002444251400724550600136720ustar00rootroot00000000000000// L2TPNS Command Line Interface // vim: sw=8 ts=8 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "constants.h" #include "util.h" #include "cluster.h" #include "tbf.h" #include "ll.h" #ifdef BGP #include "bgp.h" #endif #include "l2tplac.h" extern tunnelt *tunnel; extern bundlet *bundle; extern sessiont *session; extern radiust *radius; extern ippoolt *ip_address_pool; extern struct Tstats *_statistics; static struct cli_def *cli = NULL; extern configt *config; extern config_descriptt config_values[]; #ifdef RINGBUFFER extern struct Tringbuffer *ringbuffer; #endif extern struct cli_session_actions *cli_session_actions; extern struct cli_tunnel_actions *cli_tunnel_actions; extern tbft *filter_list; extern ip_filtert *ip_filters; struct { char critical; char error; char warning; char info; char calls; char data; } debug_flags; #ifdef RINGBUFFER static int debug_rb_tail; static char *debug_levels[] = { "CRIT", "ERROR", "WARN", "INFO", "CALL", "DATA", }; #endif static int cmd_show_session(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_tunnels(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_users(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_radius(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_version(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_pool(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_run(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_banana(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_plugins(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_throttle(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_write_memory(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_drop_user(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_drop_tunnel(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_drop_session(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_snoop(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_no_snoop(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_throttle(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_no_throttle(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_debug(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_no_debug(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_set(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_load_plugin(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_remove_plugin(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_uptime(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_shutdown(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_reload(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_setforward(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_rmtlnsconf(struct cli_def *cli, const char *command, char **argv, int argc); static int regular_stuff(struct cli_def *cli); #ifdef STATISTICS static int cmd_show_counters(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_clear_counters(struct cli_def *cli, const char *command, char **argv, int argc); #endif /* STATISTICS */ #ifdef BGP #define MODE_CONFIG_BGP 8 static int cmd_router_bgp(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_router_bgp_neighbour(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_router_bgp_no_neighbour(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_bgp(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_no_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_restart_bgp(struct cli_def *cli, const char *command, char **argv, int argc); #endif /* BGP */ #define MODE_CONFIG_NACL 9 static int cmd_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_no_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_ip_access_list_rule(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_filter(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_no_filter(struct cli_def *cli, const char *command, char **argv, int argc); static int cmd_show_access_list(struct cli_def *cli, const char *command, char **argv, int argc); /* match if b is a substr of a */ #define MATCH(a,b) (!strncmp((a), (b), strlen(b))) void init_cli() { FILE *f; char buf[4096]; struct cli_command *c; struct cli_command *c2; cli = cli_init(); c = cli_register_command(cli, NULL, "show", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "banana", cmd_show_banana, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a banana"); #ifdef BGP cli_register_command(cli, c, "bgp", cmd_show_bgp, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show BGP status"); #endif /* BGP */ cli_register_command(cli, c, "cluster", cmd_show_cluster, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show cluster information"); cli_register_command(cli, c, "ipcache", cmd_show_ipcache, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show contents of the IP cache"); cli_register_command(cli, c, "plugins", cmd_show_plugins, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all installed plugins"); cli_register_command(cli, c, "pool", cmd_show_pool, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the IP address allocation pool"); cli_register_command(cli, c, "radius", cmd_show_radius, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show active radius queries"); cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show the currently running configuration"); cli_register_command(cli, c, "remotelns-conf", cmd_show_rmtlnsconf, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show a list of remote LNS configuration"); cli_register_command(cli, c, "session", cmd_show_session, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of sessions or details for a single session"); cli_register_command(cli, c, "tbf", cmd_show_tbf, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all token bucket filters in use"); cli_register_command(cli, c, "throttle", cmd_show_throttle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all throttled sessions and associated TBFs"); cli_register_command(cli, c, "tunnels", cmd_show_tunnels, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of tunnels or details for a single tunnel"); cli_register_command(cli, c, "users", cmd_show_users, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of all connected users or details of selected user"); cli_register_command(cli, c, "version", cmd_show_version, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show currently running software version"); cli_register_command(cli, c, "access-list", cmd_show_access_list, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show named access-list"); c2 = cli_register_command(cli, c, "histogram", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c2, "idle", cmd_show_hist_idle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show histogram of session idle times"); cli_register_command(cli, c2, "open", cmd_show_hist_open, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show histogram of session durations"); #ifdef STATISTICS cli_register_command(cli, c, "counters", cmd_show_counters, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Display all the internal counters and running totals"); c = cli_register_command(cli, NULL, "clear", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "counters", cmd_clear_counters, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Clear internal counters"); #endif cli_register_command(cli, NULL, "uptime", cmd_uptime, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show uptime and bandwidth utilisation"); cli_register_command(cli, NULL, "shutdown", cmd_shutdown, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Shutdown l2tpns daemon and exit"); cli_register_command(cli, NULL, "reload", cmd_reload, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Reload configuration"); c = cli_register_command(cli, NULL, "write", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "memory", cmd_write_memory, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Save the running config to flash"); cli_register_command(cli, c, "terminal", cmd_show_run, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the running config"); cli_register_command(cli, NULL, "snoop", cmd_snoop, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Enable interception of a session"); cli_register_command(cli, NULL, "throttle", cmd_throttle, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Enable throttling of a session"); cli_register_command(cli, NULL, "filter", cmd_filter, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Add filtering to a session"); cli_register_command(cli, NULL, "debug", cmd_debug, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Set the level of logging that is shown on the console"); #ifdef BGP c = cli_register_command(cli, NULL, "suspend", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "bgp", cmd_suspend_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Withdraw routes from BGP neighbour"); #endif /* BGP */ c = cli_register_command(cli, NULL, "no", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "snoop", cmd_no_snoop, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disable interception of a session"); cli_register_command(cli, c, "throttle", cmd_no_throttle, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disable throttling of a session"); cli_register_command(cli, c, "filter", cmd_no_filter, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Remove filtering from a session"); cli_register_command(cli, c, "debug", cmd_no_debug, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Turn off logging of a certain level of debugging"); #ifdef BGP c2 = cli_register_command(cli, c, "suspend", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c2, "bgp", cmd_no_suspend_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Advertise routes to BGP neighbour"); c = cli_register_command(cli, NULL, "restart", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "bgp", cmd_restart_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Restart BGP"); c = cli_register_command(cli, NULL, "router", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); cli_register_command(cli, c, "bgp", cmd_router_bgp, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Configure BGP"); cli_register_command(cli, NULL, "neighbour", cmd_router_bgp_neighbour, PRIVILEGE_PRIVILEGED, MODE_CONFIG_BGP, "Configure BGP neighbour"); c = cli_register_command(cli, NULL, "no", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG_BGP, NULL); cli_register_command(cli, c, "neighbour", cmd_router_bgp_no_neighbour, PRIVILEGE_PRIVILEGED, MODE_CONFIG_BGP, "Remove BGP neighbour"); #endif /* BGP */ c = cli_register_command(cli, NULL, "drop", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "user", cmd_drop_user, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disconnect a user"); cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disconnect a tunnel and all sessions on that tunnel"); cli_register_command(cli, c, "session", cmd_drop_session, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disconnect a session"); c = cli_register_command(cli, NULL, "load", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); cli_register_command(cli, c, "plugin", cmd_load_plugin, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Load a plugin"); c = cli_register_command(cli, NULL, "remove", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); cli_register_command(cli, c, "plugin", cmd_remove_plugin, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Remove a plugin"); cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set a configuration variable"); cli_register_command(cli, NULL, "setforward", cmd_setforward, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set the Remote LNS Forward"); c = cli_register_command(cli, NULL, "ip", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); cli_register_command(cli, c, "access-list", cmd_ip_access_list, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Add named access-list"); cli_register_command(cli, NULL, "permit", cmd_ip_access_list_rule, PRIVILEGE_PRIVILEGED, MODE_CONFIG_NACL, "Permit rule"); cli_register_command(cli, NULL, "deny", cmd_ip_access_list_rule, PRIVILEGE_PRIVILEGED, MODE_CONFIG_NACL, "Deny rule"); c = cli_register_command(cli, NULL, "no", NULL, PRIVILEGE_UNPRIVILEGED, MODE_CONFIG, NULL); c2 = cli_register_command(cli, c, "ip", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); cli_register_command(cli, c2, "access-list", cmd_no_ip_access_list, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Remove named access-list"); // Enable regular processing cli_regular(cli, regular_stuff); if (!(f = fopen(CLIUSERS, "r"))) { LOG(0, 0, 0, "WARNING! No users specified. Command-line access is open to all\n"); } else { while (fgets(buf, 4096, f)) { char *p; if (*buf == '#') continue; if ((p = strchr(buf, '\r'))) *p = 0; if ((p = strchr(buf, '\n'))) *p = 0; if (!*buf) continue; if (!(p = strchr((char *)buf, ':'))) continue; *p++ = 0; if (!strcmp(buf, "enable")) { cli_allow_enable(cli, p); LOG(3, 0, 0, "Setting enable password\n"); } else { cli_allow_user(cli, buf, p); LOG(3, 0, 0, "Allowing user %s to connect to the CLI\n", buf); } } fclose(f); } } void cli_init_complete(char *hostname) { int on = 1; struct sockaddr_in addr; if (hostname && *hostname) cli_set_hostname(cli, hostname); else cli_set_hostname(cli, "l2tpns"); memset(&addr, 0, sizeof(addr)); clifd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt(clifd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); { int flags; // Set cli fd as non-blocking flags = fcntl(clifd, F_GETFL, 0); fcntl(clifd, F_SETFL, flags | O_NONBLOCK); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = config->cli_bind_address; /* defaults to INADDR_ANY */ addr.sin_port = htons(23); if (bind(clifd, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error binding cli on port 23: %s\n", strerror(errno)); close(clifd); clifd = -1; return; } if (listen(clifd, 10) < 0) { LOG(0, 0, 0, "Error listening on cli port 23: %s\n", strerror(errno)); close(clifd); clifd = -1; return; } } void cli_do(int sockfd) { int require_auth = 1; struct sockaddr_in addr; socklen_t l = sizeof(addr); if (fork_and_close()) return; if (getpeername(sockfd, (struct sockaddr *) &addr, &l) == 0) { require_auth = addr.sin_addr.s_addr != inet_addr("127.0.0.1"); LOG(require_auth ? 3 : 4, 0, 0, "Accepted connection to CLI from %s\n", fmtaddr(addr.sin_addr.s_addr, 0)); } else LOG(0, 0, 0, "getpeername() failed on cli socket. Requiring authentication: %s\n", strerror(errno)); if (require_auth) { LOG(3, 0, 0, "CLI is remote, requiring authentication\n"); if (!cli->users) /* paranoia */ { LOG(0, 0, 0, "No users for remote authentication! Exiting CLI\n"); exit(0); } } else { /* no username/pass required */ cli->users = 0; } #ifdef RINGBUFFER debug_rb_tail = ringbuffer->tail; #endif memset(&debug_flags, 0, sizeof(debug_flags)); debug_flags.critical = 1; cli_loop(cli, sockfd); close(sockfd); LOG(require_auth ? 3 : 4, 0, 0, "Closed CLI connection from %s\n", fmtaddr(addr.sin_addr.s_addr, 0)); exit(0); } static void cli_print_log(struct cli_def *cli, const char *string) { LOG(3, 0, 0, "%s\n", string); } void cli_do_file(FILE *fh) { LOG(3, 0, 0, "Reading configuration file\n"); cli_print_callback(cli, cli_print_log); cli_file(cli, fh, PRIVILEGE_PRIVILEGED, MODE_CONFIG); cli_print_callback(cli, NULL); } int cli_arg_help(struct cli_def *cli, int cr_ok, char *entry, ...) { va_list ap; char *desc; char buf[16]; char *p; va_start(ap, entry); while (entry) { /* allow one %d */ if ((p = strchr(entry, '%')) && !strchr(p+1, '%') && p[1] == 'd') { int v = va_arg(ap, int); snprintf(buf, sizeof(buf), entry, v); p = buf; } else p = entry; desc = va_arg(ap, char *); if (desc && *desc) cli_error(cli, " %-20s %s", p, desc); else cli_error(cli, " %s", p); entry = desc ? va_arg(ap, char *) : 0; } va_end(ap); if (cr_ok) cli_error(cli, " "); return CLI_OK; } static int cmd_show_session(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "<1-%d>", MAXSESSION-1, "Show specific session by id", NULL); time(&time_now); if (argc > 0) { // Show individual session for (i = 0; i < argc; i++) { unsigned int s, b_in, b_out; s = atoi(argv[i]); if (s <= 0 || s >= MAXSESSION) { cli_print(cli, "Invalid session id \"%s\"", argv[i]); continue; } cli_print(cli, "\r\nSession %d:", s); cli_print(cli, "\tUser:\t\t%s", session[s].user[0] ? session[s].user : "none"); cli_print(cli, "\tCalling Num:\t%s", session[s].calling); cli_print(cli, "\tCalled Num:\t%s", session[s].called); cli_print(cli, "\tTunnel ID:\t%d", session[s].tunnel); cli_print(cli, "\tPPP Phase:\t%s", ppp_phase(session[s].ppp.phase)); switch (session[s].ppp.phase) { case Establish: cli_print(cli, "\t LCP state:\t%s", ppp_state(session[s].ppp.lcp)); break; case Authenticate: case Network: cli_print(cli, "\t IPCP state:\t%s", ppp_state(session[s].ppp.ipcp)); cli_print(cli, "\t IPV6CP state:\t%s", ppp_state(session[s].ppp.ipv6cp)); cli_print(cli, "\t CCP state:\t%s", ppp_state(session[s].ppp.ccp)); } cli_print(cli, "\tIP address:\t%s", fmtaddr(htonl(session[s].ip), 0)); cli_print(cli, "\tUnique SID:\t%u", session[s].unique_id); cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0); cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0); if (session[s].session_timeout) { clockt opened = session[s].opened; if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) opened = bundle[session[s].bundle].online_time; cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (opened ? abs(time_now - opened) : 0)); } if (session[s].idle_timeout) cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0)); if (session[s].timeout) { cli_print(cli, "\tRemaining time:\t%u", (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) ? (unsigned) (session[s].timeout - bundle[session[s].bundle].online_time) : (unsigned) (session[s].timeout - (time_now - session[s].opened))); } cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin); cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin); cli_print(cli, "\tMRU:\t\t%d", session[s].mru); cli_print(cli, "\tRx Speed:\t%u", session[s].rx_connect_speed); cli_print(cli, "\tTx Speed:\t%u", session[s].tx_connect_speed); if (session[s].filter_in && session[s].filter_in <= MAXFILTER) cli_print(cli, "\tFilter in:\t%u (%s)", session[s].filter_in, ip_filters[session[s].filter_in - 1].name); if (session[s].filter_out && session[s].filter_out <= MAXFILTER) cli_print(cli, "\tFilter out:\t%u (%s)", session[s].filter_out, ip_filters[session[s].filter_out - 1].name); if (session[s].snoop_ip && session[s].snoop_port) cli_print(cli, "\tIntercepted:\t%s:%d", fmtaddr(session[s].snoop_ip, 0), session[s] .snoop_port); else cli_print(cli, "\tIntercepted:\tno"); cli_print(cli, "\tWalled Garden:\t%s", session[s].walled_garden ? "YES" : "no"); { int t = (session[s].throttle_in || session[s].throttle_out); cli_print(cli, "\tThrottled:\t%s%s%.0d%s%s%.0d%s%s", t ? "YES" : "no", t ? " (" : "", session[s].throttle_in, session[s].throttle_in ? "kbps" : t ? "-" : "", t ? "/" : "", session[s].throttle_out, session[s].throttle_out ? "kbps" : t ? "-" : "", t ? ")" : ""); } b_in = session[s].tbf_in; b_out = session[s].tbf_out; if (b_in || b_out) cli_print(cli, "\t\t\t%5s %6s %6s | %7s %7s %8s %8s %8s %8s", "Rate", "Credit", "Queued", "ByteIn", "PackIn", "ByteSent", "PackSent", "PackDrop", "PackDelay"); if (b_in) cli_print(cli, "\tTBFI#%d%1s%s\t%5d %6d %6d | %7d %7d %8d %8d %8d %8d", b_in, (filter_list[b_in].next ? "*" : " "), (b_in < 100 ? "\t" : ""), filter_list[b_in].rate * 8, filter_list[b_in].credit, filter_list[b_in].queued, filter_list[b_in].b_queued, filter_list[b_in].p_queued, filter_list[b_in].b_sent, filter_list[b_in].p_sent, filter_list[b_in].p_dropped, filter_list[b_in].p_delayed); if (b_out) cli_print(cli, "\tTBFO#%d%1s%s\t%5d %6d %6d | %7d %7d %8d %8d %8d %8d", b_out, (filter_list[b_out].next ? "*" : " "), (b_out < 100 ? "\t" : ""), filter_list[b_out].rate * 8, filter_list[b_out].credit, filter_list[b_out].queued, filter_list[b_out].b_queued, filter_list[b_out].p_queued, filter_list[b_out].b_sent, filter_list[b_out].p_sent, filter_list[b_out].p_dropped, filter_list[b_out].p_delayed); } return CLI_OK; } // Show Summary cli_print(cli, "%5s %7s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-18s %s", "SID", "LkToSID", "TID", "Username", "IP", "I", "T", "G", "6", "opened", "downloaded", "uploaded", "idle", "Rem.Time", "LAC(L)/RLNS(R)/PPPOE(P)", "CLI"); for (i = 1; i < MAXSESSION; i++) { uint32_t rem_time; if (!session[i].opened) continue; if (session[i].bundle && bundle[session[i].bundle].num_of_links > 1) rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0; else rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0; cli_print(cli, "%5d %7d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-20s %s", i, session[i].forwardtosession, session[i].tunnel, session[i].user[0] ? session[i].user : "*", fmtaddr(htonl(session[i].ip), 0), (session[i].snoop_ip && session[i].snoop_port) ? "Y" : "N", (session[i].throttle_in || session[i].throttle_out) ? "Y" : "N", (session[i].walled_garden) ? "Y" : "N", (session[i].ppp.ipv6cp == Opened) ? "Y" : "N", abs(time_now - (unsigned long)session[i].opened), (unsigned long)session[i].cout, (unsigned long)session[i].cin, abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)), (unsigned long)(rem_time), (session[i].tunnel == TUNNEL_ID_PPPOE)?"(P)":(tunnel[session[i].tunnel].isremotelns?"(R)":"(L)"), (session[i].tunnel == TUNNEL_ID_PPPOE)?fmtMacAddr(session[i].src_hwaddr):fmtaddr(htonl(tunnel[session[i].tunnel].ip), 1), session[i].calling[0] ? session[i].calling : "*"); } return CLI_OK; } static int cmd_show_tunnels(struct cli_def *cli, const char *command, char **argv, int argc) { int i, x, show_all = 0; char *states[] = { "Free", "Open", "Closing", "Opening", }; if (CLI_HELP_REQUESTED) { if (argc > 1) return cli_arg_help(cli, 1, "<1-%d>", MAXTUNNEL-1, "Show specific tunnel by id", NULL); return cli_arg_help(cli, 1, "all", "Show all tunnels, including unused", "<1-%d>", MAXTUNNEL-1, "Show specific tunnel by id", NULL); } time(&time_now); if (argc > 0) { if (strcmp(argv[0], "all") == 0) { show_all = 1; } else { // Show individual tunnel for (i = 0; i < argc; i++) { char s[65535] = {0}; unsigned int t; t = atoi(argv[i]); if (t <= 0 || t >= MAXTUNNEL) { cli_print(cli, "Invalid tunnel id \"%s\"", argv[i]); continue; } cli_print(cli, "\r\nTunnel %d:", t); cli_print(cli, "\tState:\t\t%s", states[tunnel[t].state]); cli_print(cli, "\tHostname:\t%s", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)"); cli_print(cli, "\tRemote IP:\t%s", fmtaddr(htonl(tunnel[t].ip), 0)); cli_print(cli, "\tRemote Port:\t%d", tunnel[t].port); cli_print(cli, "\tRx Window:\t%u", tunnel[t].window); cli_print(cli, "\tNext Recv:\t%u", tunnel[t].nr); cli_print(cli, "\tNext Send:\t%u", tunnel[t].ns); cli_print(cli, "\tQueue Len:\t%u", tunnel[t].controlc); cli_print(cli, "\tLast Packet Age:%u", (unsigned)(time_now - tunnel[t].last)); for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == t && session[x].opened && !session[x].die) sprintf(s, "%s%u ", s, x); cli_print(cli, "\tSessions:\t%s", s); } return CLI_OK; } } // Show tunnel summary cli_print(cli, "%4s %20s %20s %6s %s", "TID", "Hostname", "IP", "State", "Sessions"); for (i = 1; i < MAXTUNNEL; i++) { int sessions = 0; if (!show_all && (!tunnel[i].ip || tunnel[i].die)) continue; for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++; cli_print(cli, "%4d %20s %20s %6s %6d %s", i, *tunnel[i].hostname ? tunnel[i].hostname : "(null)", fmtaddr(htonl(tunnel[i].ip), 0), states[tunnel[i].state], sessions ,(i == TUNNEL_ID_PPPOE)?"Tunnel pppoe":(tunnel[i].isremotelns?"Tunnel To Remote LNS":"Tunnel To LAC") ); } return CLI_OK; } static int cmd_show_users(struct cli_def *cli, const char *command, char **argv, int argc) { char sid[32][8]; char *sargv[32]; int sargc = 0; int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "USER", "Show details for specific username", NULL); for (i = 0; i < MAXSESSION; i++) { if (!session[i].opened) continue; if (!session[i].user[0]) continue; if (argc > 0) { int j; for (j = 0; j < argc && sargc < 32; j++) { if (strcmp(argv[j], session[i].user) == 0) { snprintf(sid[sargc], sizeof(sid[0]), "%d", i); sargv[sargc] = sid[sargc]; sargc++; } } continue; } cli_print(cli, "%s", session[i].user); } if (sargc > 0) return cmd_show_session(cli, "users", sargv, sargc); return CLI_OK; } #ifdef STATISTICS static int cmd_show_counters(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "%-10s %10s %10s %10s %10s", "Ethernet", "Bytes", "Packets", "Errors", "Dropped"); cli_print(cli, "%-10s %10u %10u %10u %10u", "RX", GET_STAT(tun_rx_bytes), GET_STAT(tun_rx_packets), GET_STAT(tun_rx_errors), GET_STAT(tun_rx_dropped)); cli_print(cli, "%-10s %10u %10u %10u", "TX", GET_STAT(tun_tx_bytes), GET_STAT(tun_tx_packets), GET_STAT(tun_tx_errors)); cli_print(cli, " "); cli_print(cli, "%-10s %10s %10s %10s %10s", "Tunnel", "Bytes", "Packets", "Errors", "Retries"); cli_print(cli, "%-10s %10u %10u %10u", "RX", GET_STAT(tunnel_rx_bytes), GET_STAT(tunnel_rx_packets), GET_STAT(tunnel_rx_errors)); cli_print(cli, "%-10s %10u %10u %10u %10u", "TX", GET_STAT(tunnel_tx_bytes), GET_STAT(tunnel_tx_packets), GET_STAT(tunnel_tx_errors), GET_STAT(tunnel_retries)); cli_print(cli, " "); cli_print(cli, "%-30s%-10s", "Counter", "Value"); cli_print(cli, "-----------------------------------------"); cli_print(cli, "%-30s%u", "radius_retries", GET_STAT(radius_retries)); cli_print(cli, "%-30s%u", "arp_sent", GET_STAT(arp_sent)); cli_print(cli, "%-30s%u", "packets_snooped", GET_STAT(packets_snooped)); cli_print(cli, "%-30s%u", "tunnel_created", GET_STAT(tunnel_created)); cli_print(cli, "%-30s%u", "session_created", GET_STAT(session_created)); cli_print(cli, "%-30s%u", "tunnel_timeout", GET_STAT(tunnel_timeout)); cli_print(cli, "%-30s%u", "session_timeout", GET_STAT(session_timeout)); cli_print(cli, "%-30s%u", "radius_timeout", GET_STAT(radius_timeout)); cli_print(cli, "%-30s%u", "radius_overflow", GET_STAT(radius_overflow)); cli_print(cli, "%-30s%u", "tunnel_overflow", GET_STAT(tunnel_overflow)); cli_print(cli, "%-30s%u", "session_overflow", GET_STAT(session_overflow)); cli_print(cli, "%-30s%u", "ip_allocated", GET_STAT(ip_allocated)); cli_print(cli, "%-30s%u", "ip_freed", GET_STAT(ip_freed)); cli_print(cli, "%-30s%u", "cluster_forwarded", GET_STAT(c_forwarded)); cli_print(cli, "%-30s%u", "recv_forward", GET_STAT(recv_forward)); cli_print(cli, "%-30s%u", "select_called", GET_STAT(select_called)); cli_print(cli, "%-30s%u", "multi_read_used", GET_STAT(multi_read_used)); cli_print(cli, "%-30s%u", "multi_read_exceeded", GET_STAT(multi_read_exceeded)); #ifdef STAT_CALLS cli_print(cli, "\n%-30s%-10s", "Counter", "Value"); cli_print(cli, "-----------------------------------------"); cli_print(cli, "%-30s%u", "call_processtun", GET_STAT(call_processtun)); cli_print(cli, "%-30s%u", "call_processipout", GET_STAT(call_processipout)); cli_print(cli, "%-30s%u", "call_processipv6out", GET_STAT(call_processipv6out)); cli_print(cli, "%-30s%u", "call_processudp", GET_STAT(call_processudp)); cli_print(cli, "%-30s%u", "call_processpap", GET_STAT(call_processpap)); cli_print(cli, "%-30s%u", "call_processchap", GET_STAT(call_processchap)); cli_print(cli, "%-30s%u", "call_processlcp", GET_STAT(call_processlcp)); cli_print(cli, "%-30s%u", "call_processipcp", GET_STAT(call_processipcp)); cli_print(cli, "%-30s%u", "call_processipv6cp", GET_STAT(call_processipv6cp)); cli_print(cli, "%-30s%u", "call_processipin", GET_STAT(call_processipin)); cli_print(cli, "%-30s%u", "call_processipv6in", GET_STAT(call_processipv6in)); cli_print(cli, "%-30s%u", "call_processccp", GET_STAT(call_processccp)); cli_print(cli, "%-30s%u", "call_processrad", GET_STAT(call_processrad)); cli_print(cli, "%-30s%u", "call_sendarp", GET_STAT(call_sendarp)); cli_print(cli, "%-30s%u", "call_sendipcp", GET_STAT(call_sendipcp)); cli_print(cli, "%-30s%u", "call_sendchap", GET_STAT(call_sendchap)); cli_print(cli, "%-30s%u", "call_sessionbyip", GET_STAT(call_sessionbyip)); cli_print(cli, "%-30s%u", "call_sessionbyipv6", GET_STAT(call_sessionbyipv6)); cli_print(cli, "%-30s%u", "call_sessionbyuser", GET_STAT(call_sessionbyuser)); cli_print(cli, "%-30s%u", "call_tunnelsend", GET_STAT(call_tunnelsend)); cli_print(cli, "%-30s%u", "call_tunnelkill", GET_STAT(call_tunnelkill)); cli_print(cli, "%-30s%u", "call_tunnelshutdown", GET_STAT(call_tunnelshutdown)); cli_print(cli, "%-30s%u", "call_sessionkill", GET_STAT(call_sessionkill)); cli_print(cli, "%-30s%u", "call_sessionshutdown", GET_STAT(call_sessionshutdown)); cli_print(cli, "%-30s%u", "call_sessionsetup", GET_STAT(call_sessionsetup)); cli_print(cli, "%-30s%u", "call_assign_ip_address", GET_STAT(call_assign_ip_address)); cli_print(cli, "%-30s%u", "call_free_ip_address", GET_STAT(call_free_ip_address)); cli_print(cli, "%-30s%u", "call_dump_acct_info", GET_STAT(call_dump_acct_info)); cli_print(cli, "%-30s%u", "call_radiussend", GET_STAT(call_radiussend)); cli_print(cli, "%-30s%u", "call_radiusretry", GET_STAT(call_radiusretry)); cli_print(cli, "%-30s%u", "call_random_data", GET_STAT(call_random_data)); #endif /* STAT_CALLS */ { time_t l = GET_STAT(last_reset); char *t = ctime(&l); char *p = strchr(t, '\n'); if (p) *p = 0; cli_print(cli, " "); cli_print(cli, "Last counter reset %s", t); } return CLI_OK; } static int cmd_clear_counters(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; memset(_statistics, 0, sizeof(struct Tstats)); SET_STAT(last_reset, time(NULL)); cli_print(cli, "Counters cleared"); return CLI_OK; } #endif /* STATISTICS */ static int cmd_show_version(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "L2TPNS %s", VERSION); return CLI_OK; } static int cmd_show_pool(struct cli_def *cli, const char *command, char **argv, int argc) { int i; int used = 0, free = 0, show_all = 0; if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", fmtaddr(config->cluster_master_address, 0)); return CLI_OK; } if (CLI_HELP_REQUESTED) { if (argc > 1) return cli_arg_help(cli, 1, NULL); return cli_arg_help(cli, 1, "all", "Show all pool addresses, including unused", NULL); } if (argc > 0 && strcmp(argv[0], "all") == 0) show_all = 1; time(&time_now); cli_print(cli, "%-15s %4s %8s %s", "IP Address", "Used", "Session", "User"); for (i = 0; i < MAXIPPOOL; i++) { if (!ip_address_pool[i].address) continue; if (ip_address_pool[i].assigned) { cli_print(cli, "%-15s\tY %8d %s", fmtaddr(htonl(ip_address_pool[i].address), 0), ip_address_pool[i].session, session[ip_address_pool[i].session].user); used++; } else { if (ip_address_pool[i].last) cli_print(cli, "%-15s\tN %8s [%s] %ds", fmtaddr(htonl(ip_address_pool[i].address), 0), "", ip_address_pool[i].user, (int) time_now - ip_address_pool[i].last); else if (show_all) cli_print(cli, "%-15s\tN", fmtaddr(htonl(ip_address_pool[i].address), 0)); free++; } } if (!show_all) cli_print(cli, "(Not displaying unused addresses)"); cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used); return CLI_OK; } static FILE *save_config_fh = 0; static void print_save_config(struct cli_def *cli, const char *string) { if (save_config_fh) fprintf(save_config_fh, "%s\n", string); } static int cmd_write_memory(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; if ((save_config_fh = fopen(config->config_file, "w"))) { cli_print(cli, "Writing configuration"); cli_print_callback(cli, print_save_config); cmd_show_run(cli, command, argv, argc); cli_print_callback(cli, NULL); fclose(save_config_fh); save_config_fh = 0; } else { cli_error(cli, "Error writing configuration: %s", strerror(errno)); } return CLI_OK; } static char const *show_access_list_rule(int extended, ip_filter_rulet *rule); static int cmd_show_run(struct cli_def *cli, const char *command, char **argv, int argc) { int i; char ipv6addr[INET6_ADDRSTRLEN]; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "# Current configuration:"); for (i = 0; config_values[i].key; i++) { void *value = ((void *)config) + config_values[i].offset; if (config_values[i].type == STRING) cli_print(cli, "set %s \"%.*s\"", config_values[i].key, config_values[i].size, (char *) value); else if (config_values[i].type == IPv4) cli_print(cli, "set %s %s", config_values[i].key, fmtaddr(*(in_addr_t *) value, 0)); else if (config_values[i].type == IPv6) cli_print(cli, "set %s %s", config_values[i].key, inet_ntop(AF_INET6, value, ipv6addr, INET6_ADDRSTRLEN)); else if (config_values[i].type == SHORT) cli_print(cli, "set %s %hu", config_values[i].key, *(short *) value); else if (config_values[i].type == BOOL) cli_print(cli, "set %s %s", config_values[i].key, (*(int *) value) ? "yes" : "no"); else if (config_values[i].type == INT) cli_print(cli, "set %s %d", config_values[i].key, *(int *) value); else if (config_values[i].type == UNSIGNED_LONG) cli_print(cli, "set %s %lu", config_values[i].key, *(unsigned long *) value); } cli_print(cli, "# Plugins"); for (i = 0; i < MAXPLUGINS; i++) { if (*config->plugins[i]) { cli_print(cli, "load plugin \"%s\"", config->plugins[i]); } } #ifdef BGP if (config->as_number) { int k; int h; cli_print(cli, "# BGP"); cli_print(cli, "router bgp %u", config->as_number); for (i = 0; i < BGP_NUM_PEERS; i++) { if (!config->neighbour[i].name[0]) continue; cli_print(cli, " neighbour %s remote-as %u", config->neighbour[i].name, config->neighbour[i].as); k = config->neighbour[i].keepalive; h = config->neighbour[i].hold; if (k == -1) { if (h == -1) continue; k = BGP_KEEPALIVE_TIME; } if (h == -1) h = BGP_HOLD_TIME; cli_print(cli, " neighbour %s timers %d %d", config->neighbour[i].name, k, h); if (config->neighbour[i].update_source.s_addr != INADDR_ANY) cli_print(cli, " neighbour %s update-source %s", config->neighbour[i].name, inet_ntoa(config->neighbour[i].update_source)); } } #endif cli_print(cli, "# Filters"); for (i = 0; i < MAXFILTER; i++) { ip_filter_rulet *rules; if (!*ip_filters[i].name) continue; cli_print(cli, "ip access-list %s %s", ip_filters[i].extended ? "extended" : "standard", ip_filters[i].name); rules = ip_filters[i].rules; while (rules->action) cli_print(cli, "%s", show_access_list_rule(ip_filters[i].extended, rules++)); } cli_print(cli, "# end"); return CLI_OK; } static int cmd_show_radius(struct cli_def *cli, const char *command, char **argv, int argc) { int i, free = 0, used = 0, show_all = 0; char *states[] = { "NULL", "CHAP", "AUTH", "IPCP", "START", "STOP", "INTRM", "WAIT", }; if (CLI_HELP_REQUESTED) { if (argc > 1) return cli_arg_help(cli, 1, NULL); return cli_arg_help(cli, 1, "all", "Show all RADIUS sessions, including unused", NULL); } cli_print(cli, "%6s%7s%5s%6s%9s%9s%4s", "ID", "Radius", "Sock", "State", "Session", "Retry", "Try"); time(&time_now); if (argc > 0 && strcmp(argv[0], "all") == 0) show_all = 1; for (i = 1; i < MAXRADIUS; i++) { if (radius[i].state == RADIUSNULL) free++; else used++; if (!show_all && radius[i].state == RADIUSNULL) continue; cli_print(cli, "%6d%7d%5d%6s%9d%9u%4d", i, i >> RADIUS_SHIFT, i & RADIUS_MASK, states[radius[i].state], radius[i].session, radius[i].retry, radius[i].try); } cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used); return CLI_OK; } static int cmd_show_plugins(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "Plugins currently loaded:"); for (i = 0; i < MAXPLUGINS; i++) if (*config->plugins[i]) cli_print(cli, " %s", config->plugins[i]); return CLI_OK; } static int cmd_show_throttle(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "%5s %4s %-32s %7s %6s %6s %6s", "SID", "TID", "Username", "Rate In", "Out", "TBFI", "TBFO"); for (i = 0; i < MAXSESSION; i++) { if (session[i].throttle_in || session[i].throttle_out) cli_print(cli, "%5d %4d %-32s %6d %6d %6d %6d", i, session[i].tunnel, session[i].user, session[i].throttle_in, session[i].throttle_out, session[i].tbf_in, session[i].tbf_out); } return CLI_OK; } static int cmd_show_banana(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, " _\n" "//\\\n" "V \\\n" " \\ \\_\n" " \\,'.`-.\n" " |\\ `. `.\n" " ( \\ `. `-. _,.-:\\\n" " \\ \\ `. `-._ __..--' ,-';/\n" " \\ `. `-. `-..___..---' _.--' ,'/\n" " `. `. `-._ __..--' ,' /\n" " `. `-_ ``--..'' _.-' ,'\n" " `-_ `-.___ __,--' ,'\n" " `-.__ `----\"\"\" __.-'\n" "hh `--..____..--'"); return CLI_OK; } static int cmd_drop_user(struct cli_def *cli, const char *command, char **argv, int argc) { int i; sessionidt s; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "USER", "Username of session to drop", NULL); 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 (!argc) { cli_error(cli, "Specify a user to drop"); return CLI_OK; } for (i = 0; i < argc; i++) { if (!(s = sessionbyuser(argv[i]))) { cli_error(cli, "User %s is not connected", argv[i]); continue; } if (session[s].ip && session[s].opened && !session[s].die) { cli_print(cli, "Dropping user %s", session[s].user); cli_session_actions[s].action |= CLI_SESS_KILL; } } return CLI_OK; } static int cmd_drop_tunnel(struct cli_def *cli, const char *command, char **argv, int argc) { int i; tunnelidt t; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "<1-%d>", MAXTUNNEL-1, "Tunnel id to drop", NULL); 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 (!argc) { cli_error(cli, "Specify a tunnel to drop"); return CLI_OK; } for (i = 0; i < argc; i++) { if ((t = atol(argv[i])) <= 0 || (t >= MAXTUNNEL)) { cli_error(cli, "Invalid tunnel ID (1-%d)", MAXTUNNEL-1); continue; } if (!tunnel[t].ip) { cli_error(cli, "Tunnel %d is not connected", t); continue; } if (tunnel[t].die) { cli_error(cli, "Tunnel %d is already being shut down", t); continue; } cli_print(cli, "Tunnel %d shut down (%s)", t, tunnel[t].hostname); cli_tunnel_actions[t].action |= CLI_TUN_KILL; } return CLI_OK; } static int cmd_drop_session(struct cli_def *cli, const char *command, char **argv, int argc) { int i; sessionidt s; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "<1-%d>", MAXSESSION-1, "Session id to drop", NULL); 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 (!argc) { cli_error(cli, "Specify a session id to drop"); return CLI_OK; } for (i = 0; i < argc; i++) { if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION)) { cli_error(cli, "Invalid session ID (1-%d)", MAXSESSION-1); continue; } if (session[s].ip && session[s].opened && !session[s].die) { cli_print(cli, "Dropping session %d", s); cli_session_actions[s].action |= CLI_SESS_KILL; } else if (session[s].forwardtosession && session[s].opened && !session[s].die) { cli_print(cli, "Dropping session %d", s); cli_session_actions[s].action |= CLI_SESS_KILL; } else { cli_error(cli, "Session %d is not active.", s); } } return CLI_OK; } static int cmd_snoop(struct cli_def *cli, const char *command, char **argv, int argc) { in_addr_t ip; uint16_t port; sessionidt s; if (CLI_HELP_REQUESTED) { switch (argc) { case 1: return cli_arg_help(cli, 0, "USER", "Username of session to snoop", NULL); case 2: return cli_arg_help(cli, 0, "A.B.C.D", "IP address of snoop destination", NULL); case 3: return cli_arg_help(cli, 0, "N", "Port of snoop destination", NULL); case 4: if (!argv[3][1]) return cli_arg_help(cli, 1, NULL); default: return CLI_OK; } } 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 (argc < 3) { cli_error(cli, "Specify username, ip and port"); return CLI_OK; } if (!(s = sessionbyuser(argv[0]))) { cli_error(cli, "User %s is not connected", argv[0]); return CLI_OK; } ip = inet_addr(argv[1]); if (!ip || ip == INADDR_NONE) { cli_error(cli, "Cannot parse IP \"%s\"", argv[1]); return CLI_OK; } port = atoi(argv[2]); if (!port) { cli_error(cli, "Invalid port %s", argv[2]); return CLI_OK; } cli_print(cli, "Snooping user %s to %s:%d", argv[0], fmtaddr(ip, 0), port); cli_session_actions[s].snoop_ip = ip; cli_session_actions[s].snoop_port = port; cli_session_actions[s].action |= CLI_SESS_SNOOP; return CLI_OK; } static int cmd_no_snoop(struct cli_def *cli, const char *command, char **argv, int argc) { int i; sessionidt s; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "USER", "Username of session to unsnoop", NULL); 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 (!argc) { cli_error(cli, "Specify a user to unsnoop"); return CLI_OK; } for (i = 0; i < argc; i++) { if (!(s = sessionbyuser(argv[i]))) { cli_error(cli, "User %s is not connected", argv[i]); continue; } cli_print(cli, "Not snooping user %s", argv[i]); cli_session_actions[s].action |= CLI_SESS_NOSNOOP; } return CLI_OK; } static int cmd_throttle(struct cli_def *cli, const char *command, char **argv, int argc) { int rate_in = 0; int rate_out = 0; sessionidt s; /* throttle USER - throttle in/out to default rate throttle USER RATE - throttle in/out to default rate throttle USER in RATE - throttle input only throttle USER out RATE - throttle output only throttle USER in RATE out RATE - throttle both */ if (CLI_HELP_REQUESTED) { switch (argc) { case 1: return cli_arg_help(cli, 0, "USER", "Username of session to throttle", NULL); case 2: return cli_arg_help(cli, 1, "RATE", "Rate in kbps (in and out)", "in", "Select incoming rate", "out", "Select outgoing rate", NULL); case 4: return cli_arg_help(cli, 1, "in", "Select incoming rate", "out", "Select outgoing rate", NULL); case 3: if (isdigit(argv[1][0])) return cli_arg_help(cli, 1, NULL); case 5: return cli_arg_help(cli, 0, "RATE", "Rate in kbps", NULL); default: return cli_arg_help(cli, argc > 1, NULL); } } 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 (argc == 0) { cli_error(cli, "Specify a user to throttle"); return CLI_OK; } if (!(s = sessionbyuser(argv[0]))) { cli_error(cli, "User %s is not connected", argv[0]); return CLI_OK; } if (argc == 1) { rate_in = rate_out = config->rl_rate; } else if (argc == 2) { rate_in = rate_out = atoi(argv[1]); if (rate_in < 1) { cli_error(cli, "Invalid rate \"%s\"", argv[1]); return CLI_OK; } } else if (argc == 3 || argc == 5) { int i; for (i = 1; i < argc - 1; i += 2) { int r = 0; if (MATCH("in", argv[i])) r = rate_in = atoi(argv[i+1]); else if (MATCH("out", argv[i])) r = rate_out = atoi(argv[i+1]); if (r < 1) { cli_error(cli, "Invalid rate specification \"%s %s\"", argv[i], argv[i+1]); return CLI_OK; } } } else { cli_error(cli, "Invalid arguments"); return CLI_OK; } if ((rate_in && session[s].throttle_in) || (rate_out && session[s].throttle_out)) { cli_error(cli, "User %s already throttled, unthrottle first", argv[0]); return CLI_OK; } cli_session_actions[s].throttle_in = cli_session_actions[s].throttle_out = -1; if (rate_in && session[s].throttle_in != rate_in) cli_session_actions[s].throttle_in = rate_in; if (rate_out && session[s].throttle_out != rate_out) cli_session_actions[s].throttle_out = rate_out; if (cli_session_actions[s].throttle_in == -1 && cli_session_actions[s].throttle_out == -1) { cli_error(cli, "User %s already throttled at this rate", argv[0]); return CLI_OK; } cli_print(cli, "Throttling user %s", argv[0]); cli_session_actions[s].action |= CLI_SESS_THROTTLE; return CLI_OK; } static int cmd_no_throttle(struct cli_def *cli, const char *command, char **argv, int argc) { int i; sessionidt s; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "USER", "Username of session to unthrottle", NULL); 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 (!argc) { cli_error(cli, "Specify a user to unthrottle"); return CLI_OK; } for (i = 0; i < argc; i++) { if (!(s = sessionbyuser(argv[i]))) { cli_error(cli, "User %s is not connected", argv[i]); continue; } if (session[s].throttle_in || session[s].throttle_out) { cli_print(cli, "Unthrottling user %s", argv[i]); cli_session_actions[s].action |= CLI_SESS_NOTHROTTLE; } else { cli_error(cli, "User %s not throttled", argv[i]); } } return CLI_OK; } static int cmd_debug(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "all", "Enable debugging for all except \"data\"", "critical", "", // FIXME: add descriptions "error", "", "warning", "", "info", "", "calls", "", "data", "", NULL); if (!argc) { char *p = (char *) &debug_flags; for (i = 0; i < sizeof(debug_flags); i++) { if (p[i]) { cli_print(cli, "Currently debugging:%s%s%s%s%s%s", (debug_flags.critical) ? " critical" : "", (debug_flags.error) ? " error" : "", (debug_flags.warning) ? " warning" : "", (debug_flags.info) ? " info" : "", (debug_flags.calls) ? " calls" : "", (debug_flags.data) ? " data" : ""); return CLI_OK; } } cli_print(cli, "Debugging off"); return CLI_OK; } for (i = 0; i < argc; i++) { int len = strlen(argv[i]); if (argv[i][0] == 'c' && len < 2) len = 2; /* distinguish [cr]itical from [ca]lls */ if (!strncmp("critical", argv[i], len)) { debug_flags.critical = 1; continue; } if (!strncmp("error", argv[i], len)) { debug_flags.error = 1; continue; } if (!strncmp("warning", argv[i], len)) { debug_flags.warning = 1; continue; } if (!strncmp("info", argv[i], len)) { debug_flags.info = 1; continue; } if (!strncmp("calls", argv[i], len)) { debug_flags.calls = 1; continue; } if (!strncmp("data", argv[i], len)) { debug_flags.data = 1; continue; } if (!strncmp("all", argv[i], len)) { memset(&debug_flags, 1, sizeof(debug_flags)); debug_flags.data = 0; continue; } cli_error(cli, "Invalid debugging flag \"%s\"", argv[i]); } return CLI_OK; } static int cmd_no_debug(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "all", "Disable all debugging", "critical", "", // FIXME: add descriptions "error", "", "warning", "", "info", "", "calls", "", "data", "", NULL); if (!argc) { memset(&debug_flags, 0, sizeof(debug_flags)); return CLI_OK; } for (i = 0; i < argc; i++) { int len = strlen(argv[i]); if (argv[i][0] == 'c' && len < 2) len = 2; /* distinguish [cr]itical from [ca]lls */ if (!strncmp("critical", argv[i], len)) { debug_flags.critical = 0; continue; } if (!strncmp("error", argv[i], len)) { debug_flags.error = 0; continue; } if (!strncmp("warning", argv[i], len)) { debug_flags.warning = 0; continue; } if (!strncmp("info", argv[i], len)) { debug_flags.info = 0; continue; } if (!strncmp("calls", argv[i], len)) { debug_flags.calls = 0; continue; } if (!strncmp("data", argv[i], len)) { debug_flags.data = 0; continue; } if (!strncmp("all", argv[i], len)) { memset(&debug_flags, 0, sizeof(debug_flags)); continue; } cli_error(cli, "Invalid debugging flag \"%s\"", argv[i]); } return CLI_OK; } static int cmd_load_plugin(struct cli_def *cli, const char *command, char **argv, int argc) { int i, firstfree = 0; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "PLUGIN", "Name of plugin to load", NULL); if (argc != 1) { cli_error(cli, "Specify a plugin to load"); return CLI_OK; } for (i = 0; i < MAXPLUGINS; i++) { if (!*config->plugins[i] && !firstfree) firstfree = i; if (strcmp(config->plugins[i], argv[0]) == 0) { cli_error(cli, "Plugin is already loaded"); return CLI_OK; } } if (firstfree) { strncpy(config->plugins[firstfree], argv[0], sizeof(config->plugins[firstfree]) - 1); config->reload_config = 1; cli_print(cli, "Loading plugin %s", argv[0]); } return CLI_OK; } static int cmd_remove_plugin(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "PLUGIN", "Name of plugin to unload", NULL); if (argc != 1) { cli_error(cli, "Specify a plugin to remove"); return CLI_OK; } for (i = 0; i < MAXPLUGINS; i++) { if (strcmp(config->plugins[i], argv[0]) == 0) { config->reload_config = 1; memset(config->plugins[i], 0, sizeof(config->plugins[i])); return CLI_OK; } } cli_error(cli, "Plugin is not loaded"); return CLI_OK; } static char *duration(time_t secs) { static char *buf = NULL; int p = 0; if (!buf) buf = calloc(64, 1); if (secs >= 86400) { int days = secs / 86400; p = sprintf(buf, "%d day%s, ", days, days > 1 ? "s" : ""); secs %= 86400; } if (secs >= 3600) { int mins = secs / 60; int hrs = mins / 60; mins %= 60; sprintf(buf + p, "%d:%02d", hrs, mins); } else if (secs >= 60) { int mins = secs / 60; sprintf(buf + p, "%d min%s", mins, mins > 1 ? "s" : ""); } else sprintf(buf, "%ld sec%s", secs, secs > 1 ? "s" : ""); return buf; } static int cmd_uptime(struct cli_def *cli, const char *command, char **argv, int argc) { FILE *fh; char buf[100], *p = buf, *loads[3]; int i, num_sessions = 0; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; fh = fopen("/proc/loadavg", "r"); p = fgets(buf, 100, fh); fclose(fh); for (i = 0; i < 3; i++) loads[i] = strdup(strsep(&p, " ")); time(&time_now); strftime(buf, 99, "%H:%M:%S", localtime(&time_now)); for (i = 1; i < MAXSESSION; i++) if (session[i].opened) num_sessions++; cli_print(cli, "%s up %s, %d users, load average: %s, %s, %s", buf, duration(time_now - config->start_time), num_sessions, loads[0], loads[1], loads[2] ); for (i = 0; i < 3; i++) if (loads[i]) free(loads[i]); cli_print(cli, "Bandwidth: %s", config->bandwidth); return CLI_OK; } static int cmd_set(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) { switch (argc) { case 1: { int len = strlen(argv[0])-1; for (i = 0; config_values[i].key; i++) if (!len || !strncmp(config_values[i].key, argv[0], len)) cli_error(cli, " %s", config_values[i].key); } return CLI_OK; case 2: return cli_arg_help(cli, 0, "VALUE", "Value for variable", NULL); case 3: if (!argv[2][1]) return cli_arg_help(cli, 1, NULL); default: return CLI_OK; } } if (argc != 2) { cli_error(cli, "Specify variable and value"); return CLI_OK; } for (i = 0; config_values[i].key; i++) { void *value = ((void *) config) + config_values[i].offset; if (strcmp(config_values[i].key, argv[0]) == 0) { // Found a value to set cli_print(cli, "Setting \"%s\" to \"%s\"", argv[0], argv[1]); switch (config_values[i].type) { case STRING: snprintf((char *) value, config_values[i].size, "%s", argv[1]); break; case INT: *(int *) value = atoi(argv[1]); break; case UNSIGNED_LONG: *(unsigned long *) value = atol(argv[1]); break; case SHORT: *(short *) value = atoi(argv[1]); break; case IPv4: *(in_addr_t *) value = inet_addr(argv[1]); break; case IPv6: inet_pton(AF_INET6, argv[1], value); break; case BOOL: if (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "true") == 0 || strcasecmp(argv[1], "1") == 0) *(int *) value = 1; else *(int *) value = 0; break; default: cli_error(cli, "Unknown variable type"); break; } config->reload_config = 1; return CLI_OK; } } cli_error(cli, "Unknown variable \"%s\"", argv[0]); return CLI_OK; } int regular_stuff(struct cli_def *cli) { #ifdef RINGBUFFER int out = 0; int i; for (i = debug_rb_tail; i != ringbuffer->tail; i = (i + 1) % RINGBUFFER_SIZE) { char *m = ringbuffer->buffer[i].message; char *p; int show = 0; if (!*m) continue; switch (ringbuffer->buffer[i].level) { case 0: show = debug_flags.critical; break; case 1: show = debug_flags.error; break; case 2: show = debug_flags.warning; break; case 3: show = debug_flags.info; break; case 4: show = debug_flags.calls; break; case 5: show = debug_flags.data; break; } if (!show) continue; if (!(p = strchr(m, '\n'))) p = m + strlen(m); cli_error(cli, "\r%s-%u-%u %.*s", debug_levels[(int)ringbuffer->buffer[i].level], ringbuffer->buffer[i].tunnel, ringbuffer->buffer[i].session, (int) (p - m), m); out++; } debug_rb_tail = ringbuffer->tail; if (out) cli_reprompt(cli); #endif return CLI_OK; } #ifdef BGP static int cmd_router_bgp(struct cli_def *cli, const char *command, char **argv, int argc) { int as; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "<1-65535>", "Autonomous system number", NULL); if (argc != 1 || (as = atoi(argv[0])) < 1 || as > 65535) { cli_error(cli, "Invalid autonomous system number"); return CLI_OK; } if (bgp_configured && as != config->as_number) { cli_error(cli, "Can't change local AS on a running system"); return CLI_OK; } config->as_number = as; cli_set_configmode(cli, MODE_CONFIG_BGP, "router"); return CLI_OK; } static int find_bgp_neighbour(char const *name) { int i; int new = -1; struct hostent *h; in_addr_t addrs[4] = { 0 }; char **a; if (!(h = gethostbyname(name)) || h->h_addrtype != AF_INET) return -2; for (i = 0; i < sizeof(addrs) / sizeof(*addrs) && h->h_addr_list[i]; i++) memcpy(&addrs[i], h->h_addr_list[i], sizeof(*addrs)); for (i = 0; i < BGP_NUM_PEERS; i++) { if (!config->neighbour[i].name[0]) { if (new == -1) new = i; continue; } if (!strcmp(name, config->neighbour[i].name)) return i; if (!(h = gethostbyname(config->neighbour[i].name)) || h->h_addrtype != AF_INET) continue; for (a = h->h_addr_list; *a; a++) { int j; for (j = 0; j < sizeof(addrs) / sizeof(*addrs) && addrs[j]; j++) if (!memcmp(&addrs[j], *a, sizeof(*addrs))) return i; } } return new; } static int cmd_router_bgp_neighbour(struct cli_def *cli, const char *command, char **argv, int argc) { int i; int keepalive; int hold; if (CLI_HELP_REQUESTED) { switch (argc) { case 1: return cli_arg_help(cli, 0, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", NULL); case 2: return cli_arg_help(cli, 0, "remote-as", "Set remote autonomous system number", "timers", "Set timers", "update-source", "Set source address to use for the BGP session", NULL); default: if (MATCH("remote-as", argv[1])) return cli_arg_help(cli, argv[2][1], "<1-65535>", "Autonomous system number", NULL); if (MATCH("timers", argv[1])) { if (argc == 3) return cli_arg_help(cli, 0, "<1-65535>", "Keepalive time", NULL); if (argc == 4) return cli_arg_help(cli, argv[3][1], "<3-65535>", "Hold time", NULL); if (argc == 5 && !argv[4][1]) return cli_arg_help(cli, 1, NULL); } if (MATCH("update-source", argv[1])) return cli_arg_help(cli, argc > 3, "A.B.C.D", "Source IP address", NULL); return CLI_OK; } } if (argc < 3) { cli_error(cli, "Invalid arguments"); return CLI_OK; } if ((i = find_bgp_neighbour(argv[0])) == -2) { cli_error(cli, "Invalid neighbour"); return CLI_OK; } if (i == -1) { cli_error(cli, "Too many neighbours (max %d)", BGP_NUM_PEERS); return CLI_OK; } if (MATCH("remote-as", argv[1])) { int as = atoi(argv[2]); if (as < 0 || as > 65535) { cli_error(cli, "Invalid autonomous system number"); return CLI_OK; } if (!config->neighbour[i].name[0]) { snprintf(config->neighbour[i].name, sizeof(config->neighbour[i].name), "%s", argv[0]); config->neighbour[i].keepalive = -1; config->neighbour[i].hold = -1; config->neighbour[i].update_source.s_addr = INADDR_ANY; } config->neighbour[i].as = as; return CLI_OK; } if (MATCH("update-source", argv[1])) { struct in_addr addr; if (!config->neighbour[i].name[0]) { cli_error(cli, "Specify remote-as first"); return CLI_OK; } if (!inet_aton(argv[2], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[2]); return CLI_OK; } config->neighbour[i].update_source = addr; return CLI_OK; } if (argc != 4 || !MATCH("timers", argv[1])) { cli_error(cli, "Invalid arguments"); return CLI_OK; } if (!config->neighbour[i].name[0]) { cli_error(cli, "Specify remote-as first"); return CLI_OK; } keepalive = atoi(argv[2]); hold = atoi(argv[3]); if (keepalive < 1 || keepalive > 65535) { cli_error(cli, "Invalid keepalive time"); return CLI_OK; } if (hold < 3 || hold > 65535) { cli_error(cli, "Invalid hold time"); return CLI_OK; } if (keepalive == BGP_KEEPALIVE_TIME) keepalive = -1; // using default value if (hold == BGP_HOLD_TIME) hold = -1; config->neighbour[i].keepalive = keepalive; config->neighbour[i].hold = hold; return CLI_OK; } static int cmd_router_bgp_no_neighbour(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 0, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", NULL); if (argc != 1) { cli_error(cli, "Specify a BGP neighbour"); return CLI_OK; } if ((i = find_bgp_neighbour(argv[0])) == -2) { cli_error(cli, "Invalid neighbour"); return CLI_OK; } if (i < 0 || !config->neighbour[i].name[0]) { cli_error(cli, "Neighbour %s not configured", argv[0]); return CLI_OK; } memset(&config->neighbour[i], 0, sizeof(config->neighbour[i])); return CLI_OK; } static int cmd_show_bgp(struct cli_def *cli, const char *command, char **argv, int argc) { int i; int hdr = 0; char *addr; if (!bgp_configured) return CLI_OK; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", NULL); cli_print(cli, "BGPv%d router identifier %s, local AS number %d", BGP_VERSION, fmtaddr(my_address, 0), (int) config->as_number); time(&time_now); for (i = 0; i < BGP_NUM_PEERS; i++) { if (!*bgp_peers[i].name) continue; addr = fmtaddr(bgp_peers[i].addr, 0); if (argc && strcmp(addr, argv[0]) && strncmp(bgp_peers[i].name, argv[0], strlen(argv[0]))) continue; if (!hdr++) { cli_print(cli, " "); cli_print(cli, "Peer AS Address " "State Retries Retry in Route Pend Timers"); cli_print(cli, "------------------ ----- --------------- " "----------- ------- -------- ----- ---- ---------"); } cli_print(cli, "%-18.18s %5d %15s %-11s %7d %7lds %5s %4s %4d %4d", bgp_peers[i].name, bgp_peers[i].as, addr, bgp_state_str(bgp_peers[i].state), bgp_peers[i].retry_count, bgp_peers[i].retry_time ? bgp_peers[i].retry_time - time_now : 0, bgp_peers[i].routing ? "yes" : "no", bgp_peers[i].update_routes ? "yes" : "no", bgp_peers[i].keepalive, bgp_peers[i].hold); } return CLI_OK; } static int cmd_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc) { int i; char *addr; if (!bgp_configured) return CLI_OK; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", NULL); for (i = 0; i < BGP_NUM_PEERS; i++) { if (bgp_peers[i].state != Established) continue; if (!bgp_peers[i].routing) continue; addr = fmtaddr(bgp_peers[i].addr, 0); if (argc && strcmp(addr, argv[0]) && strcmp(bgp_peers[i].name, argv[0])) continue; bgp_peers[i].cli_flag = BGP_CLI_SUSPEND; cli_print(cli, "Suspending peer %s", bgp_peers[i].name); } return CLI_OK; } static int cmd_no_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc) { int i; char *addr; if (!bgp_configured) return CLI_OK; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", NULL); for (i = 0; i < BGP_NUM_PEERS; i++) { if (bgp_peers[i].state != Established) continue; if (bgp_peers[i].routing) continue; addr = fmtaddr(bgp_peers[i].addr, 0); if (argc && strcmp(addr, argv[0]) && strncmp(bgp_peers[i].name, argv[0], strlen(argv[0]))) continue; bgp_peers[i].cli_flag = BGP_CLI_ENABLE; cli_print(cli, "Un-suspending peer %s", bgp_peers[i].name); } return CLI_OK; } static int cmd_restart_bgp(struct cli_def *cli, const char *command, char **argv, int argc) { int i; char *addr; if (!bgp_configured) return CLI_OK; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", NULL); for (i = 0; i < BGP_NUM_PEERS; i++) { if (!*bgp_peers[i].name) continue; addr = fmtaddr(bgp_peers[i].addr, 0); if (argc && strcmp(addr, argv[0]) && strncmp(bgp_peers[i].name, argv[0], strlen(argv[0]))) continue; bgp_peers[i].cli_flag = BGP_CLI_RESTART; cli_print(cli, "Restarting peer %s", bgp_peers[i].name); } return CLI_OK; } #endif /* BGP*/ static int filt; static int access_list(struct cli_def *cli, char **argv, int argc, int add) { int extended; if (CLI_HELP_REQUESTED) { switch (argc) { case 1: return cli_arg_help(cli, 0, "standard", "Standard syntax", "extended", "Extended syntax", NULL); case 2: return cli_arg_help(cli, argv[1][1], "NAME", "Access-list name", NULL); default: if (argc == 3 && !argv[2][1]) return cli_arg_help(cli, 1, NULL); return CLI_OK; } } if (argc != 2) { cli_error(cli, "Specify access-list type and name"); return CLI_OK; } if (MATCH("standard", argv[0])) extended = 0; else if (MATCH("extended", argv[0])) extended = 1; else { cli_error(cli, "Invalid access-list type"); return CLI_OK; } if (strlen(argv[1]) > sizeof(ip_filters[0].name) - 1 || strspn(argv[1], "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-") != strlen(argv[1])) { cli_error(cli, "Invalid access-list name"); return CLI_OK; } filt = find_filter(argv[1], strlen(argv[1])); if (add) { if (filt < 0) { cli_error(cli, "Too many access-lists"); return CLI_OK; } // racy if (!*ip_filters[filt].name) { memset(&ip_filters[filt], 0, sizeof(ip_filters[filt])); strcpy(ip_filters[filt].name, argv[1]); ip_filters[filt].extended = extended; } else if (ip_filters[filt].extended != extended) { cli_error(cli, "Access-list is %s", ip_filters[filt].extended ? "extended" : "standard"); return CLI_OK; } cli_set_configmode(cli, MODE_CONFIG_NACL, extended ? "ext-nacl" : "std-nacl"); return CLI_OK; } if (filt < 0 || !*ip_filters[filt].name) { cli_error(cli, "Access-list not defined"); return CLI_OK; } // racy if (ip_filters[filt].used) { cli_error(cli, "Access-list in use"); return CLI_OK; } memset(&ip_filters[filt], 0, sizeof(ip_filters[filt])); return CLI_OK; } static int cmd_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc) { return access_list(cli, argv, argc, 1); } static int cmd_no_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc) { return access_list(cli, argv, argc, 0); } static int show_ip_wild(char *buf, in_addr_t ip, in_addr_t wild) { if (ip == INADDR_ANY && wild == INADDR_BROADCAST) return sprintf(buf, " any"); if (wild == INADDR_ANY) return sprintf(buf, " host %s", fmtaddr(ip, 0)); return sprintf(buf, " %s %s", fmtaddr(ip, 0), fmtaddr(wild, 1)); } static int show_ports(char *buf, ip_filter_portt *ports) { switch (ports->op) { case FILTER_PORT_OP_EQ: return sprintf(buf, " eq %u", ports->port); case FILTER_PORT_OP_NEQ: return sprintf(buf, " neq %u", ports->port); case FILTER_PORT_OP_GT: return sprintf(buf, " gt %u", ports->port); case FILTER_PORT_OP_LT: return sprintf(buf, " lt %u", ports->port); case FILTER_PORT_OP_RANGE: return sprintf(buf, " range %u %u", ports->port, ports->port2); } return 0; } static char const *show_access_list_rule(int extended, ip_filter_rulet *rule) { static char buf[256]; char *p = buf; p += sprintf(p, " %s", rule->action == FILTER_ACTION_PERMIT ? "permit" : "deny"); if (extended) { struct protoent *proto = getprotobynumber(rule->proto); p += sprintf(p, " %s", proto ? proto->p_name : "ERR"); } p += show_ip_wild(p, rule->src_ip, rule->src_wild); if (!extended) return buf; if (rule->proto == IPPROTO_TCP || rule->proto == IPPROTO_UDP) p += show_ports(p, &rule->src_ports); p += show_ip_wild(p, rule->dst_ip, rule->dst_wild); if (rule->proto == IPPROTO_TCP || rule->proto == IPPROTO_UDP) p += show_ports(p, &rule->dst_ports); if (rule->proto == IPPROTO_TCP && rule->tcp_flag_op) { switch (rule->tcp_flag_op) { case FILTER_FLAG_OP_EST: p += sprintf(p, " established"); break; case FILTER_FLAG_OP_ANY: case FILTER_FLAG_OP_ALL: p += sprintf(p, " match-%s", rule->tcp_flag_op == FILTER_FLAG_OP_ALL ? "all" : "any"); if (rule->tcp_sflags & TCP_FLAG_FIN) p += sprintf(p, " +fin"); if (rule->tcp_cflags & TCP_FLAG_FIN) p += sprintf(p, " -fin"); if (rule->tcp_sflags & TCP_FLAG_SYN) p += sprintf(p, " +syn"); if (rule->tcp_cflags & TCP_FLAG_SYN) p += sprintf(p, " -syn"); if (rule->tcp_sflags & TCP_FLAG_RST) p += sprintf(p, " +rst"); if (rule->tcp_cflags & TCP_FLAG_RST) p += sprintf(p, " -rst"); if (rule->tcp_sflags & TCP_FLAG_PSH) p += sprintf(p, " +psh"); if (rule->tcp_cflags & TCP_FLAG_PSH) p += sprintf(p, " -psh"); if (rule->tcp_sflags & TCP_FLAG_ACK) p += sprintf(p, " +ack"); if (rule->tcp_cflags & TCP_FLAG_ACK) p += sprintf(p, " -ack"); if (rule->tcp_sflags & TCP_FLAG_URG) p += sprintf(p, " +urg"); if (rule->tcp_cflags & TCP_FLAG_URG) p += sprintf(p, " -urg"); break; } } if (rule->frag) p += sprintf(p, " fragments"); return buf; } static ip_filter_rulet *access_list_rule_ext(struct cli_def *cli, const char *command, char **argv, int argc) { static ip_filter_rulet rule; struct in_addr addr; int i; int a; if (CLI_HELP_REQUESTED) { if (argc == 1) { cli_arg_help(cli, 0, "ip", "Match IP packets", "tcp", "Match TCP packets", "udp", "Match UDP packets", NULL); return NULL; } // *sigh*, too darned complex cli_arg_help(cli, 0, "RULE", "SOURCE [PORTS] DEST [PORTS] FLAGS", NULL); return NULL; } if (argc < 3) { cli_error(cli, "Specify rule details"); return NULL; } memset(&rule, 0, sizeof(rule)); rule.action = (command[0] == 'p') ? FILTER_ACTION_PERMIT : FILTER_ACTION_DENY; if (MATCH("ip", argv[0])) rule.proto = IPPROTO_IP; else if (MATCH("udp", argv[0])) rule.proto = IPPROTO_UDP; else if (MATCH("tcp", argv[0])) rule.proto = IPPROTO_TCP; else { cli_error(cli, "Invalid protocol \"%s\"", argv[0]); return NULL; } for (a = 1, i = 0; i < 2; i++) { in_addr_t *ip; in_addr_t *wild; ip_filter_portt *port; if (i == 0) { ip = &rule.src_ip; wild = &rule.src_wild; port = &rule.src_ports; } else { ip = &rule.dst_ip; wild = &rule.dst_wild; port = &rule.dst_ports; if (a >= argc) { cli_error(cli, "Specify destination"); return NULL; } } if (MATCH("any", argv[a])) { *ip = INADDR_ANY; *wild = INADDR_BROADCAST; a++; } else if (MATCH("host", argv[a])) { if (++a >= argc) { cli_error(cli, "Specify host ip address"); return NULL; } if (!inet_aton(argv[a], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[a]); return NULL; } *ip = addr.s_addr; *wild = INADDR_ANY; a++; } else { if (a >= argc - 1) { cli_error(cli, "Specify %s ip address and wildcard", i ? "destination" : "source"); return NULL; } if (!inet_aton(argv[a], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[a]); return NULL; } *ip = addr.s_addr; if (!inet_aton(argv[++a], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[a]); return NULL; } *wild = addr.s_addr; a++; } if (rule.proto == IPPROTO_IP || a >= argc) continue; port->op = 0; if (MATCH("eq", argv[a])) port->op = FILTER_PORT_OP_EQ; else if (MATCH("neq", argv[a])) port->op = FILTER_PORT_OP_NEQ; else if (MATCH("gt", argv[a])) port->op = FILTER_PORT_OP_GT; else if (MATCH("lt", argv[a])) port->op = FILTER_PORT_OP_LT; else if (MATCH("range", argv[a])) port->op = FILTER_PORT_OP_RANGE; if (!port->op) continue; if (++a >= argc) { cli_error(cli, "Specify port"); return NULL; } if (!(port->port = atoi(argv[a]))) { cli_error(cli, "Invalid port \"%s\"", argv[a]); return NULL; } a++; if (port->op != FILTER_PORT_OP_RANGE) continue; if (a >= argc) { cli_error(cli, "Specify port"); return NULL; } if (!(port->port2 = atoi(argv[a])) || port->port2 < port->port) { cli_error(cli, "Invalid port \"%s\"", argv[a]); return NULL; } a++; } if (rule.proto == IPPROTO_TCP && a < argc) { if (MATCH("established", argv[a])) { rule.tcp_flag_op = FILTER_FLAG_OP_EST; a++; } else if (!strcmp(argv[a], "match-any") || !strcmp(argv[a], "match-an") || !strcmp(argv[a], "match-all") || !strcmp(argv[a], "match-al")) { rule.tcp_flag_op = argv[a][7] == 'n' ? FILTER_FLAG_OP_ANY : FILTER_FLAG_OP_ALL; if (++a >= argc) { cli_error(cli, "Specify tcp flags"); return NULL; } while (a < argc && (argv[a][0] == '+' || argv[a][0] == '-')) { uint8_t *f; f = (argv[a][0] == '+') ? &rule.tcp_sflags : &rule.tcp_cflags; if (MATCH("fin", &argv[a][1])) *f |= TCP_FLAG_FIN; else if (MATCH("syn", &argv[a][1])) *f |= TCP_FLAG_SYN; else if (MATCH("rst", &argv[a][1])) *f |= TCP_FLAG_RST; else if (MATCH("psh", &argv[a][1])) *f |= TCP_FLAG_PSH; else if (MATCH("ack", &argv[a][1])) *f |= TCP_FLAG_ACK; else if (MATCH("urg", &argv[a][1])) *f |= TCP_FLAG_URG; else { cli_error(cli, "Invalid tcp flag \"%s\"", argv[a]); return NULL; } a++; } } } if (a < argc && MATCH("fragments", argv[a])) { if (rule.src_ports.op || rule.dst_ports.op || rule.tcp_flag_op) { cli_error(cli, "Can't specify \"fragments\" on rules with layer 4 matches"); return NULL; } rule.frag = 1; a++; } if (a < argc) { cli_error(cli, "Invalid flag \"%s\"", argv[a]); return NULL; } return &rule; } static ip_filter_rulet *access_list_rule_std(struct cli_def *cli, const char *command, char **argv, int argc) { static ip_filter_rulet rule; struct in_addr addr; if (CLI_HELP_REQUESTED) { if (argc == 1) { cli_arg_help(cli, argv[0][1], "A.B.C.D", "Source address", "any", "Any source address", "host", "Source host", NULL); return NULL; } if (MATCH("any", argv[0])) { if (argc == 2 && !argv[1][1]) cli_arg_help(cli, 1, NULL); } else if (MATCH("host", argv[0])) { if (argc == 2) { cli_arg_help(cli, argv[1][1], "A.B.C.D", "Host address", NULL); } else if (argc == 3 && !argv[2][1]) cli_arg_help(cli, 1, NULL); } else { if (argc == 2) { cli_arg_help(cli, 1, "A.B.C.D", "Wildcard bits", NULL); } else if (argc == 3 && !argv[2][1]) cli_arg_help(cli, 1, NULL); } return NULL; } if (argc < 1) { cli_error(cli, "Specify rule details"); return NULL; } memset(&rule, 0, sizeof(rule)); rule.action = (command[0] == 'p') ? FILTER_ACTION_PERMIT : FILTER_ACTION_DENY; rule.proto = IPPROTO_IP; if (MATCH("any", argv[0])) { rule.src_ip = INADDR_ANY; rule.src_wild = INADDR_BROADCAST; } else if (MATCH("host", argv[0])) { if (argc != 2) { cli_error(cli, "Specify host ip address"); return NULL; } if (!inet_aton(argv[1], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[1]); return NULL; } rule.src_ip = addr.s_addr; rule.src_wild = INADDR_ANY; } else { if (argc > 2) { cli_error(cli, "Specify source ip address and wildcard"); return NULL; } if (!inet_aton(argv[0], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[0]); return NULL; } rule.src_ip = addr.s_addr; if (argc > 1) { if (!inet_aton(argv[1], &addr)) { cli_error(cli, "Cannot parse IP \"%s\"", argv[1]); return NULL; } rule.src_wild = addr.s_addr; } else rule.src_wild = INADDR_ANY; } return &rule; } static int cmd_ip_access_list_rule(struct cli_def *cli, const char *command, char **argv, int argc) { int i; ip_filter_rulet *rule = ip_filters[filt].extended ? access_list_rule_ext(cli, command, argv, argc) : access_list_rule_std(cli, command, argv, argc); if (!rule) return CLI_OK; for (i = 0; i < MAXFILTER_RULES - 1; i++) // -1: list always terminated by empty rule { if (!ip_filters[filt].rules[i].action) { memcpy(&ip_filters[filt].rules[i], rule, sizeof(*rule)); return CLI_OK; } if (!memcmp(&ip_filters[filt].rules[i], rule, offsetof(ip_filter_rulet, counter))) return CLI_OK; } cli_error(cli, "Too many rules"); return CLI_OK; } static int cmd_filter(struct cli_def *cli, const char *command, char **argv, int argc) { sessionidt s; int i; /* filter USER {in|out} FILTER ... */ if (CLI_HELP_REQUESTED) { switch (argc) { case 1: return cli_arg_help(cli, 0, "USER", "Username of session to filter", NULL); case 2: case 4: return cli_arg_help(cli, 0, "in", "Set incoming filter", "out", "Set outgoing filter", NULL); case 3: case 5: return cli_arg_help(cli, argc == 5 && argv[4][1], "NAME", "Filter name", NULL); default: return cli_arg_help(cli, argc > 1, NULL); } } 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 (argc != 3 && argc != 5) { cli_error(cli, "Specify a user and filters"); return CLI_OK; } if (!(s = sessionbyuser(argv[0]))) { cli_error(cli, "User %s is not connected", argv[0]); return CLI_OK; } cli_session_actions[s].filter_in = cli_session_actions[s].filter_out = -1; for (i = 1; i < argc; i += 2) { int *f = 0; int v; if (MATCH("in", argv[i])) { if (session[s].filter_in) { cli_error(cli, "Input already filtered"); return CLI_OK; } f = &cli_session_actions[s].filter_in; } else if (MATCH("out", argv[i])) { if (session[s].filter_out) { cli_error(cli, "Output already filtered"); return CLI_OK; } f = &cli_session_actions[s].filter_out; } else { cli_error(cli, "Invalid filter specification"); return CLI_OK; } v = find_filter(argv[i+1], strlen(argv[i+1])); if (v < 0 || !*ip_filters[v].name) { cli_error(cli, "Access-list %s not defined", argv[i+1]); return CLI_OK; } *f = v + 1; } cli_print(cli, "Filtering user %s", argv[0]); cli_session_actions[s].action |= CLI_SESS_FILTER; return CLI_OK; } static int cmd_no_filter(struct cli_def *cli, const char *command, char **argv, int argc) { int i; sessionidt s; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "USER", "Username of session to remove filters from", NULL); 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 (!argc) { cli_error(cli, "Specify a user to remove filters from"); return CLI_OK; } for (i = 0; i < argc; i++) { if (!(s = sessionbyuser(argv[i]))) { cli_error(cli, "User %s is not connected", argv[i]); continue; } if (session[s].filter_in || session[s].filter_out) { cli_print(cli, "Removing filters from user %s", argv[i]); cli_session_actions[s].action |= CLI_SESS_NOFILTER; } else { cli_error(cli, "User %s not filtered", argv[i]); } } return CLI_OK; } static int cmd_show_access_list(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, "NAME", "Filter name", NULL); if (argc < 1) { cli_error(cli, "Specify a filter name"); return CLI_OK; } for (i = 0; i < argc; i++) { int f = find_filter(argv[i], strlen(argv[i])); ip_filter_rulet *rules; if (f < 0 || !*ip_filters[f].name) { cli_error(cli, "Access-list %s not defined", argv[i]); return CLI_OK; } if (i) cli_print(cli, " "); cli_print(cli, "%s IP access list %s", ip_filters[f].extended ? "Extended" : "Standard", ip_filters[f].name); for (rules = ip_filters[f].rules; rules->action; rules++) { char const *r = show_access_list_rule(ip_filters[f].extended, rules); if (rules->counter) cli_print(cli, "%s (%u match%s)", r, rules->counter, rules->counter > 1 ? "es" : ""); else cli_print(cli, "%s", r); } } return CLI_OK; } static int cmd_shutdown(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; kill(getppid(), SIGQUIT); return CLI_OK; } static int cmd_reload(struct cli_def *cli, const char *command, char **argv, int argc) { if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; kill(getppid(), SIGHUP); return CLI_OK; } static int cmd_setforward(struct cli_def *cli, const char *command, char **argv, int argc) { int ret; if (CLI_HELP_REQUESTED) { switch (argc) { case 1: return cli_arg_help(cli, 0, "MASK", "Users mask to forward (ex: myISP@operator.com)", NULL); case 2: return cli_arg_help(cli, 0, "IP", "IP of the remote LNS(ex: 64.64.64.64)", NULL); case 3: return cli_arg_help(cli, 0, "PORT", "Port of the remote LNS (ex: 1701)", NULL); case 4: return cli_arg_help(cli, 0, "SECRET", "l2tp secret of the remote LNS (ex: mysecretpsw)", NULL); default: return cli_arg_help(cli, argc > 1, NULL); } } if (argc != 4) { cli_error(cli, "Specify variable and value"); return CLI_OK; } // lac_addremotelns(mask, IP_RemoteLNS, Port_RemoteLNS, SecretRemoteLNS) ret = lac_addremotelns(argv[0], argv[1], argv[2], argv[3]); if (ret) { cli_print(cli, "setforward %s %s %s %s", argv[0], argv[1], argv[2], argv[3]); if (ret == 2) cli_print(cli, "%s Updated, the tunnel must be dropped", argv[0]); } else cli_error(cli, "ERROR setforward %s %s %s %s", argv[0], argv[1], argv[2], argv[3]); return CLI_OK; } static int cmd_show_rmtlnsconf(struct cli_def *cli, const char *command, char **argv, int argc) { confrlnsidt idrlns; char strdisp[1024]; if (CLI_HELP_REQUESTED) { return cli_arg_help(cli, 0, "remotelns-conf", "Show a list of remote LNS configurations", NULL); } for (idrlns = 0; idrlns < MAXRLNSTUNNEL; idrlns++) { if (lac_cli_show_remotelns(idrlns, strdisp) != 0) cli_print(cli, "%s", strdisp); else break; } return CLI_OK; } l2tpns-2.3.3/cluster.c000066400000000000000000002140341400724550600145750ustar00rootroot00000000000000// L2TPNS Clustering Stuff #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "cluster.h" #include "util.h" #include "tbf.h" #include "pppoe.h" #ifdef BGP #include "bgp.h" #endif /* * All cluster packets have the same format. * * One or more instances of * a 32 bit 'type' id. * a 32 bit 'extra' data dependant on the 'type'. * zero or more bytes of structure data, dependant on the type. * */ // Module variables. extern int cluster_sockfd; // The filedescriptor for the cluster communications port. in_addr_t my_address = 0; // The network address of my ethernet port. static int walk_session_number = 0; // The next session to send when doing the slow table walk. static int walk_bundle_number = 0; // The next bundle to send when doing the slow table walk. static int walk_tunnel_number = 0; // The next tunnel to send when doing the slow table walk. int forked = 0; // Sanity check: CLI must not diddle with heartbeat table #define MAX_HEART_SIZE (8192) // Maximum size of heartbeat packet. Must be less than max IP packet size :) #define MAX_CHANGES (MAX_HEART_SIZE/(sizeof(sessiont) + sizeof(int) ) - 2) // Assumes a session is the biggest type! static struct { int type; int id; } cluster_changes[MAX_CHANGES]; // Queue of changed structures that need to go out when next heartbeat. static struct { int seq; int size; uint8_t data[MAX_HEART_SIZE]; } past_hearts[HB_HISTORY_SIZE]; // Ring buffer of heartbeats that we've recently sent out. Needed so // we can re-transmit if needed. static struct { in_addr_t peer; uint32_t basetime; clockt timestamp; int uptodate; } peers[CLUSTER_MAX_SIZE]; // List of all the peers we've heard from. static int num_peers; // Number of peers in list. static int rle_decompress(uint8_t **src_p, int ssize, uint8_t *dst, int dsize); static int rle_compress(uint8_t **src_p, int ssize, uint8_t *dst, int dsize); // // Create a listening socket // // This joins the cluster multi-cast group. // int cluster_init() { struct sockaddr_in addr; struct sockaddr_in interface_addr; struct ip_mreq mreq; struct ifreq ifr; int opt; config->cluster_undefined_sessions = MAXSESSION-1; config->cluster_undefined_bundles = MAXBUNDLE-1; config->cluster_undefined_tunnels = MAXTUNNEL-1; if (!config->cluster_address) return 0; if (!*config->cluster_interface) return 0; cluster_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(config->cluster_port); addr.sin_addr.s_addr = INADDR_ANY; setsockopt(cluster_sockfd, SOL_SOCKET, SO_REUSEADDR, &addr, sizeof(addr)); opt = fcntl(cluster_sockfd, F_GETFL, 0); fcntl(cluster_sockfd, F_SETFL, opt | O_NONBLOCK); if (bind(cluster_sockfd, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Failed to bind cluster socket: %s\n", strerror(errno)); return -1; } strcpy(ifr.ifr_name, config->cluster_interface); if (ioctl(cluster_sockfd, SIOCGIFADDR, &ifr) < 0) { LOG(0, 0, 0, "Failed to get interface address for (%s): %s\n", config->cluster_interface, strerror(errno)); return -1; } memcpy(&interface_addr, &ifr.ifr_addr, sizeof(interface_addr)); my_address = interface_addr.sin_addr.s_addr; // Join multicast group. mreq.imr_multiaddr.s_addr = config->cluster_address; mreq.imr_interface = interface_addr.sin_addr; opt = 0; // Turn off multicast loopback. setsockopt(cluster_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &opt, sizeof(opt)); if (config->cluster_mcast_ttl != 1) { uint8_t ttl = 0; if (config->cluster_mcast_ttl > 0) ttl = config->cluster_mcast_ttl < 256 ? config->cluster_mcast_ttl : 255; setsockopt(cluster_sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); } if (setsockopt(cluster_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { LOG(0, 0, 0, "Failed to setsockopt (join mcast group): %s\n", strerror(errno)); return -1; } if (setsockopt(cluster_sockfd, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr)) < 0) { LOG(0, 0, 0, "Failed to setsockopt (set mcast interface): %s\n", strerror(errno)); return -1; } config->cluster_last_hb = TIME; config->cluster_seq_number = -1; return cluster_sockfd; } // // Send a chunk of data to the entire cluster (usually via the multicast // address ). // static int cluster_send_data(void *data, int datalen) { struct sockaddr_in addr = {0}; if (!cluster_sockfd) return -1; if (!config->cluster_address) return 0; addr.sin_addr.s_addr = config->cluster_address; addr.sin_port = htons(config->cluster_port); addr.sin_family = AF_INET; LOG(5, 0, 0, "Cluster send data: %d bytes\n", datalen); if (sendto(cluster_sockfd, data, datalen, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "sendto: %s\n", strerror(errno)); return -1; } return 0; } // // Add a chunk of data to a heartbeat packet. // Maintains the format. Assumes that the caller // has passed in a big enough buffer! // static void add_type(uint8_t **p, int type, int more, uint8_t *data, int size) { *((uint32_t *) (*p)) = type; *p += sizeof(uint32_t); *((uint32_t *)(*p)) = more; *p += sizeof(uint32_t); if (data && size > 0) { memcpy(*p, data, size); *p += size; } } // advertise our presence via BGP or gratuitous ARP static void advertise_routes(void) { #ifdef BGP if (bgp_configured) bgp_enable_routing(1); else #endif /* BGP */ if (config->send_garp) send_garp(config->bind_address); // Start taking traffic. } // withdraw our routes (BGP only) static void withdraw_routes(void) { #ifdef BGP if (bgp_configured) bgp_enable_routing(0); #endif /* BGP */ } static void cluster_uptodate(void) { if (config->cluster_iam_uptodate) return; if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles) return; config->cluster_iam_uptodate = 1; LOG(0, 0, 0, "Now uptodate with master.\n"); advertise_routes(); } // // Send a unicast UDP packet to a peer with 'data' as the // contents. // static int peer_send_data(in_addr_t peer, uint8_t *data, int size) { struct sockaddr_in addr = {0}; if (!cluster_sockfd) return -1; if (!config->cluster_address) return 0; if (!peer) // Odd?? return -1; addr.sin_addr.s_addr = peer; addr.sin_port = htons(config->cluster_port); addr.sin_family = AF_INET; LOG_HEX(5, "Peer send", data, size); if (sendto(cluster_sockfd, data, size, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "sendto: %s\n", strerror(errno)); return -1; } return 0; } // // Send a structured message to a peer with a single element of type 'type'. // static int peer_send_message(in_addr_t peer, int type, int more, uint8_t *data, int size) { uint8_t buf[65536]; // Vast overkill. uint8_t *p = buf; LOG(4, 0, 0, "Sending message to peer (type %d, more %d, size %d)\n", type, more, size); add_type(&p, type, more, data, size); return peer_send_data(peer, buf, (p-buf) ); } // send a packet to the master static int _forward_packet(uint8_t *data, int size, in_addr_t addr, int port, int type) { uint8_t buf[65536]; // Vast overkill. uint8_t *p = buf; if (!config->cluster_master_address) // No election has been held yet. Just skip it. return -1; LOG(4, 0, 0, "Forwarding packet from %s to master (size %d)\n", fmtaddr(addr, 0), size); STAT(c_forwarded); add_type(&p, type, addr, (uint8_t *) &port, sizeof(port)); // ick. should be uint16_t memcpy(p, data, size); p += size; return peer_send_data(config->cluster_master_address, buf, (p - buf)); } // // Forward a state changing packet to the master. // // The master just processes the payload as if it had // received it off the tun device. //(note: THIS ROUTINE WRITES TO pack[-6]). int master_forward_packet(uint8_t *data, int size, in_addr_t addr, uint16_t port, uint16_t indexudp) { uint8_t *p = data - (3 * sizeof(uint32_t)); uint8_t *psave = p; uint32_t indexandport = port | ((indexudp << 16) & 0xFFFF0000); if (!config->cluster_master_address) // No election has been held yet. Just skip it. return -1; LOG(4, 0, 0, "Forwarding packet from %s to master (size %d)\n", fmtaddr(addr, 0), size); STAT(c_forwarded); add_type(&p, C_FORWARD, addr, (uint8_t *) &indexandport, sizeof(indexandport)); return peer_send_data(config->cluster_master_address, psave, size + (3 * sizeof(uint32_t))); } // Forward PPPOE packet to the master. //(note: THIS ROUTINE WRITES TO pack[-4]). int master_forward_pppoe_packet(uint8_t *data, int size, uint8_t codepad) { uint8_t *p = data - (2 * sizeof(uint32_t)); uint8_t *psave = p; if (!config->cluster_master_address) // No election has been held yet. Just skip it. return -1; LOG(4, 0, 0, "Forward PPPOE packet to master, code %s (size %d)\n", get_string_codepad(codepad), size); STAT(c_forwarded); add_type(&p, C_PPPOE_FORWARD, codepad, NULL, 0); return peer_send_data(config->cluster_master_address, psave, size + (2 * sizeof(uint32_t))); } // Forward a DAE RADIUS packet to the master. int master_forward_dae_packet(uint8_t *data, int size, in_addr_t addr, int port) { return _forward_packet(data, size, addr, port, C_FORWARD_DAE); } // // Forward a throttled packet to the master for handling. // // The master just drops the packet into the appropriate // token bucket queue, and lets normal processing take care // of it. // int master_throttle_packet(int tbfid, uint8_t *data, int size) { uint8_t buf[65536]; // Vast overkill. uint8_t *p = buf; if (!config->cluster_master_address) // No election has been held yet. Just skip it. return -1; LOG(4, 0, 0, "Throttling packet master (size %d, tbfid %d)\n", size, tbfid); add_type(&p, C_THROTTLE, tbfid, data, size); return peer_send_data(config->cluster_master_address, buf, (p-buf) ); } // // Forward a walled garden packet to the master for handling. // // The master just writes the packet straight to the tun // device (where is will normally loop through the // firewall rules, and come back in on the tun device) // // (Note that this must be called with the tun header // as the start of the data). int master_garden_packet(sessionidt s, uint8_t *data, int size) { uint8_t buf[65536]; // Vast overkill. uint8_t *p = buf; if (!config->cluster_master_address) // No election has been held yet. Just skip it. return -1; LOG(4, 0, 0, "Walled garden packet to master (size %d)\n", size); add_type(&p, C_GARDEN, s, data, size); return peer_send_data(config->cluster_master_address, buf, (p-buf)); } // // Forward a MPPP packet to the master for handling. // // (Note that this must be called with the tun header // as the start of the data). // (i.e. this routine writes to data[-8]). int master_forward_mppp_packet(sessionidt s, uint8_t *data, int size) { uint8_t *p = data - (2 * sizeof(uint32_t)); uint8_t *psave = p; if (!config->cluster_master_address) // No election has been held yet. Just skip it. return -1; LOG(4, 0, 0, "Forward MPPP packet to master (size %d)\n", size); add_type(&p, C_MPPP_FORWARD, s, NULL, 0); return peer_send_data(config->cluster_master_address, psave, size + (2 * sizeof(uint32_t))); } // // Send a chunk of data as a heartbeat.. // We save it in the history buffer as we do so. // static void send_heartbeat(int seq, uint8_t *data, int size) { int i; if (size > sizeof(past_hearts[0].data)) { LOG(0, 0, 0, "Tried to heartbeat something larger than the maximum packet!\n"); kill(0, SIGTERM); exit(1); } i = seq % HB_HISTORY_SIZE; past_hearts[i].seq = seq; past_hearts[i].size = size; memcpy(&past_hearts[i].data, data, size); // Save it. cluster_send_data(data, size); } // // Send an 'i am alive' message to every machine in the cluster. // void cluster_send_ping(time_t basetime) { uint8_t buff[100 + sizeof(pingt)]; uint8_t *p = buff; pingt x; if (config->cluster_iam_master && basetime) // We're heartbeating so no need to ping. return; LOG(5, 0, 0, "Sending cluster ping...\n"); x.ver = 1; x.addr = config->bind_address; x.undef = config->cluster_undefined_sessions + config->cluster_undefined_tunnels + config->cluster_undefined_bundles; x.basetime = basetime; add_type(&p, C_PING, basetime, (uint8_t *) &x, sizeof(x)); cluster_send_data(buff, (p-buff) ); } // // Walk the session counters looking for non-zero ones to send // to the master. We send up to 600 of them at one time. // We examine a maximum of 3000 sessions. // (50k max session should mean that we normally // examine the entire session table every 25 seconds). #define MAX_B_RECS (600) void master_update_counts(void) { int i, c; bytest b[MAX_B_RECS+1]; if (config->cluster_iam_master) // Only happens on the slaves. return; if (!config->cluster_master_address) // If we don't have a master, skip it for a while. return; i = MAX_B_RECS * 5; // Examine max 3000 sessions; if (config->cluster_highest_sessionid > i) i = config->cluster_highest_sessionid; for ( c = 0; i > 0 ; --i) { // Next session to look at. walk_session_number++; if ( walk_session_number > config->cluster_highest_sessionid) walk_session_number = 1; if (!sess_local[walk_session_number].cin && !sess_local[walk_session_number].cout) continue; // Unchanged. Skip it. b[c].sid = walk_session_number; b[c].pin = sess_local[walk_session_number].pin; b[c].pout = sess_local[walk_session_number].pout; b[c].cin = sess_local[walk_session_number].cin; b[c].cout = sess_local[walk_session_number].cout; // Reset counters. sess_local[walk_session_number].pin = sess_local[walk_session_number].pout = 0; sess_local[walk_session_number].cin = sess_local[walk_session_number].cout = 0; if (++c > MAX_B_RECS) // Send a max of 600 elements in a packet. break; } if (!c) // Didn't find any that changes. Get out of here! return; // Forward the data to the master. LOG(4, 0, 0, "Sending byte counters to master (%d elements)\n", c); peer_send_message(config->cluster_master_address, C_BYTES, c, (uint8_t *) &b, sizeof(b[0]) * c); return; } // // On the master, check how our slaves are going. If // one of them's not up-to-date we'll heartbeat faster. // If we don't have any of them, then we need to turn // on our own packet handling! // void cluster_check_slaves(void) { int i; static int have_peers = 0; int had_peers = have_peers; clockt t = TIME; if (!config->cluster_iam_master) return; // Only runs on the master... config->cluster_iam_uptodate = 1; // cleared in loop below for (i = have_peers = 0; i < num_peers; i++) { if ((peers[i].timestamp + config->cluster_hb_timeout) < t) continue; // Stale peer! Skip them. if (!peers[i].basetime) continue; // Shutdown peer! Skip them. if (peers[i].uptodate) have_peers++; else config->cluster_iam_uptodate = 0; // Start fast heartbeats } // in a cluster, withdraw/add routes when we get a peer/lose peers if (have_peers != had_peers) { if (had_peers < config->cluster_master_min_adv && have_peers >= config->cluster_master_min_adv) withdraw_routes(); else if (had_peers >= config->cluster_master_min_adv && have_peers < config->cluster_master_min_adv) advertise_routes(); } } // // Check that we have a master. If it's been too // long since we heard from a master then hold an election. // void cluster_check_master(void) { int i, count, high_unique_id = 0; int last_free = 0; clockt t = TIME; static int probed = 0; int have_peers; if (config->cluster_iam_master) return; // Only runs on the slaves... // If the master is late (missed 2 hearbeats by a second and a // hair) it may be that the switch has dropped us from the // multicast group, try unicasting probes to the master // which will hopefully respond with a unicast heartbeat that // will allow us to limp along until the querier next runs. if (config->cluster_master_address && TIME > (config->cluster_last_hb + 2 * config->cluster_hb_interval + 11)) { if (!probed || (TIME > (probed + 2 * config->cluster_hb_interval))) { probed = TIME; LOG(1, 0, 0, "Heartbeat from master %.1fs late, probing...\n", 0.1 * (TIME - (config->cluster_last_hb + config->cluster_hb_interval))); peer_send_message(config->cluster_master_address, C_LASTSEEN, config->cluster_seq_number, NULL, 0); } } else { // We got a recent heartbeat; reset the probe flag. probed = 0; } if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout)) return; // Everything's ok! config->cluster_last_hb = TIME + 1; // Just the one election thanks. config->cluster_master_address = 0; LOG(0, 0, 0, "Master timed out! Holding election...\n"); // In the process of shutting down, can't be master if (main_quit) return; for (i = have_peers = 0; i < num_peers; i++) { if ((peers[i].timestamp + config->cluster_hb_timeout) < t) continue; // Stale peer! Skip them. if (!peers[i].basetime) continue; // Shutdown peer! Skip them. if (peers[i].basetime < basetime) { LOG(1, 0, 0, "Expecting %s to become master\n", fmtaddr(peers[i].peer, 0)); return; // They'll win the election. Get out of here. } if (peers[i].basetime == basetime && peers[i].peer > my_address) { LOG(1, 0, 0, "Expecting %s to become master\n", fmtaddr(peers[i].peer, 0)); return; // They'll win the election. Wait for them to come up. } if (peers[i].uptodate) have_peers++; } // Wow. it's been ages since I last heard a heartbeat // and I'm better than an of my peers so it's time // to become a master!!! config->cluster_iam_master = 1; pppoe_send_garp(); // gratuitous arp of the pppoe interface LOG(0, 0, 0, "I am declaring myself the master!\n"); if (have_peers < config->cluster_master_min_adv) advertise_routes(); else withdraw_routes(); if (config->cluster_seq_number == -1) config->cluster_seq_number = 0; // // Go through and mark all the tunnels as defined. // Count the highest used tunnel number as well. // config->cluster_highest_tunnelid = 0; for (i = 0; i < MAXTUNNEL; ++i) { if (tunnel[i].state == TUNNELUNDEF) tunnel[i].state = TUNNELFREE; if (tunnel[i].state != TUNNELFREE && i > config->cluster_highest_tunnelid) config->cluster_highest_tunnelid = i; } // // Go through and mark all the bundles as defined. // Count the highest used bundle number as well. // config->cluster_highest_bundleid = 0; for (i = 0; i < MAXBUNDLE; ++i) { if (bundle[i].state == BUNDLEUNDEF) bundle[i].state = BUNDLEFREE; if (bundle[i].state != BUNDLEFREE && i > config->cluster_highest_bundleid) config->cluster_highest_bundleid = i; } // // Go through and mark all the sessions as being defined. // reset the idle timeouts. // add temporary byte counters to permanent ones. // Re-string the free list. // Find the ID of the highest session. last_free = 0; high_unique_id = 0; config->cluster_highest_sessionid = 0; for (i = 0, count = 0; i < MAXSESSION; ++i) { if (session[i].tunnel == T_UNDEF) { session[i].tunnel = T_FREE; ++count; } if (!session[i].opened) { // Unused session. Add to free list. memset(&session[i], 0, sizeof(session[i])); session[i].tunnel = T_FREE; session[last_free].next = i; session[i].next = 0; last_free = i; continue; } // Reset idle timeouts.. session[i].last_packet = session[i].last_data = time_now; // Reset die relative to our uptime rather than the old master's if (session[i].die) session[i].die = TIME; // Accumulate un-sent byte/packet counters. increment_counter(&session[i].cin, &session[i].cin_wrap, sess_local[i].cin); increment_counter(&session[i].cout, &session[i].cout_wrap, sess_local[i].cout); session[i].cin_delta += sess_local[i].cin; session[i].cout_delta += sess_local[i].cout; session[i].pin += sess_local[i].pin; session[i].pout += sess_local[i].pout; sess_local[i].cin = sess_local[i].cout = 0; sess_local[i].pin = sess_local[i].pout = 0; sess_local[i].radius = 0; // Reset authentication as the radius blocks aren't up to date. if (session[i].unique_id >= high_unique_id) // This is different to the index into the session table!!! high_unique_id = session[i].unique_id+1; session[i].tbf_in = session[i].tbf_out = 0; // Remove stale pointers from old master. throttle_session(i, session[i].throttle_in, session[i].throttle_out); config->cluster_highest_sessionid = i; } session[last_free].next = 0; // End of chain. last_id = high_unique_id; // Keep track of the highest used session ID. become_master(); rebuild_address_pool(); // If we're not the very first master, this is a big issue! if (count > 0) LOG(0, 0, 0, "Warning: Fixed %d uninitialized sessions in becoming master!\n", count); config->cluster_undefined_sessions = 0; config->cluster_undefined_bundles = 0; config->cluster_undefined_tunnels = 0; config->cluster_iam_uptodate = 1; // assume all peers are up-to-date // FIXME. We need to fix up the tunnel control message // queue here! There's a number of other variables we // should also update. } // // Check that our session table is validly matching what the // master has in mind. // // In particular, if we have too many sessions marked 'undefined' // we fix it up here, and we ensure that the 'first free session' // pointer is valid. // static void cluster_check_sessions(int highsession, int freesession_ptr, int highbundle, int hightunnel) { int i; sessionfree = freesession_ptr; // Keep the freesession ptr valid. if (config->cluster_iam_uptodate) return; if (highsession > config->cluster_undefined_sessions && highbundle > config->cluster_undefined_bundles && hightunnel > config->cluster_undefined_tunnels) return; // Clear out defined sessions, counting the number of // undefs remaining. config->cluster_undefined_sessions = 0; for (i = 1 ; i < MAXSESSION; ++i) { if (i > highsession) { if (session[i].tunnel == T_UNDEF) session[i].tunnel = T_FREE; // Defined. continue; } if (session[i].tunnel == T_UNDEF) ++config->cluster_undefined_sessions; } // Clear out defined bundles, counting the number of // undefs remaining. config->cluster_undefined_bundles = 0; for (i = 1 ; i < MAXBUNDLE; ++i) { if (i > highbundle) { if (bundle[i].state == BUNDLEUNDEF) bundle[i].state = BUNDLEFREE; // Defined. continue; } if (bundle[i].state == BUNDLEUNDEF) ++config->cluster_undefined_bundles; } // Clear out defined tunnels, counting the number of // undefs remaining. config->cluster_undefined_tunnels = 0; for (i = 1 ; i < MAXTUNNEL; ++i) { if (i > hightunnel) { if (tunnel[i].state == TUNNELUNDEF) tunnel[i].state = TUNNELFREE; // Defined. continue; } if (tunnel[i].state == TUNNELUNDEF) ++config->cluster_undefined_tunnels; } if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles) { LOG(2, 0, 0, "Cleared undefined sessions/bundles/tunnels. %d sess (high %d), %d bund (high %d), %d tunn (high %d)\n", config->cluster_undefined_sessions, highsession, config->cluster_undefined_bundles, highbundle, config->cluster_undefined_tunnels, hightunnel); return; } // Are we up to date? if (!config->cluster_iam_uptodate) cluster_uptodate(); } static int hb_add_type(uint8_t **p, int type, int id) { switch (type) { case C_CSESSION: { // Compressed C_SESSION. uint8_t c[sizeof(sessiont) * 2]; // Bigger than worst case. uint8_t *d = (uint8_t *) &session[id]; uint8_t *orig = d; int size; size = rle_compress( &d, sizeof(sessiont), c, sizeof(c) ); // Did we compress the full structure, and is the size actually // reduced?? if ( (d - orig) == sizeof(sessiont) && size < sizeof(sessiont) ) { add_type(p, C_CSESSION, id, c, size); break; } // Failed to compress : Fall through. } case C_SESSION: add_type(p, C_SESSION, id, (uint8_t *) &session[id], sizeof(sessiont)); break; case C_CBUNDLE: { // Compressed C_BUNDLE uint8_t c[sizeof(bundlet) * 2]; // Bigger than worst case. uint8_t *d = (uint8_t *) &bundle[id]; uint8_t *orig = d; int size; size = rle_compress( &d, sizeof(bundlet), c, sizeof(c) ); // Did we compress the full structure, and is the size actually // reduced?? if ( (d - orig) == sizeof(bundlet) && size < sizeof(bundlet) ) { add_type(p, C_CBUNDLE, id, c, size); break; } // Failed to compress : Fall through. } case C_BUNDLE: add_type(p, C_BUNDLE, id, (uint8_t *) &bundle[id], sizeof(bundlet)); break; case C_CTUNNEL: { // Compressed C_TUNNEL uint8_t c[sizeof(tunnelt) * 2]; // Bigger than worst case. uint8_t *d = (uint8_t *) &tunnel[id]; uint8_t *orig = d; int size; size = rle_compress( &d, sizeof(tunnelt), c, sizeof(c) ); // Did we compress the full structure, and is the size actually // reduced?? if ( (d - orig) == sizeof(tunnelt) && size < sizeof(tunnelt) ) { add_type(p, C_CTUNNEL, id, c, size); break; } // Failed to compress : Fall through. } case C_TUNNEL: add_type(p, C_TUNNEL, id, (uint8_t *) &tunnel[id], sizeof(tunnelt)); break; default: LOG(0, 0, 0, "Found an invalid type in heart queue! (%d)\n", type); kill(0, SIGTERM); exit(1); } return 0; } // // Send a heartbeat, incidently sending out any queued changes.. // void cluster_heartbeat() { int i, count = 0, tcount = 0, bcount = 0; uint8_t buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ]; heartt h; uint8_t *p = buff; if (!config->cluster_iam_master) // Only the master does this. return; config->cluster_table_version += config->cluster_num_changes; // Fill out the heartbeat header. memset(&h, 0, sizeof(h)); h.version = HB_VERSION; h.seq = config->cluster_seq_number; h.basetime = basetime; h.clusterid = config->bind_address; // Will this do?? h.basetime = basetime; h.highsession = config->cluster_highest_sessionid; h.freesession = sessionfree; h.hightunnel = config->cluster_highest_tunnelid; h.highbundle = config->cluster_highest_bundleid; h.size_sess = sizeof(sessiont); // Just in case. h.size_bund = sizeof(bundlet); h.size_tunn = sizeof(tunnelt); h.interval = config->cluster_hb_interval; h.timeout = config->cluster_hb_timeout; h.table_version = config->cluster_table_version; add_type(&p, C_HEARTBEAT, HB_VERSION, (uint8_t *) &h, sizeof(h)); for (i = 0; i < config->cluster_num_changes; ++i) { hb_add_type(&p, cluster_changes[i].type, cluster_changes[i].id); } if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? LOG(0, 0, 0, "FATAL: Overran the heartbeat buffer! This is fatal. Exiting. (size %d)\n", (int) (p - buff)); kill(0, SIGTERM); exit(1); } // // Fill out the packet with sessions from the session table... // (not forgetting to leave space so we can get some tunnels in too ) while ( (p + sizeof(uint32_t) * 2 + sizeof(sessiont) * 2 ) < (buff + MAX_HEART_SIZE) ) { if (!walk_session_number) // session #0 isn't valid. ++walk_session_number; if (count >= config->cluster_highest_sessionid) // If we're a small cluster, don't go wild. break; hb_add_type(&p, C_CSESSION, walk_session_number); walk_session_number = (1+walk_session_number)%(config->cluster_highest_sessionid+1); // +1 avoids divide by zero. ++count; // Count the number of extra sessions we're sending. } // // Fill out the packet with tunnels from the tunnel table... // This effectively means we walk the tunnel table more quickly // than the session table. This is good because stuffing up a // tunnel is a much bigger deal than stuffing up a session. // while ( (p + sizeof(uint32_t) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE) ) { if (!walk_tunnel_number) // tunnel #0 isn't valid. ++walk_tunnel_number; if (tcount >= config->cluster_highest_tunnelid) break; hb_add_type(&p, C_CTUNNEL, walk_tunnel_number); walk_tunnel_number = (1+walk_tunnel_number)%(config->cluster_highest_tunnelid+1); // +1 avoids divide by zero. ++tcount; } // // Fill out the packet with bundles from the bundle table... while ( (p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE) ) { if (!walk_bundle_number) // bundle #0 isn't valid. ++walk_bundle_number; if (bcount >= config->cluster_highest_bundleid) break; hb_add_type(&p, C_CBUNDLE, walk_bundle_number); walk_bundle_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1); // +1 avoids divide by zero. ++bcount; } // // Did we do something wrong? if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? LOG(0, 0, 0, "Overran the heartbeat buffer now! This is fatal. Exiting. (size %d)\n", (int) (p - buff)); kill(0, SIGTERM); exit(1); } LOG(4, 0, 0, "Sending v%d heartbeat #%d, change #%" PRIu64 " with %d changes " "(%d x-sess, %d x-bundles, %d x-tunnels, %d highsess, %d highbund, %d hightun, size %d)\n", HB_VERSION, h.seq, h.table_version, config->cluster_num_changes, count, bcount, tcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid, config->cluster_highest_tunnelid, (int) (p - buff)); config->cluster_num_changes = 0; send_heartbeat(h.seq, buff, (p-buff) ); // Send out the heartbeat to the cluster, keeping a copy of it. config->cluster_seq_number = (config->cluster_seq_number+1)%HB_MAX_SEQ; // Next seq number to use. } // // A structure of type 'type' has changed; Add it to the queue to send. // static int type_changed(int type, int id) { int i; for (i = 0 ; i < config->cluster_num_changes ; ++i) { if ( cluster_changes[i].id == id && cluster_changes[i].type == type) { // Already marked for change, remove it --config->cluster_num_changes; memmove(&cluster_changes[i], &cluster_changes[i+1], (config->cluster_num_changes - i) * sizeof(cluster_changes[i])); break; } } cluster_changes[config->cluster_num_changes].type = type; cluster_changes[config->cluster_num_changes].id = id; ++config->cluster_num_changes; if (config->cluster_num_changes > MAX_CHANGES) cluster_heartbeat(); // flush now return 1; } // A particular session has been changed! int cluster_send_session(int sid) { if (!config->cluster_iam_master) { LOG(0, sid, 0, "I'm not a master, but I just tried to change a session!\n"); return -1; } if (forked) { LOG(0, sid, 0, "cluster_send_session called from child process!\n"); return -1; } return type_changed(C_CSESSION, sid); } // A particular bundle has been changed! int cluster_send_bundle(int bid) { if (!config->cluster_iam_master) { LOG(0, 0, bid, "I'm not a master, but I just tried to change a bundle!\n"); return -1; } return type_changed(C_CBUNDLE, bid); } // A particular tunnel has been changed! int cluster_send_tunnel(int tid) { if (!config->cluster_iam_master) { LOG(0, 0, tid, "I'm not a master, but I just tried to change a tunnel!\n"); return -1; } return type_changed(C_CTUNNEL, tid); } // // We're a master, and a slave has just told us that it's // missed a packet. We'll resend it every packet since // the last one it's seen. // static int cluster_catchup_slave(int seq, in_addr_t slave) { int s; int diff; LOG(1, 0, 0, "Slave %s sent LASTSEEN with seq %d\n", fmtaddr(slave, 0), seq); if (!config->cluster_iam_master) { LOG(1, 0, 0, "Got LASTSEEN but I'm not a master! Redirecting it to %s.\n", fmtaddr(config->cluster_master_address, 0)); peer_send_message(slave, C_MASTER, config->cluster_master_address, NULL, 0); return 0; } diff = config->cluster_seq_number - seq; // How many packet do we need to send? if (diff < 0) diff += HB_MAX_SEQ; if (diff >= HB_HISTORY_SIZE) { // Ouch. We don't have the packet to send it! LOG(0, 0, 0, "A slave asked for message %d when our seq number is %d. Killing it.\n", seq, config->cluster_seq_number); return peer_send_message(slave, C_KILL, seq, NULL, 0);// Kill the slave. Nothing else to do. } LOG(1, 0, 0, "Sending %d catchup packets to slave %s\n", diff, fmtaddr(slave, 0) ); // Now resend every packet that it missed, in order. while (seq != config->cluster_seq_number) { s = seq % HB_HISTORY_SIZE; if (seq != past_hearts[s].seq) { LOG(0, 0, 0, "Tried to re-send heartbeat for %s but %d doesn't match %d! (%d,%d)\n", fmtaddr(slave, 0), seq, past_hearts[s].seq, s, config->cluster_seq_number); return -1; // What to do here!? } peer_send_data(slave, past_hearts[s].data, past_hearts[s].size); seq = (seq+1)%HB_MAX_SEQ; // Increment to next seq number. } return 0; // All good! } // // We've heard from another peer! Add it to the list // that we select from at election time. // static int cluster_add_peer(in_addr_t peer, time_t basetime, pingt *pp, int size) { int i; in_addr_t clusterid; pingt p; // Allow for backward compatability. // Just the ping packet into a new structure to allow // for the possibility that we might have received // more or fewer elements than we were expecting. if (size > sizeof(p)) size = sizeof(p); memset( (void *) &p, 0, sizeof(p) ); memcpy( (void *) &p, (void *) pp, size); clusterid = p.addr; if (clusterid != config->bind_address) { // Is this for us? LOG(4, 0, 0, "Skipping ping from %s (different cluster)\n", fmtaddr(peer, 0)); return 0; } for (i = 0; i < num_peers ; ++i) { if (peers[i].peer != peer) continue; // This peer already exists. Just update the timestamp. peers[i].basetime = basetime; peers[i].timestamp = TIME; peers[i].uptodate = !p.undef; break; } // Is this the master shutting down?? if (peer == config->cluster_master_address) { LOG(3, 0, 0, "Master %s %s\n", fmtaddr(config->cluster_master_address, 0), basetime ? "has restarted!" : "shutting down..."); config->cluster_master_address = 0; config->cluster_last_hb = 0; // Force an election. cluster_check_master(); } if (i >= num_peers) { LOG(4, 0, 0, "Adding %s as a peer\n", fmtaddr(peer, 0)); // Not found. Is there a stale slot to re-use? for (i = 0; i < num_peers ; ++i) { if (!peers[i].basetime) // Shutdown break; if ((peers[i].timestamp + config->cluster_hb_timeout * 10) < TIME) // Stale. break; } if (i >= CLUSTER_MAX_SIZE) { // Too many peers!! LOG(0, 0, 0, "Tried to add %s as a peer, but I already have %d of them!\n", fmtaddr(peer, 0), i); return -1; } peers[i].peer = peer; peers[i].basetime = basetime; peers[i].timestamp = TIME; peers[i].uptodate = !p.undef; if (i == num_peers) ++num_peers; LOG(1, 0, 0, "Added %s as a new peer. Now %d peers\n", fmtaddr(peer, 0), num_peers); } return 1; } // A slave responds with C_MASTER when it gets a message which should have gone to a master. static int cluster_set_master(in_addr_t peer, in_addr_t master) { if (config->cluster_iam_master) // Sanity... return 0; LOG(3, 0, 0, "Peer %s set the master to %s...\n", fmtaddr(peer, 0), fmtaddr(master, 1)); config->cluster_master_address = master; if (master) { // catchup with new master peer_send_message(master, C_LASTSEEN, config->cluster_seq_number, NULL, 0); // delay next election config->cluster_last_hb = TIME; } // run election (or reset "probed" if master was set) cluster_check_master(); return 0; } /* Handle the slave updating the byte counters for the master. */ // // Note that we don't mark the session as dirty; We rely on // the slow table walk to propogate this back out to the slaves. // static int cluster_handle_bytes(uint8_t *data, int size) { bytest *b; b = (bytest *) data; LOG(3, 0, 0, "Got byte counter update (size %d)\n", size); /* Loop around, adding the byte counts to each of the sessions. */ while (size >= sizeof(*b) ) { if (b->sid > MAXSESSION) { LOG(0, 0, 0, "Got C_BYTES with session #%d!\n", b->sid); return -1; /* Abort processing */ } session[b->sid].pin += b->pin; session[b->sid].pout += b->pout; increment_counter(&session[b->sid].cin, &session[b->sid].cin_wrap, b->cin); increment_counter(&session[b->sid].cout, &session[b->sid].cout_wrap, b->cout); session[b->sid].cin_delta += b->cin; session[b->sid].cout_delta += b->cout; if (b->cin) session[b->sid].last_packet = session[b->sid].last_data = time_now; else if (b->cout) session[b->sid].last_data = time_now; size -= sizeof(*b); ++b; } if (size != 0) LOG(0, 0, 0, "Got C_BYTES with %d bytes of trailing junk!\n", size); return size; } // // Handle receiving a session structure in a heartbeat packet. // static int cluster_recv_session(int more, uint8_t *p) { if (more >= MAXSESSION) { LOG(0, 0, 0, "DANGER: Received a heartbeat session id > MAXSESSION!\n"); return -1; } if (session[more].tunnel == T_UNDEF) { if (config->cluster_iam_uptodate) { // Sanity. LOG(0, 0, 0, "I thought I was uptodate but I just found an undefined session!\n"); } else { --config->cluster_undefined_sessions; } } load_session(more, (sessiont *) p); // Copy session into session table.. LOG(5, more, 0, "Received session update (%d undef)\n", config->cluster_undefined_sessions); if (!config->cluster_iam_uptodate) cluster_uptodate(); // Check to see if we're up to date. return 0; } static int cluster_recv_bundle(int more, uint8_t *p) { if (more >= MAXBUNDLE) { LOG(0, 0, 0, "DANGER: Received a bundle id > MAXBUNDLE!\n"); return -1; } if (bundle[more].state == BUNDLEUNDEF) { if (config->cluster_iam_uptodate) { // Sanity. LOG(0, 0, 0, "I thought I was uptodate but I just found an undefined bundle!\n"); } else { --config->cluster_undefined_bundles; } } memcpy(&bundle[more], p, sizeof(bundle[more]) ); LOG(5, 0, more, "Received bundle update\n"); if (!config->cluster_iam_uptodate) cluster_uptodate(); // Check to see if we're up to date. return 0; } static int cluster_recv_tunnel(int more, uint8_t *p) { if (more >= MAXTUNNEL) { LOG(0, 0, 0, "DANGER: Received a tunnel session id > MAXTUNNEL!\n"); return -1; } if (tunnel[more].state == TUNNELUNDEF) { if (config->cluster_iam_uptodate) { // Sanity. LOG(0, 0, 0, "I thought I was uptodate but I just found an undefined tunnel!\n"); } else { --config->cluster_undefined_tunnels; } } memcpy(&tunnel[more], p, sizeof(tunnel[more]) ); // // Clear tunnel control messages. These are dynamically allocated. // If we get unlucky, this may cause the tunnel to drop! // tunnel[more].controls = tunnel[more].controle = NULL; tunnel[more].controlc = 0; LOG(5, 0, more, "Received tunnel update\n"); if (!config->cluster_iam_uptodate) cluster_uptodate(); // Check to see if we're up to date. return 0; } // pre v6 heartbeat session structure struct oldsession { sessionidt next; sessionidt far; tunnelidt tunnel; uint8_t flags; struct { uint8_t phase; uint8_t lcp:4; uint8_t ipcp:4; uint8_t ipv6cp:4; uint8_t ccp:4; } ppp; char reserved_1[2]; in_addr_t ip; int ip_pool_index; uint32_t unique_id; char reserved_2[4]; uint32_t magic; uint32_t pin, pout; uint32_t cin, cout; uint32_t cin_wrap, cout_wrap; uint32_t cin_delta, cout_delta; uint16_t throttle_in; uint16_t throttle_out; uint8_t filter_in; uint8_t filter_out; uint16_t mru; clockt opened; clockt die; uint32_t session_timeout; uint32_t idle_timeout; time_t last_packet; time_t last_data; in_addr_t dns1, dns2; routet route[MAXROUTE]; uint16_t tbf_in; uint16_t tbf_out; int random_vector_length; uint8_t random_vector[MAXTEL]; char user[MAXUSER]; char called[MAXTEL]; char calling[MAXTEL]; uint32_t tx_connect_speed; uint32_t rx_connect_speed; clockt timeout; uint32_t mrru; uint8_t mssf; epdist epdis; bundleidt bundle; in_addr_t snoop_ip; uint16_t snoop_port; uint8_t walled_garden; uint8_t ipv6prefixlen; struct in6_addr ipv6route; char reserved_3[11]; }; struct oldsessionV7 { 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; uint16_t mru; // maximum receive unit 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 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 snoop_port; // Interception destination port in_addr_t snoop_ip; // Interception destination IP clockt opened; // when started clockt die; // being closed, when to finally free uint32_t session_timeout; // Maximum session time in seconds uint32_t idle_timeout; // Maximum idle time in seconds time_t last_packet; // Last packet from the user (used for idle timeouts) time_t last_data; // Last data packet to/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 session for radius stop messages) char called[MAXTEL]; // called number char calling[MAXTEL]; // calling number uint32_t tx_connect_speed; uint32_t rx_connect_speed; clockt timeout; // Session timeout uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit epdist epdis; // Multilink Endpoint Discriminator bundleidt bundle; // Multilink Bundle Identifier uint8_t mssf; // Multilink Short Sequence Number Header Format uint8_t walled_garden; // is this session gardened? uint8_t classlen; // class (needed for radius accounting messages) char class[MAXCLASS]; uint8_t ipv6prefixlen; // IPv6 route prefix length struct in6_addr ipv6route; // Static IPv6 route sessionidt forwardtosession; // LNS id_session to forward uint8_t src_hwaddr[ETH_ALEN]; // MAC addr source (for pppoe sessions 6 bytes) char reserved[4]; // Space to expand structure without changing HB_VERSION }; struct oldsessionV8 { 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; uint16_t mru; // maximum receive unit 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 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 snoop_port; // Interception destination port in_addr_t snoop_ip; // Interception destination IP clockt opened; // when started clockt die; // being closed, when to finally free uint32_t session_timeout; // Maximum session time in seconds uint32_t idle_timeout; // Maximum idle time in seconds time_t last_packet; // Last packet from the user (used for idle timeouts) time_t last_data; // Last data packet to/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 session for radius stop messages) char called[MAXTEL]; // called number char calling[MAXTEL]; // calling number uint32_t tx_connect_speed; uint32_t rx_connect_speed; clockt timeout; // Session timeout uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit epdist epdis; // Multilink Endpoint Discriminator bundleidt bundle; // Multilink Bundle Identifier uint8_t mssf; // Multilink Short Sequence Number Header Format uint8_t walled_garden; // is this session gardened? uint8_t classlen; // class (needed for radius accounting messages) char class[MAXCLASS]; uint8_t ipv6prefixlen; // IPv6 route prefix length struct in6_addr ipv6route; // Static IPv6 route sessionidt forwardtosession; // LNS id_session to forward uint8_t src_hwaddr[ETH_ALEN]; // MAC addr source (for pppoe sessions 6 bytes) uint32_t dhcpv6_prefix_iaid; // prefix iaid requested by client uint32_t dhcpv6_iana_iaid; // iaid of iana requested by client struct in6_addr ipv6address; // Framed Ipv6 address struct dhcp6_opt_clientid dhcpv6_client_id; // Size max (headers + DUID) char reserved[4]; // Space to expand structure without changing HB_VERSION }; static uint8_t *convert_session(struct oldsession *old) { static sessiont new; int i; memset(&new, 0, sizeof(new)); new.next = old->next; new.far = old->far; new.tunnel = old->tunnel; new.flags = old->flags; new.ppp.phase = old->ppp.phase; new.ppp.lcp = old->ppp.lcp; new.ppp.ipcp = old->ppp.ipcp; new.ppp.ipv6cp = old->ppp.ipv6cp; new.ppp.ccp = old->ppp.ccp; new.ip = old->ip; new.ip_pool_index = old->ip_pool_index; new.unique_id = old->unique_id; new.magic = old->magic; new.pin = old->pin; new.pout = old->pout; new.cin = old->cin; new.cout = old->cout; new.cin_wrap = old->cin_wrap; new.cout_wrap = old->cout_wrap; new.cin_delta = old->cin_delta; new.cout_delta = old->cout_delta; new.throttle_in = old->throttle_in; new.throttle_out = old->throttle_out; new.filter_in = old->filter_in; new.filter_out = old->filter_out; new.mru = old->mru; new.opened = old->opened; new.die = old->die; new.session_timeout = old->session_timeout; new.idle_timeout = old->idle_timeout; new.last_packet = old->last_packet; new.last_data = old->last_data; new.dns1 = old->dns1; new.dns2 = old->dns2; new.tbf_in = old->tbf_in; new.tbf_out = old->tbf_out; new.random_vector_length = old->random_vector_length; new.tx_connect_speed = old->tx_connect_speed; new.rx_connect_speed = old->rx_connect_speed; new.timeout = old->timeout; new.mrru = old->mrru; new.mssf = old->mssf; new.epdis = old->epdis; new.bundle = old->bundle; new.snoop_ip = old->snoop_ip; new.snoop_port = old->snoop_port; new.walled_garden = old->walled_garden; new.route6[0].ipv6prefixlen = old->ipv6prefixlen; new.route6[0].ipv6route = old->ipv6route; memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector)); memcpy(new.user, old->user, sizeof(new.user)); memcpy(new.called, old->called, sizeof(new.called)); memcpy(new.calling, old->calling, sizeof(new.calling)); for (i = 0; i < MAXROUTE; i++) memcpy(&new.route[i], &old->route[i], sizeof(new.route[i])); return (uint8_t *) &new; } static uint8_t *convert_sessionV7(struct oldsessionV7 *old) { static sessiont new; int i; memset(&new, 0, sizeof(new)); new.next = old->next; new.far = old->far; new.tunnel = old->tunnel; new.flags = old->flags; new.ppp.phase = old->ppp.phase; new.ppp.lcp = old->ppp.lcp; new.ppp.ipcp = old->ppp.ipcp; new.ppp.ipv6cp = old->ppp.ipv6cp; new.ppp.ccp = old->ppp.ccp; new.mru = old->mru; new.ip = old->ip; new.ip_pool_index = old->ip_pool_index; new.unique_id = old->unique_id; new.magic = old->magic; new.pin = old->pin; new.pout = old->pout; new.cin = old->cin; new.cout = old->cout; new.cin_wrap = old->cin_wrap; new.cout_wrap = old->cout_wrap; new.cin_delta = old->cin_delta; new.cout_delta = old->cout_delta; new.throttle_in = old->throttle_in; new.throttle_out = old->throttle_out; new.filter_in = old->filter_in; new.filter_out = old->filter_out; new.snoop_port = old->snoop_port; new.snoop_ip = old->snoop_ip; new.opened = old->opened; new.die = old->die; new.session_timeout = old->session_timeout; new.idle_timeout = old->idle_timeout; new.last_packet = old->last_packet; new.last_data = old->last_data; new.dns1 = old->dns1; new.dns2 = old->dns2; for (i = 0; i < MAXROUTE; i++) memcpy(&new.route[i], &old->route[i], sizeof(new.route[i])); new.tbf_in = old->tbf_in; new.tbf_out = old->tbf_out; new.random_vector_length = old->random_vector_length; memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector)); memcpy(new.user, old->user, sizeof(new.user)); memcpy(new.called, old->called, sizeof(new.called)); memcpy(new.calling, old->calling, sizeof(new.calling)); new.tx_connect_speed = old->tx_connect_speed; new.rx_connect_speed = old->rx_connect_speed; new.timeout = old->timeout; new.mrru = old->mrru; new.epdis = old->epdis; new.bundle = old->bundle; new.mssf = old->mssf; new.walled_garden = old->walled_garden; new.classlen = old->classlen; memcpy(new.class, old->class, sizeof(new.class)); new.route6[0].ipv6prefixlen = old->ipv6prefixlen; new.route6[0].ipv6route = old->ipv6route; new.forwardtosession = old->forwardtosession; memcpy(new.src_hwaddr, old->src_hwaddr, sizeof(new.src_hwaddr)); return (uint8_t *) &new; } static uint8_t *convert_sessionV8(struct oldsessionV8 *old) { static sessiont new; int i; memset(&new, 0, sizeof(new)); new.next = old->next; new.far = old->far; new.tunnel = old->tunnel; new.flags = old->flags; new.ppp.phase = old->ppp.phase; new.ppp.lcp = old->ppp.lcp; new.ppp.ipcp = old->ppp.ipcp; new.ppp.ipv6cp = old->ppp.ipv6cp; new.ppp.ccp = old->ppp.ccp; new.mru = old->mru; new.ip = old->ip; new.ip_pool_index = old->ip_pool_index; new.unique_id = old->unique_id; new.magic = old->magic; new.pin = old->pin; new.pout = old->pout; new.cin = old->cin; new.cout = old->cout; new.cin_wrap = old->cin_wrap; new.cout_wrap = old->cout_wrap; new.cin_delta = old->cin_delta; new.cout_delta = old->cout_delta; new.throttle_in = old->throttle_in; new.throttle_out = old->throttle_out; new.filter_in = old->filter_in; new.filter_out = old->filter_out; new.snoop_port = old->snoop_port; new.snoop_ip = old->snoop_ip; new.opened = old->opened; new.die = old->die; new.session_timeout = old->session_timeout; new.idle_timeout = old->idle_timeout; new.last_packet = old->last_packet; new.last_data = old->last_data; new.dns1 = old->dns1; new.dns2 = old->dns2; for (i = 0; i < MAXROUTE; i++) memcpy(&new.route[i], &old->route[i], sizeof(new.route[i])); new.tbf_in = old->tbf_in; new.tbf_out = old->tbf_out; new.random_vector_length = old->random_vector_length; memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector)); memcpy(new.user, old->user, sizeof(new.user)); memcpy(new.called, old->called, sizeof(new.called)); memcpy(new.calling, old->calling, sizeof(new.calling)); new.tx_connect_speed = old->tx_connect_speed; new.rx_connect_speed = old->rx_connect_speed; new.timeout = old->timeout; new.mrru = old->mrru; new.epdis = old->epdis; new.bundle = old->bundle; new.mssf = old->mssf; new.walled_garden = old->walled_garden; new.classlen = old->classlen; memcpy(new.class, old->class, sizeof(new.class)); new.route6[0].ipv6prefixlen = old->ipv6prefixlen; new.route6[0].ipv6route = old->ipv6route; new.forwardtosession = old->forwardtosession; memcpy(new.src_hwaddr, old->src_hwaddr, sizeof(new.src_hwaddr)); new.dhcpv6_prefix_iaid = old->dhcpv6_prefix_iaid; new.dhcpv6_iana_iaid = old->dhcpv6_iana_iaid; new.ipv6address = old->ipv6address; new.dhcpv6_client_id = old->dhcpv6_client_id; return (uint8_t *) &new; } // // Process a heartbeat.. // // v6: added RADIUS class attribute, re-ordered session structure // v7: added tunnelt attribute at the end of struct (tunnelt size change) static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t *p, in_addr_t addr) { heartt *h; int s = size - (p-data); int i, type; int hb_ver = more; #if HB_VERSION != 9 # error "need to update cluster_process_heartbeat()" #endif // we handle versions 5 through 8 if (hb_ver < 5 || hb_ver > HB_VERSION) { LOG(0, 0, 0, "Received a heartbeat version that I don't support (%d)!\n", hb_ver); return -1; // Ignore it?? } if (size > sizeof(past_hearts[0].data)) { LOG(0, 0, 0, "Received an oversize heartbeat from %s (%d)!\n", fmtaddr(addr, 0), size); return -1; } if (s < sizeof(*h)) goto shortpacket; h = (heartt *) p; p += sizeof(*h); s -= sizeof(*h); if (h->clusterid != config->bind_address) return -1; // It's not part of our cluster. if (config->cluster_iam_master) { // Sanity... // Note that this MUST match the election process above! LOG(0, 0, 0, "I just got a heartbeat from master %s, but _I_ am the master!\n", fmtaddr(addr, 0)); if (!h->basetime) { LOG(0, 0, 0, "Heartbeat with zero basetime! Ignoring\n"); return -1; // Skip it. } if (h->table_version > config->cluster_table_version) { LOG(0, 0, 0, "They've seen more state changes (%" PRIu64 " vs my %" PRIu64 ") so I'm gone!\n", h->table_version, config->cluster_table_version); kill(0, SIGTERM); exit(1); } if (h->table_version < config->cluster_table_version) return -1; if (basetime > h->basetime) { LOG(0, 0, 0, "They're an older master than me so I'm gone!\n"); kill(0, SIGTERM); exit(1); } if (basetime < h->basetime) return -1; if (my_address < addr) { // Tie breaker. LOG(0, 0, 0, "They're a higher IP address than me, so I'm gone!\n"); kill(0, SIGTERM); exit(1); } // // Send it a unicast heartbeat to see give it a chance to die. // NOTE: It's actually safe to do seq-number - 1 without checking // for wrap around. // cluster_catchup_slave(config->cluster_seq_number - 1, addr); return -1; // Skip it. } // // Try and guard against a stray master appearing. // // Ignore heartbeats received from another master before the // timeout (less a smidgen) for the old master has elapsed. // // Note that after a clean failover, the cluster_master_address // is cleared, so this doesn't run. // if (config->cluster_master_address && addr != config->cluster_master_address) { LOG(0, 0, 0, "Ignoring stray heartbeat from %s, current master %s has not yet timed out (last heartbeat %.1f seconds ago).\n", fmtaddr(addr, 0), fmtaddr(config->cluster_master_address, 1), 0.1 * (TIME - config->cluster_last_hb)); return -1; // ignore } if (config->cluster_seq_number == -1) // Don't have one. Just align to the master... config->cluster_seq_number = h->seq; config->cluster_last_hb = TIME; // Reset to ensure that we don't become master!! config->cluster_last_hb_ver = hb_ver; // remember what cluster version the master is using if (config->cluster_seq_number != h->seq) { // Out of sequence heartbeat! static int lastseen_seq = 0; static time_t lastseen_time = 0; // limit to once per second for a particular seq# int ask = (config->cluster_seq_number != lastseen_seq || time_now != lastseen_time); LOG(1, 0, 0, "HB: Got seq# %d but was expecting %d. %s.\n", h->seq, config->cluster_seq_number, ask ? "Asking for resend" : "Ignoring"); if (ask) { lastseen_seq = config->cluster_seq_number; lastseen_time = time_now; peer_send_message(addr, C_LASTSEEN, config->cluster_seq_number, NULL, 0); } config->cluster_last_hb = TIME; // Reset to ensure that we don't become master!! // Just drop the packet. The master will resend it as part of the catchup. return 0; } // Save the packet in our buffer. // This is needed in case we become the master. config->cluster_seq_number = (h->seq+1)%HB_MAX_SEQ; i = h->seq % HB_HISTORY_SIZE; past_hearts[i].seq = h->seq; past_hearts[i].size = size; memcpy(&past_hearts[i].data, data, size); // Save it. // Check that we don't have too many undefined sessions, and // that the free session pointer is correct. cluster_check_sessions(h->highsession, h->freesession, h->highbundle, h->hightunnel); if (h->interval != config->cluster_hb_interval) { LOG(2, 0, 0, "Master set ping/heartbeat interval to %u (was %u)\n", h->interval, config->cluster_hb_interval); config->cluster_hb_interval = h->interval; } if (h->timeout != config->cluster_hb_timeout) { LOG(2, 0, 0, "Master set heartbeat timeout to %u (was %u)\n", h->timeout, config->cluster_hb_timeout); config->cluster_hb_timeout = h->timeout; } // Ok. process the packet... while ( s > 0) { type = *((uint32_t *) p); p += sizeof(uint32_t); s -= sizeof(uint32_t); more = *((uint32_t *) p); p += sizeof(uint32_t); s -= sizeof(uint32_t); switch (type) { case C_CSESSION: { // Compressed session structure. uint8_t c[ sizeof(sessiont) + 2]; int size; uint8_t *orig_p = p; size = rle_decompress((uint8_t **) &p, s, c, sizeof(c) ); s -= (p - orig_p); // session struct changed with v5 if (hb_ver < 6) { if (size != sizeof(struct oldsession)) { LOG(0, 0, 0, "DANGER: Received a v%d CSESSION that didn't decompress correctly!\n", hb_ver); // Now what? Should exit! No-longer up to date! break; } cluster_recv_session(more, convert_session((struct oldsession *) c)); break; } if (size != sizeof(sessiont)) { // Ouch! Very very bad! if ((hb_ver < HB_VERSION) && (size < sizeof(sessiont))) { if ((hb_ver == 7) && (size == sizeof(struct oldsessionV7))) cluster_recv_session(more, convert_sessionV7((struct oldsessionV7 *) c)); else if (size == sizeof(struct oldsessionV8)) cluster_recv_session(more, convert_sessionV8((struct oldsessionV8 *) c)); else LOG(0, 0, 0, "DANGER: Received a CSESSION version=%d that didn't decompress correctly!\n", hb_ver); break; } else { LOG(0, 0, 0, "DANGER: Received a CSESSION that didn't decompress correctly!\n"); // Now what? Should exit! No-longer up to date! break; } } cluster_recv_session(more, c); break; } case C_SESSION: if (hb_ver < 6) { if (s < sizeof(struct oldsession)) goto shortpacket; cluster_recv_session(more, convert_session((struct oldsession *) p)); p += sizeof(struct oldsession); s -= sizeof(struct oldsession); break; } if ( s < sizeof(session[more])) goto shortpacket; cluster_recv_session(more, p); p += sizeof(session[more]); s -= sizeof(session[more]); break; case C_CTUNNEL: { // Compressed tunnel structure. uint8_t c[ sizeof(tunnelt) + 2]; int size; uint8_t *orig_p = p; size = rle_decompress((uint8_t **) &p, s, c, sizeof(c)); s -= (p - orig_p); if ( ((hb_ver >= HB_VERSION) && (size != sizeof(tunnelt))) || ((hb_ver < HB_VERSION) && (size > sizeof(tunnelt))) ) { // Ouch! Very very bad! LOG(0, 0, 0, "DANGER: Received a CTUNNEL that didn't decompress correctly!\n"); // Now what? Should exit! No-longer up to date! break; } cluster_recv_tunnel(more, c); break; } case C_TUNNEL: if ( s < sizeof(tunnel[more])) goto shortpacket; cluster_recv_tunnel(more, p); p += sizeof(tunnel[more]); s -= sizeof(tunnel[more]); break; case C_CBUNDLE: { // Compressed bundle structure. uint8_t c[ sizeof(bundlet) + 2]; int size; uint8_t *orig_p = p; size = rle_decompress((uint8_t **) &p, s, c, sizeof(c)); s -= (p - orig_p); if (size != sizeof(bundlet) ) { // Ouch! Very very bad! LOG(0, 0, 0, "DANGER: Received a CBUNDLE that didn't decompress correctly!\n"); // Now what? Should exit! No-longer up to date! break; } cluster_recv_bundle(more, c); break; } case C_BUNDLE: if ( s < sizeof(bundle[more])) goto shortpacket; cluster_recv_bundle(more, p); p += sizeof(bundle[more]); s -= sizeof(bundle[more]); break; default: LOG(0, 0, 0, "DANGER: I received a heartbeat element where I didn't understand the type! (%d)\n", type); return -1; // can't process any more of the packet!! } } if (config->cluster_master_address != addr) { LOG(0, 0, 0, "My master just changed from %s to %s!\n", fmtaddr(config->cluster_master_address, 0), fmtaddr(addr, 1)); config->cluster_master_address = addr; } config->cluster_last_hb = TIME; // Successfully received a heartbeat! config->cluster_table_version = h->table_version; return 0; shortpacket: LOG(0, 0, 0, "I got an incomplete heartbeat packet! This means I'm probably out of sync!!\n"); return -1; } // // We got a packet on the cluster port! // Handle pings, lastseens, and heartbeats! // int processcluster(uint8_t *data, int size, in_addr_t addr) { int type, more; uint8_t *p = data; int s = size; if (addr == my_address) return -1; // Ignore it. Something looped back the multicast! LOG(5, 0, 0, "Process cluster: %d bytes from %s\n", size, fmtaddr(addr, 0)); if (s <= 0) // Any data there?? return -1; if (s < 8) goto shortpacket; type = *((uint32_t *) p); p += sizeof(uint32_t); s -= sizeof(uint32_t); more = *((uint32_t *) p); p += sizeof(uint32_t); s -= sizeof(uint32_t); switch (type) { case C_PING: // Update the peers table. return cluster_add_peer(addr, more, (pingt *) p, s); case C_MASTER: // Our master is wrong return cluster_set_master(addr, more); case C_LASTSEEN: // Catch up a slave (slave missed a packet). return cluster_catchup_slave(more, addr); case C_FORWARD: // Forwarded control packet. pass off to processudp. case C_FORWARD_DAE: // Forwarded DAE packet. pass off to processdae. if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_FORWARD%s from %s?\n", type == C_FORWARD_DAE ? "_DAE" : "", fmtaddr(addr, 0)); return -1; } else { struct sockaddr_in a; uint16_t indexudp; a.sin_addr.s_addr = more; a.sin_port = (*(int *) p) & 0xFFFF; indexudp = ((*(int *) p) >> 16) & 0xFFFF; s -= sizeof(int); p += sizeof(int); LOG(4, 0, 0, "Got a forwarded %spacket... (%s:%d)\n", type == C_FORWARD_DAE ? "DAE " : "", fmtaddr(more, 0), a.sin_port); STAT(recv_forward); if (type == C_FORWARD_DAE) { struct in_addr local; local.s_addr = config->bind_address ? config->bind_address : my_address; processdae(p, s, &a, sizeof(a), &local); } else processudp(p, s, &a, indexudp); return 0; } case C_PPPOE_FORWARD: if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_PPPOE_FORWARD from %s?\n", fmtaddr(addr, 0)); return -1; } else { pppoe_process_forward(p, s, addr); return 0; } case C_MPPP_FORWARD: // Receive a MPPP packet from a slave. if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_MPPP_FORWARD from %s?\n", fmtaddr(addr, 0)); return -1; } processipout(p, s); return 0; case C_THROTTLE: { // Receive a forwarded packet from a slave. if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_THROTTLE from %s?\n", fmtaddr(addr, 0)); return -1; } tbf_queue_packet(more, p, s); // The TBF id tells wether it goes in or out. return 0; } case C_GARDEN: // Receive a walled garden packet from a slave. if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_GARDEN from %s?\n", fmtaddr(addr, 0)); return -1; } tun_write(p, s); return 0; case C_BYTES: if (!config->cluster_iam_master) { LOG(0, 0, 0, "I'm not the master, but I got a C_BYTES from %s?\n", fmtaddr(addr, 0)); return -1; } return cluster_handle_bytes(p, s); case C_KILL: // The master asked us to die!? (usually because we're too out of date). if (config->cluster_iam_master) { LOG(0, 0, 0, "_I_ am master, but I received a C_KILL from %s! (Seq# %d)\n", fmtaddr(addr, 0), more); return -1; } if (more != config->cluster_seq_number) { LOG(0, 0, 0, "The master asked us to die but the seq number didn't match!?\n"); return -1; } if (addr != config->cluster_master_address) { LOG(0, 0, 0, "Received a C_KILL from %s which doesn't match config->cluster_master_address (%s)\n", fmtaddr(addr, 0), fmtaddr(config->cluster_master_address, 1)); // We can only warn about it. The master might really have switched! } LOG(0, 0, 0, "Received a valid C_KILL: I'm going to die now.\n"); kill(0, SIGTERM); exit(0); // Lets be paranoid; return -1; // Just signalling the compiler. case C_HEARTBEAT: LOG(4, 0, 0, "Got a heartbeat from %s\n", fmtaddr(addr, 0)); return cluster_process_heartbeat(data, size, more, p, addr); default: LOG(0, 0, 0, "Strange type packet received on cluster socket (%d)\n", type); return -1; } return 0; shortpacket: LOG(0, 0, 0, "I got a _short_ cluster heartbeat packet! This means I'm probably out of sync!!\n"); return -1; } //==================================================================================================== int cmd_show_cluster(struct cli_def *cli, const char *command, char **argv, int argc) { int i; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "Cluster status : %s", config->cluster_iam_master ? "Master" : "Slave" ); cli_print(cli, "My address : %s", fmtaddr(my_address, 0)); cli_print(cli, "VIP address : %s", fmtaddr(config->bind_address, 0)); cli_print(cli, "Multicast address: %s", fmtaddr(config->cluster_address, 0)); cli_print(cli, "UDP port : %u", config->cluster_port); cli_print(cli, "Multicast i'face : %s", config->cluster_interface); if (!config->cluster_iam_master) { cli_print(cli, "My master : %s (last heartbeat %.1f seconds old)", config->cluster_master_address ? fmtaddr(config->cluster_master_address, 0) : "Not defined", 0.1 * (TIME - config->cluster_last_hb)); cli_print(cli, "Uptodate : %s", config->cluster_iam_uptodate ? "Yes" : "No"); cli_print(cli, "Table version # : %" PRIu64, config->cluster_table_version); cli_print(cli, "Next sequence number expected: %d", config->cluster_seq_number); cli_print(cli, "%d sessions undefined of %d", config->cluster_undefined_sessions, config->cluster_highest_sessionid); cli_print(cli, "%d bundles undefined of %d", config->cluster_undefined_bundles, config->cluster_highest_bundleid); cli_print(cli, "%d tunnels undefined of %d", config->cluster_undefined_tunnels, config->cluster_highest_tunnelid); } else { cli_print(cli, "Table version # : %" PRIu64, config->cluster_table_version); cli_print(cli, "Next heartbeat # : %d", config->cluster_seq_number); cli_print(cli, "Highest session : %d", config->cluster_highest_sessionid); cli_print(cli, "Highest bundle : %d", config->cluster_highest_bundleid); cli_print(cli, "Highest tunnel : %d", config->cluster_highest_tunnelid); cli_print(cli, "%d changes queued for sending", config->cluster_num_changes); } cli_print(cli, "%d peers.", num_peers); if (num_peers) cli_print(cli, "%20s %10s %8s", "Address", "Basetime", "Age"); for (i = 0; i < num_peers; ++i) { cli_print(cli, "%20s %10u %8d", fmtaddr(peers[i].peer, 0), peers[i].basetime, TIME - peers[i].timestamp); } return CLI_OK; } // // Simple run-length-encoding compression. // Format is // 1 byte < 128 = count of non-zero bytes following. // Not legal to be zero. // n non-zero bytes; // or // 1 byte > 128 = (count - 128) run of zero bytes. // // repeat. // count == 0 indicates end of compressed stream. // // Compress from 'src' into 'dst'. return number of bytes // used from 'dst'. // Updates *src_p to indicate 1 past last bytes used. // // We could get an extra byte in the zero runs by storing (count-1) // but I'm playing it safe. // // Worst case is a 50% expansion in space required (trying to // compress { 0x00, 0x01 } * N ) static int rle_compress(uint8_t **src_p, int ssize, uint8_t *dst, int dsize) { int count; int orig_dsize = dsize; uint8_t *x, *src; src = *src_p; while (ssize > 0 && dsize > 2) { count = 0; x = dst++; --dsize; // Reserve space for count byte.. if (*src) { // Copy a run of non-zero bytes. while (*src && count < 127 && ssize > 0 && dsize > 1) { // Count number of non-zero bytes. *dst++ = *src++; --dsize; --ssize; ++count; } *x = count; // Store number of non-zero bytes. Guarenteed to be non-zero! } else { // Compress a run of zero bytes. while (*src == 0 && count < 127 && ssize > 0) { ++src; --ssize; ++count; } *x = count | 0x80 ; } } *dst++ = 0x0; // Add Stop byte. --dsize; *src_p = src; return (orig_dsize - dsize); } // // Decompress the buffer into **p. // 'psize' is the size of the decompression buffer available. // // Returns the number of bytes decompressed. // // Decompresses from '*src_p' into 'dst'. // Return the number of dst bytes used. // Updates the 'src_p' pointer to point to the // first un-used byte. static int rle_decompress(uint8_t **src_p, int ssize, uint8_t *dst, int dsize) { int count; int orig_dsize = dsize; uint8_t *src = *src_p; while (ssize >0 && dsize > 0) { // While there's more to decompress, and there's room in the decompress buffer... count = *src++; --ssize; // get the count byte from the source. if (count == 0x0) // End marker reached? If so, finish. break; if (count & 0x80) { // Decompress a run of zeros for (count &= 0x7f ; count > 0 && dsize > 0; --count) { *dst++ = 0x0; --dsize; } } else { // Copy run of non-zero bytes. for ( ; count > 0 && ssize && dsize; --count) { // Copy non-zero bytes across. *dst++ = *src++; --ssize; --dsize; } } } *src_p = src; return (orig_dsize - dsize); } l2tpns-2.3.3/cluster.h000066400000000000000000000074311400724550600146030ustar00rootroot00000000000000// L2TPNS Clustering Stuff // $Id: cluster.h,v 1.16 2006-12-04 20:54:51 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 C_BUNDLE 17 // Bundle structure. #define C_CBUNDLE 18 // Compressed bundle structure. #define C_MPPP_FORWARD 19 // MPPP Forwarded packet.. #define C_PPPOE_FORWARD 20 // PPPOE Forwarded packet.. #define HB_VERSION 9 // 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 highbundle; // Id of the highest used bundle. uint32_t hightunnel; // Id of the highest used tunnel. uint32_t size_sess; // Size of the session structure. uint32_t size_bund; // size of the bundle 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) - sizeof(uint64_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_bundle(int bid); int cluster_send_tunnel(int tid); int master_forward_packet(uint8_t *data, int size, in_addr_t addr, uint16_t port, uint16_t indexudp); 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); int master_forward_mppp_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, const char *command, char **argv, int argc); int master_forward_pppoe_packet(uint8_t *data, int size, uint8_t codepad); #endif /* __CLUSTER_H__ */ l2tpns-2.3.3/constants.c000066400000000000000000000142601400724550600151270ustar00rootroot00000000000000// L2TPNS: constants #include #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 "RADIUSJUSTAUTH" // 7 ) 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 ) l2tpns-2.3.3/constants.h000066400000000000000000000010141400724550600151250ustar00rootroot00000000000000#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__ */ l2tpns-2.3.3/control.c000066400000000000000000000064041400724550600145740ustar00rootroot00000000000000// L2TPNS: control #include #include #include "dhcp6.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"); } l2tpns-2.3.3/control.h000066400000000000000000000025651400724550600146050ustar00rootroot00000000000000#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__ */ l2tpns-2.3.3/dhcp6.c000066400000000000000000000441701400724550600141220ustar00rootroot00000000000000/* * Fernando ALVES 2014 * Add functionality DHCPv6 to l2tpns. * GPL licenced */ #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "ipv6_u.h" struct dhcp6_in_option { struct dhcp6_mess_hdr *p_mess_hdr; struct dhcp6_opt_h *p_opt_clientid; struct dhcp6_opt_h *p_opt_serverid; struct dhcp6_opt_h *p_opt_ia_na; struct dhcp6_opt_h *p_opt_ia_ta; struct dhcp6_opt_h *p_opt_ia_pd; struct dhcp6_opt_h *p_opt_oro; struct dhcp6_opt_h *p_opt_rapidcommit; }; static struct dhcp6_opt_serverid dhcp6_local_serverid; static struct dhcp6_in_option list_option; static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer); static void dhcp6_send_reply(sessionidt s, tunnelidt t, struct in6_addr *ip6_src) { struct ip6_hdr *p_ip6_hdr; struct udphdr *p_udp; struct dhcp6_mess_hdr *p_mess_hdr; struct dhcp6_opt_h *p_opt; struct ipv6_pseudo_hdr pseudo_hdr; uint8_t b[MAXETHER + 20]; int len; memset(b, 0, sizeof(b)); p_ip6_hdr = (struct ip6_hdr *) makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0); // IPv6 Header p_ip6_hdr->ip6_vfc = 0x60; // IPv6 p_ip6_hdr->ip6_plen = 0; // Length of payload (not header) (calculation below) p_ip6_hdr->ip6_nxt = IPPROTO_UDP; // icmp6 is next p_ip6_hdr->ip6_hlim = 1; // Hop limit // IPv6 Src FE02::1:2 inet_pton(AF_INET6, "FE02::1:2", &p_ip6_hdr->ip6_src.s6_addr); // IPv6 Dest memcpy(&p_ip6_hdr->ip6_dst.s6_addr, ip6_src, sizeof(p_ip6_hdr->ip6_dst.s6_addr)); // UDP Header p_udp = (struct udphdr *) &p_ip6_hdr[1]; p_udp->source = htons(547); p_udp->dest = htons(546); p_udp->len = 0; // Length udp size_udp_header + data (calculation below) p_udp->check = 0; // checksum (calculation below with ip pseudo header) // DHCPv6 msg header p_mess_hdr = (struct dhcp6_mess_hdr *) &p_udp[1]; if (list_option.p_mess_hdr->type == DHCP6_SOLICIT) p_mess_hdr->type = list_option.p_opt_rapidcommit ? DHCP6_REPLY : DHCP6_ADVERTISE; else p_mess_hdr->type = DHCP6_REPLY; p_mess_hdr->trans_id = list_option.p_mess_hdr->trans_id; // DHCPv6 options header p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1]; memcpy(p_opt, &dhcp6_local_serverid, ntohs(dhcp6_local_serverid.opt_hdr.len) + sizeof(dhcp6_local_serverid.opt_hdr)); // ServerID p_opt = (struct dhcp6_opt_h *) (((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); // next option if (list_option.p_opt_clientid) { memcpy(p_opt, list_option.p_opt_clientid, ntohs(list_option.p_opt_clientid->len) + sizeof(*p_opt)); // ClientID p_opt = (struct dhcp6_opt_h *) (((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); // next option } if (list_option.p_opt_ia_pd && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST)) { p_opt->code = htons(D6_OPT_IA_PD); // D6_OPT_IA_PD ((struct dhcp6_opt_ia_pd *)p_opt)->iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid; ((struct dhcp6_opt_ia_pd *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF; ((struct dhcp6_opt_ia_pd *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF; if ((list_option.p_mess_hdr->type == DHCP6_RENEW) && session[s].dhcpv6_prefix_iaid != ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid) { p_opt->len = htons(sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status)); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1]; ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE); ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2); ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option } else { struct dhcp6_opt_h *p_opt_head; int r; uint16_t lenopt; if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit) { session[s].dhcpv6_prefix_iaid = ((struct dhcp6_opt_ia_pd *)list_option.p_opt_ia_pd)->iaid; } p_opt_head = p_opt; lenopt = sizeof(struct dhcp6_opt_ia_pd) - sizeof(*p_opt); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_pd *)p_opt)[1]; for (r = 0; r < MAXROUTE6 && session[s].route6[r].ipv6route.s6_addr[0] && session[s].route6[r].ipv6prefixlen; r++) { ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.code = htons(D6_OPT_IAPREFIX); ((struct dhcp6_opt_ia_prefix *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_prefix) - sizeof(*p_opt)); ((struct dhcp6_opt_ia_prefix *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF; ((struct dhcp6_opt_ia_prefix *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF; ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix_len = session[s].route6[r].ipv6prefixlen; ((struct dhcp6_opt_ia_prefix *)p_opt)->prefix = session[s].route6[r].ipv6route; p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_prefix *)p_opt)[1]; // next option lenopt += sizeof(struct dhcp6_opt_ia_prefix); } p_opt_head->len = htons(lenopt); } } if (list_option.p_opt_ia_na && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST)) { p_opt->code = htons(D6_OPT_IA_NA); // D6_OPT_IA_NA ((struct dhcp6_opt_ia_na *)p_opt)->iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid; ((struct dhcp6_opt_ia_na *)p_opt)->T1 = (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime/2) : 0xFFFFFFFF; ((struct dhcp6_opt_ia_na *)p_opt)->T2 = (config->dhcp6_preferred_lifetime > 0) ? htonl((config->dhcp6_preferred_lifetime*4)/5) : 0xFFFFFFFF; if ((list_option.p_mess_hdr->type == DHCP6_RENEW) && session[s].dhcpv6_iana_iaid != ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid) { p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status)); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1]; ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE); ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2); ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_NoBinding); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option } else { in_addr_t addr_ipv4; if (list_option.p_mess_hdr->type == DHCP6_REQUEST || list_option.p_opt_rapidcommit) { session[s].dhcpv6_iana_iaid = ((struct dhcp6_opt_ia_na *)list_option.p_opt_ia_na)->iaid; } p_opt->len = htons(sizeof(struct dhcp6_opt_ia_na) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_ia_addr)); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_na *)p_opt)[1]; ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.code = htons(D6_OPT_IAADDR); ((struct dhcp6_opt_ia_addr *)p_opt)->hdr.len = htons(sizeof(struct dhcp6_opt_ia_addr) - sizeof(*p_opt)); if (session[s].ipv6address.s6_addr[0]) { memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &session[s].ipv6address, 16); // copy ipv6 prefix } else { memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr, &config->ipv6_prefix, 8); // copy prefix 64 addr_ipv4 = htonl(session[s].ip); memcpy(&((struct dhcp6_opt_ia_addr *)p_opt)->addr.s6_addr[8], &addr_ipv4, 4); // copy ipv4 } ((struct dhcp6_opt_ia_addr *)p_opt)->pref_lifetime= (config->dhcp6_preferred_lifetime > 0) ? htonl(config->dhcp6_preferred_lifetime) : 0xFFFFFFFF; ((struct dhcp6_opt_ia_addr *)p_opt)->valid_lifetime= (config->dhcp6_valid_lifetime > 0) ? htonl(config->dhcp6_valid_lifetime) : 0xFFFFFFFF; p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_addr *)p_opt)[1]; // next option } } if (list_option.p_opt_ia_ta && (list_option.p_mess_hdr->type != DHCP6_INFORMATION_REQUEST)) { p_opt->code = htons(D6_OPT_IA_TA); // D6_OPT_IA_TA p_opt->len = htons(sizeof(struct dhcp6_opt_ia_ta) - sizeof(*p_opt) + sizeof(struct dhcp6_opt_status)); ((struct dhcp6_opt_ia_ta *)p_opt)->iaid = ((struct dhcp6_opt_ia_ta *)list_option.p_opt_ia_ta)->iaid; p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_ia_ta *)p_opt)[1]; ((struct dhcp6_opt_status *)p_opt)->hdr.code = htons(D6_OPT_STATUS_CODE); ((struct dhcp6_opt_status *)p_opt)->hdr.len = htons(2); ((struct dhcp6_opt_status *)p_opt)->code = htons(D6_STATUS_UnspecFail); p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_status *)p_opt)[1]; // next option } if (list_option.p_opt_oro) { int countopt; uint16_t *ptrw; struct in6_addr *ptr_in6_addr; for (countopt = ntohs(list_option.p_opt_oro->len)/2, ptrw = (uint16_t *)((struct dhcp6_opt_oro *)list_option.p_opt_oro)->opt_demand; countopt; countopt--, ptrw++) { if (ntohs(*ptrw) == D6_OPT_DNS_SERVERS) { if (config->default_ipv6_dns1.s6_addr[0]) { p_opt->code = htons(D6_OPT_DNS_SERVERS); // D6_OPT_DNS_SERVERS p_opt->len = htons(sizeof(*ptr_in6_addr)); ptr_in6_addr = (struct in6_addr *) &p_opt[1]; memcpy(ptr_in6_addr, &config->default_ipv6_dns1, sizeof(*ptr_in6_addr)); if (config->default_ipv6_dns2.s6_addr[0]) { p_opt->len = htons(2*sizeof(*ptr_in6_addr)); ptr_in6_addr = &ptr_in6_addr[1]; memcpy(ptr_in6_addr, &config->default_ipv6_dns2, sizeof(*ptr_in6_addr)); } p_opt = (struct dhcp6_opt_h *) &ptr_in6_addr[1]; // next option } } if (ntohs(*ptrw) == D6_OPT_DOMAIN_LIST) { if (*config->default_ipv6_domain_list) { uint8_t buffer[255]; int len = dhcpv6_format_dns_search_name(config->default_ipv6_domain_list, buffer); if (len > 0) { p_opt->code = htons(D6_OPT_DOMAIN_LIST); // D6_OPT_DOMAIN_LIST p_opt->len = htons(len); memcpy((char *)&p_opt[1], buffer, len); p_opt = (struct dhcp6_opt_h *) (((uint8_t *) &p_opt[1]) + len); // next option } } } } } if (list_option.p_opt_rapidcommit && (list_option.p_mess_hdr->type == DHCP6_SOLICIT)) { p_opt->code = htons(D6_OPT_RAPID_COMMIT); // D6_OPT_RAPID_COMMIT p_opt->len = 0; p_opt = &p_opt[1]; // next option } p_opt->code = htons(D6_OPT_PREFERENCE); // D6_OPT_PREFERENCE p_opt->len = htons(1); ((struct dhcp6_opt_preference *)p_opt)->pref = 255; p_opt = (struct dhcp6_opt_h *) &((struct dhcp6_opt_preference *)p_opt)[1]; // next option // calculation of lenght len = ((uint8_t *) p_opt) - ((uint8_t *) p_udp); p_ip6_hdr->ip6_plen = p_udp->len = htons(len); /* Use pseudo hearder for checksum calculation */ memset(&pseudo_hdr, 0, sizeof(pseudo_hdr)); memcpy(&pseudo_hdr.src, &p_ip6_hdr->ip6_src, 16); memcpy(&pseudo_hdr.dest, &p_ip6_hdr->ip6_dst, 16); pseudo_hdr.ulp_length = htonl(len); // Lenght whitout Ipv6 header pseudo_hdr.nexthdr = IPPROTO_UDP; // Checksum is over the udp payload plus the pseudo header p_udp->check = ipv6_checksum(&pseudo_hdr, (uint8_t *) p_udp, len); // Add ipv6 header to length len += sizeof(*p_ip6_hdr); LOG(3, s, t, "Send DHCPv6 message %s\n", (p_mess_hdr->type == DHCP6_REPLY) ? "REPLY" : "ADVERTISE"); tunnelsend(b, len + (((uint8_t *) p_ip6_hdr)-b), t); // send it... } static char * get_msg_type(uint8_t type) { switch(type) { case DHCP6_SOLICIT: { return "Solicit"; } break; case DHCP6_REQUEST: return "Request"; break; case DHCP6_RENEW: return "Renew"; break; case DHCP6_INFORMATION_REQUEST: return "Information Request"; break; case DHCP6_REBIND: return "Rebind"; break; case DHCP6_RELEASE: return "Release"; break; case DHCP6_DECLINE: return "Decline"; break; default: return "Unknown"; break; } } void dhcpv6_process(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { struct ip6_hdr *p_ip6_hdr_in; struct dhcp6_mess_hdr *p_mess_hdr; struct dhcp6_opt_h *p_opt; uint8_t *p_end; uint16_t len; CSTAT(dhcpv6_process); p_ip6_hdr_in = (struct ip6_hdr *) p; p_mess_hdr = (struct dhcp6_mess_hdr *) (p + 48); LOG(3, s, t, "Got DHCPv6 message Type: %s(%d)\n", get_msg_type(p_mess_hdr->type), p_mess_hdr->type); if (!session[s].route6[0].ipv6route.s6_addr[0] || !session[s].route6[0].ipv6prefixlen) return; p_opt = (struct dhcp6_opt_h *) &p_mess_hdr[1]; p_end = ((uint8_t *)p_ip6_hdr_in) + ntohs(p_ip6_hdr_in->ip6_plen) + sizeof(*p_ip6_hdr_in); memset(&list_option, 0, sizeof(list_option)); list_option.p_mess_hdr = p_mess_hdr; while (((uint8_t *)p_opt) < p_end) { switch(ntohs(p_opt->code)) { case D6_OPT_CLIENTID: list_option.p_opt_clientid = p_opt; LOG(3, s, t, "......Option D6_OPT_CLIENTID\n"); break; case D6_OPT_SERVERID: list_option.p_opt_serverid = p_opt; LOG(3, s, t, "......Option D6_OPT_SERVERID\n"); break; case D6_OPT_RAPID_COMMIT: list_option.p_opt_rapidcommit = p_opt; LOG(3, s, t, "......Option D6_OPT_RAPID_COMMIT\n"); break; case D6_OPT_IA_NA: list_option.p_opt_ia_na = p_opt; LOG(3, s, t, "......Option D6_OPT_IA_NA\n"); break; case D6_OPT_IA_TA: list_option.p_opt_ia_ta = p_opt; LOG(3, s, t, "......Option D6_OPT_IA_TA\n"); break; case D6_OPT_ORO: list_option.p_opt_oro = p_opt; LOG(3, s, t, "......Option D6_OPT_ORO\n"); break; case D6_OPT_IA_PD: list_option.p_opt_ia_pd = p_opt; LOG(3, s, t, "......Option D6_OPT_IA_PD\n"); break; case D6_OPT_ELAPSED_TIME: LOG(3, s, t, "......Option D6_OPT_ELAPSED_TIME\n"); break; default: LOG(3, s, t, "......DHCPv6 option: %d\n", ntohs(p_opt->code)); break; } p_opt = (struct dhcp6_opt_h *)(((uint8_t *) p_opt) + ntohs(p_opt->len) + sizeof(*p_opt)); } switch(p_mess_hdr->type) { case DHCP6_SOLICIT: { if (!list_option.p_opt_clientid) { LOG(3, s, t, "DHCPv6: error no Client-ID\n"); return; } else if (list_option.p_opt_rapidcommit) { if (!session[s].dhcpv6_client_id.opt_hdr.len) { len = ntohs(list_option.p_opt_clientid->len); if ((len > 0) && (len <= sizeof(struct dhcp6_duid))) { memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len); } else { LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n"); return; } } else if (session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len || memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len))) { LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n"); return; } } if (list_option.p_opt_serverid) { LOG(3, s, t, "DHCPv6: Error unexpected Server-ID option on Solicit\n"); return; } dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); } break; case DHCP6_REQUEST: { if (!list_option.p_opt_clientid) { LOG(3, s, t, "DHCPv6: error no Client-ID\n"); return; } if (!list_option.p_opt_serverid) { LOG(3, s, t, "DHCPv6: error no Server-ID\n"); return; } else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len || memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len))) { LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n"); return; } if (!session[s].dhcpv6_client_id.opt_hdr.len) { len = ntohs(list_option.p_opt_clientid->len); if ((len > 0) && (len <= sizeof(struct dhcp6_duid))) { memcpy(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + len); } else { LOG(3, s, t, "DHCPv6: Error malformed Client-ID option\n"); return; } } else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len || memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len))) { LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n"); return; } dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); send_ipv6_ra(s, t, &p_ip6_hdr_in->ip6_src); // send a RA } break; case DHCP6_RENEW: { if (!list_option.p_opt_clientid) { LOG(3, s, t, "DHCPv6: error no Client-ID\n"); return; } else if ( session[s].dhcpv6_client_id.opt_hdr.len != list_option.p_opt_clientid->len || memcmp(&session[s].dhcpv6_client_id, list_option.p_opt_clientid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_clientid->len))) { LOG(3, s, t, "DHCPv6: Error unmatched Client-ID option\n"); return; } if (!list_option.p_opt_serverid) { LOG(3, s, t, "DHCPv6: error no Server-ID\n"); return; } else if (dhcp6_local_serverid.opt_hdr.len != list_option.p_opt_serverid->len || memcmp(&dhcp6_local_serverid, list_option.p_opt_serverid, sizeof(struct dhcp6_opt_h) + ntohs(list_option.p_opt_serverid->len))) { LOG(3, s, t, "DHCPv6: Error unmatched Server-ID option\n"); return; } dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); } break; case DHCP6_INFORMATION_REQUEST: { if (!list_option.p_opt_clientid) { LOG(3, s, t, "DHCPv6: error no Client-ID\n"); return; } dhcp6_send_reply(s, t, &p_ip6_hdr_in->ip6_src); } break; case DHCP6_REBIND: { } break; case DHCP6_RELEASE: { } break; case DHCP6_DECLINE: { } break; default: break; } return; } static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer) { int n = strlen(strdns); const char *ptr; if (strdns[n - 1] == '.') n++; else n += 2; if (n > 255) { LOG(3, 0, 0, "DHCPv6: DNS search '%s' is too long\n", strdns); return 0; } while (1) { ptr = strchr(strdns, '.'); if (!ptr) ptr = strchr(strdns, 0); if (ptr - strdns > 63) { LOG(3, 0, 0, "DHCPv6: DNS search '%s' is invalid\n", strdns); return 0; } *buffer = ptr - strdns; memcpy(buffer + 1, strdns, ptr - strdns); buffer += 1 + (ptr - strdns); strdns = ptr + 1; if (!*ptr || !*strdns) { *buffer = 0; break; } } return n; } void dhcpv6_init(void) { uint32_t id; dhcp6_local_serverid.opt_hdr.code = htons(D6_OPT_SERVERID); dhcp6_local_serverid.opt_hdr.len = htons(4 + sizeof(id)); dhcp6_local_serverid.duid.type = htons(DUID_LL); dhcp6_local_serverid.duid.u.ll.htype = htons(27); if (config->dhcp6_server_duid) id = htobe32(config->dhcp6_server_duid); else id = htobe32(0xFDFDFAFA); memcpy(dhcp6_local_serverid.duid.u.ll.addr, &id, sizeof(id)); } l2tpns-2.3.3/dhcp6.h000066400000000000000000000164741400724550600141350ustar00rootroot00000000000000/* * Fernando ALVES 2014 * Add functionality DHCPv6 to l2tpns. * GPL licenced */ #ifndef __DHCP6_H__ #define __DHCP6_H__ #define DHCP6_SOLICIT 1 #define DHCP6_ADVERTISE 2 #define DHCP6_REQUEST 3 #define DHCP6_CONFIRM 4 #define DHCP6_RENEW 5 #define DHCP6_REBIND 6 #define DHCP6_REPLY 7 #define DHCP6_RELEASE 8 #define DHCP6_DECLINE 9 #define DHCP6_RECONFIGURE 10 #define DHCP6_INFORMATION_REQUEST 11 #define DHCP6_RELAY_FORM 12 #define DHCP6_RELAY_REPL 13 #define D6_OPT_CLIENTID 1 #define D6_OPT_SERVERID 2 #define D6_OPT_IA_NA 3 #define D6_OPT_IA_TA 4 #define D6_OPT_IAADDR 5 #define D6_OPT_ORO 6 #define D6_OPT_PREFERENCE 7 #define D6_OPT_ELAPSED_TIME 8 #define D6_OPT_RELAY_MSG 9 #define D6_OPT_AUTH 11 #define D6_OPT_UNICAST 12 #define D6_OPT_STATUS_CODE 13 #define D6_OPT_RAPID_COMMIT 14 #define D6_OPT_USER_CLASS 15 #define D6_OPT_VENDOR_CLASS 16 #define D6_OPT_VENDOR_SPECIFIC 17 #define D6_OPT_INTERFACE_ID 18 #define D6_OPT_RECONF_MSG 19 #define D6_OPT_RECONF_ACCEPT 20 #define D6_OPT_DNS_SERVERS 23 #define D6_OPT_DOMAIN_LIST 24 #define D6_OPT_IA_PD 25 #define D6_OPT_IAPREFIX 26 #define D6_STATUS_Success 0 #define D6_STATUS_UnspecFail 1 #define D6_STATUS_NoAddrsAvail 2 #define D6_STATUS_NoBinding 3 #define D6_STATUS_NotOnLink 4 #define D6_STATUS_UseMulticast 5 #define D6_STATUS_NoPrefixAvail 6 #define DUID_LLT 1 #define DUID_EN 2 #define DUID_LL 3 //~ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | D6_OPT_IA_PD | Longueur d’option | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | IAID (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | T1 (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | T2 (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | Options-IA_PD | //~ . . //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Prefix for IA_PD //~ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | OPTION_IAPREFIX | option-length | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | preferred-lifetime | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | valid-lifetime | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | prefix-length | | //~ +-+-+-+-+-+-+-+-+ IPv6 prefix | //~ | (16 octets) | //~ | | //~ | | //~ | | //~ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | | . //~ +-+-+-+-+-+-+-+-+ . //~ . IAprefix-options . //~ . . //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | D6_OPT_IA_NA | Longueur d’option | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | IAID (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | T1 (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | T2 (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | Options-IA_NA | //~ . . //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | OPTION_IA_TA | Longueur d’option | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | IAID (4 octets) | //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //~ | Options-IA_TA | //~ . . //~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ struct dhcp6_mess_hdr { uint32_t type:8; uint32_t trans_id:24; } __attribute__((packed)); struct dhcp6_opt_h { uint16_t code; uint16_t len; } __attribute__((packed)); struct dhcp6_duid { uint16_t type; union { struct { uint16_t htype; uint32_t time; uint8_t addr[0]; } __attribute__((packed)) llt; struct { uint32_t enterprise; uint8_t id[0]; } __attribute__((packed)) en; struct { uint16_t htype; uint8_t addr[0]; } __attribute__((packed)) ll; uint8_t raw[128]; } u; } __attribute__((packed)); struct dhcp6_opt_serverid { struct dhcp6_opt_h opt_hdr; struct dhcp6_duid duid; } __attribute__((packed)); struct dhcp6_opt_clientid { struct dhcp6_opt_h opt_hdr; struct dhcp6_duid duid; } __attribute__((packed)); struct dhcp6_opt_ia_na { struct dhcp6_opt_h hdr; uint32_t iaid; uint32_t T1; uint32_t T2; } __attribute__((packed)); struct dhcp6_opt_ia_ta { struct dhcp6_opt_h hdr; uint32_t iaid; } __attribute__((packed)); struct dhcp6_opt_ia_pd { struct dhcp6_opt_h hdr; uint32_t iaid; uint32_t T1; uint32_t T2; } __attribute__((packed)); struct dhcp6_opt_ia_addr { struct dhcp6_opt_h hdr; struct in6_addr addr; uint32_t pref_lifetime; uint32_t valid_lifetime; } __attribute__((packed)); struct dhcp6_opt_oro { struct dhcp6_opt_h hdr; uint16_t opt_demand[0]; } __attribute__((packed)); struct dhcp6_opt_status { struct dhcp6_opt_h hdr; uint16_t code; } __attribute__((packed)); struct dhcp6_opt_preference { struct dhcp6_opt_h hdr; uint8_t pref; } __attribute__((packed)); struct dhcp6_opt_ia_prefix { struct dhcp6_opt_h hdr; uint32_t pref_lifetime; uint32_t valid_lifetime; uint8_t prefix_len; struct in6_addr prefix; } __attribute__((packed)); // dhcp6.c void dhcpv6_process(uint16_t s, uint16_t t, uint8_t *p, uint16_t l); void dhcpv6_init(void); #endif /* __DHCP6_H__ */ l2tpns-2.3.3/docs/000077500000000000000000000000001400724550600136745ustar00rootroot00000000000000l2tpns-2.3.3/docs/gen-docs.sh000077500000000000000000000017711400724550600157400ustar00rootroot00000000000000#!/bin/sh # Source documentation in markdown lives in the src/ folder (html and manpages). # This is what you should edit if you want to make changes to the documentation. # From these sources, this script generates actual manpages and general # documentation in html using pandoc. if ! [ -x "$(command -v pandoc)" ]; then echo "Pandoc is missing, please install it first" exit 1 fi # First we generate manpages echo "Manpages generation …" for src in src/man/*.md do pandoc -s -t man "$src" -o manpages/"$(basename "$src" .md)" && echo "$(basename "$src" .md) successfully built in docs/manpages directory" || echo "Unable to generate manpage from $src" done # We then generate the rest of the documentation echo "" echo "HTML generation …" for src in src/html/*.md do pandoc -f markdown -t html "$src" > html/"$(basename "$src" .md)".html && echo "$(basename "$src" .md).html successfully built in docs/html directory" || echo "Unable to generate html from $src" done l2tpns-2.3.3/docs/html/000077500000000000000000000000001400724550600146405ustar00rootroot00000000000000l2tpns-2.3.3/docs/html/images/000077500000000000000000000000001400724550600161055ustar00rootroot00000000000000l2tpns-2.3.3/docs/html/images/site-to-site-vpn.png000066400000000000000000003274051400724550600217550ustar00rootroot00000000000000PNG  IHDRNɓlbKGD pHYs  tIME <76 IDATx}w]E;.KA1A "`"E EKb(E&(KQ!@$"piF 䜽*Sf z !Zkw{{el{J)ֱi]{m^|޺Cqpsd%t~?%"7F>lo5;_9,:* Ƙ[7yҍsU{.خRֺTn~oιۄDwjfH)BE4MADT*R}RB}Ayn0 !y8wi"";5 hp<6sy^[[J)iZ/ ٕRBA"͹>x/և?;$I\rΑ$ *; oR,R j_ r!2dYgTQ)<{2?[$Vߺ ðmZ . }E.}RJ;"-.Znv,qKɋsr(U<`sg+}Vo[s’]/,jqS]xu$p}OYLĽ۷jӾu]%pi9"5~K._^gpG[47ߌ1c8nnI'!C`֬YT*nS4h_B.\YfAJ(jPT00H1x6l{"7vǎqUWaB`8蠃pꩧ r 9uW^38C m݆ 7ЁGjZ%twcΜ9VqYa}>Xln!BB!I8of!C Chy~w}=a>#Gtkx̙3Io߾>`wvt|_#5KQy% KJ)RRDDjN 9M>/_NDD-#88&L!0 w3fpZgiJJu,iɓ mt' . ohڴi4vX:ө?>eO)EF"4eZb-\:(<ФIUw\r{}iIJ㘮ڶycI',e-_=\@m-Z?0 1Fhn;;;iܸqn DD˖-ڋ|ߧ UUz'Ik8&"_NiR m_ctҚ$ M:8nZ-""c=Fo7iҤI kǘ1ch-^{RDDE۴1{)%=sFرcZSen~QtqǹM\iӨZ\,]vم?4uig}Xu~)%tA Ð?]wEjyիWSZm{R&-YjnԨQ$IW_mo賟,uww^hmIq{?iPzZN"V[mD`kYqvj8q뭷/{x+ 8ꨣޓ[Ƭ*?wڢa 0t[lc"gy/M)BlOI0zh1s ibΜ9:u*n|N18>}:f͚V_WѣG;ae曱rJ.Ү Ng>#ի8vzGZ_W\7pϟ( ltA@)gy7ڼ=4lztVm`EOl`Mq~0Wb= ߷o__&XNh4~xDžE"bVE{U*?~{ۦqʕ+۸,}@Q6ZA/2M8M[WrDݍ7ޘz6.rfwSnȉr-#\{!C8N'?iS-؂^xޭhl9J{̈́ є)S;_8o{e]ֶ9o59M7O^]̳&Td$a@` 0mH^"#MD$qͤ M䞡(RD矣A&pF`qƓPg ^J 6Bz`bolBIwގOjڶ[϶oZ?q]ғ^ZO3ϤVTk4iBZIL3>Gji1cY *Cg/&MD>9+ ۮ"MBIWPXmu`{oo:mnmR+0ibwIIAG~(IUj垽8?(4ioM-n=pkMq4='MLtQ'0Pm'VMȨtq_'01_?v :#j&FZ@'t XwqVkͷB?54idg} tWFv"xA5xm@h7ۀJ(l|Xw/=0i"i":۸/k8<靉V\I'p>vuW@s Ihuw %]-ǤHS&ٻUjUן[nufa܇u0wȯYXh~i"w %im}zKއd -*n"IAK3Uγ7t7S&EGU>{4fXDUkq'Nh45goD4oN=fZ D-%ǹhZ+rOϼÍW}5ZMJEF{}Hve!r&fKo~@*'8mt i=@^h̸nL8Y8:2~G1ahSN;m,'[쁤Hyoa*2z!;߾OEFXE:z|"wvzؿh]s Uuys8@ʤV, ? Bgwm\9{gP*2JEƙWޒUj[Xkl\ z>J4lݝh=N8ހgoFw qМZY08p>wA`3gM_@sΡ,(cbQn몮h5q4_~ZxeٳNs׾J7FiMOq:П@:MTʤ$K@ m5]35q$D}oׇ[Չ:\Zkpf,Ț:K%ݱxb9|RTk59s&99g03KrGu0;Q{DhҸo߾Yx.\?@&3 r R~SkvZ3'|͛,a3'DkQ8{>*QZk00d"CFc=SOد}Spw`ԨQQ!*1FQ,L[E󐦩DjZk}y4M0qR0Da4u6h#9cU ?>VXGyvwى)S஻q9c:~N? [TK"?=(vWז=ƌ&[kTom~E5c8qE.ܪ!,?;y0 qԪbti )w}syVzpq +ӛ;QK?oz>z{h O=s<gBh҈9G=1Ì iL I@kB@&&Olr(0ؤy{T*DnCE9I7߄PRq! B~Zkq8 !D̜942}pͷGŰa\c=VBME\"ɲ ˗/wߟ{.IRJqɓ&߬+ua9y[o5 1*tM5jTO2{?XEJ%!$J=KH:{A*(i,MѵzI8&^K8`(ˌI@&8Ic9rd[08p nv|ɸWPlJy3Eʈg"tc7KK.E&2 sqǻd :;;1tPj՚Q~ 뀳1gM6Uń 6kBo~&OFe:?4<E]89k2=x`?~| j B6Q D2&lIIZϬ3xg !9Gpb@=VZRIwyF%0a|y'b7vz (=IU@ / q<_Uŋ>V>kHh7hzu:(82)\;VLbuQoE.{4`O'i]Wɻ= >E?+ZQqGTESO?͉#{-%ٶkysSqɵ|i*g8,Ċ;wQssHvx{ٻ3zI YNOH 34KM&8묳ݵB%3K'VZA+%%<ΆrW:ZHՓonJR2-VB&JyNBc|h-g\JcȐ!4x/B*c:GDIM-'sƱjg.q|d" $BGG89fVVrڌ@=] +VO+Nw}qψJ:׆Zηp)FE^g?8 1Y>(rt1}R)e9wjg3IL6 Ç7kK8K(sX\v>K˞kIA'};}noJN3:g9_$Kimv5zog~2)٠6٘@ +>JwΤ}ۗZIg}8ec=ϯ=*TtnYrF^7bs:l7jmF1Ƚ"k3 ~0=6yMDq:no׶r՛䇁3?UW]_pg+.RRբ8P>}o,h̙t!;w"W_o1ɶ=d᭑%a_(I}ŗ_j8MܸF@wϺǭVvxs%mfNX6ZS4dUjÀNt1N^~O\@_0i7?GȯJ{fݳqϿ}mZGY ZcM6۴2}1^}|"i}(49tyҖ[oDl럹Ͷsa\zgqߣ Ǝ؟Foh:*my>WjU:gGA~оGC>v ԧ__d)գ8j7ggEoecu,G}j+ock/Z -E눣Avb}sfC[l{vjKYgstj5~{gI; 0-iB?2J^ZY_&*~)hK8|]waV¬Yab]vnYE>"?MSu݊RF^ǒ%K0sLu~0`02ƣ<ܹswy1tPS&PPXtuwg܈y6g˚&f͚ŋw:t(9i:cc 8RwySIZ 3gҥKс!CBkQ|Z#U2POꫯbvV#f"m/m 23lEa8C-iڵ9k;V=doE5$ľ/7Ե[qᇿgY>.TZn^ιWuVB>}ݬaKzBФbUkmmY''&eu_ۯb}u΁1SϧZsVθp]qz̋Vimɺ=XmVt[;fwutX*Qt.mX=MMgbh~di@ml 0.NbT+6 !nq!iU[W`-ZkTkWK,mEi͖0)a95Iڅ_6:ŖD6gXmHJkb{x߉Fzl4mrvyCWwzuBĮގLcb`n,ZLvlḎ5\[Uw[,Z,g]27ip(hSPY ,׋=8~A{?k*BU"˹Zf, d"ʉȶZuTvb)eaz!s;b詴)tb0۪XZ|7J*J+ tQ*N[q=ι1 |(eIE+魁ppYN3l 01Y0 5BpRj>w%Pڗ{M%1!Eŋq}7D>}馛bwoO` >F˸;Dꫯ?yt&]P('gWFGdXj 4dW+.*4zKibƌtp` 7D̙3>o?<2ы IDATd&M8Rɸvp<->̟\(T70YUye`]a#{fΜaÆ^BJ]8xQ9 KbРA>}:^zE|)xg?̘1O=&L+wq 2Aܸgb?7?{B mĈxkLrW;3\Y[zcCy;v3c1fYv;LYcC{0e"~z82c{1z)pZ-w{mi1lՎɆ̅ǜ9s5\<,X1v?;~)e[~lwvY ܱ>YGG !!Ba'0tP+BSN^Z%}_QdtqkBZ[|E@Jjոa?+p)S:#HSzP#IlrtFt}3j51tu59*wyhV+O@n[Fn\V9P%`pmKkF+jCw'tTRa#I2DQ>;K`,jFmf΍駟 Bki?٫>N]kG'H+!DQ۾+N|ǴiӐ$&9Ck Iz`M`B{FX EX#CBJV +^VsqI$ѭbT"HV"-f _45ϰnEǝsxX;}PrU7ۖe[ZI{3ż4y ]67ވo|80qkHpD e ڽV+y$ ::jnmB,˜@)r^1~8Kul8:_^z!駟skBR e‰|-0 sh:ZZʼnh=cJ]z Y2pG4s5 ݖ{1q7Iq "#j¨1>n5IRa3\Wm=$&tw7QFyRDHqV8@deȝ4 4Tٌi!L_N8Ōd" R*iyÊvil9^qVz!1e%{0X$rU#,%繿`O-_<Ϻ?Q\n6+_Kz }n /o}["ӧC)Lp:u?x?D^_K/_vG?{oǼywرKpea 3~gƬY9׾5֨ժλj-'N7t??O9u4oi*b0a,XիVao~gy&*JAzq+ѷ_?zя~Zj5BÕW^ )a]vȑ#ƍ1c "w8}9ܹw}7#N8|F^s=܃|Z ZkZ-L6 7p&L{y0d.RkúGYc"!5eBׇ|51 )IMs SV#ӫL(jjʄɬfAi2w7Z EJj{ ӡJ3IYHAX -ޔh%$>D'?C0{әgM>QYtK~Ľ߈{AD=fԥq2&:3w k#MD1߿'Lrŕf^_}νԊSj)rڞL? X&JRAB6p;n Iq9`8m/DBj:\f[$#!5y~~7e47[*ĸOpMhw=}K~ʩp͵דTrP ;Rn'eBѫĸO#IVBi&i#SZo]ׇFB7/j9hhm/ܔ(D8u`R,3_\r%\;7&r;HypeA 0Hq^?Q5c8CЫW'$ŀ\m9$INP^[d-[Qh ~[8ݻ7Hkx:(@ׯ.-x봭&zpC[XY[>n8V ?3ql~x>.rZ->T$gu07MUdC=dޡO~10αt2skH dޱ]듶K#T*U*JՌaɒ%<8A,/$V,,ǢE@e~oZVMO?ݖݑk36VuSRqw90 lsʆ[rQ:ֵG)S,m>(DB8Ng`(|>*%)'1!\ 84MU}S#`޼yBओNg;ͳ"oĺ8mVeya/q|Dɤtŋ<'I8n?/`[*_N^rU7"m, 8PBϚꃭZbZ |/C+fXRjԪqHE8Ji&ͦcV"l~I}C R3a4CT,HJTEF0Y& !5:{盾Cp/Gc>@qF G#j3-  F%]A)AB(I$;B˨Ukh6bL!$ RAd7~"z 3 pTkHR!Fi23><^ZRtkDPT!Ri x y/n,: ˟{!}aam5Yr2QEsP}Fidf8kM Ib-7r14P9aƐ*U!Y&F߾}]7#"Z*9'mFYtƭV z5 MJ%2Z;0Py4нޛGOWj;=+,ǰa0㢋зo_yp^j8sv,fls{ٌ߲AFQi8/%m9q6lZkzv91u(EHdܯZFc`)S&8έފ{i:9"H)]&j5Yg!H/فqlDV}YcJS.$0\/uHwbHy >i*PTz؊R<0ΑĶtr1ӂYh4DHh9X8YiAJƭ 'M!FRN̯V " :Խ_=?QV 2=A+_ j^@:EVzZuQ_DV+A^^{,?DoW^yōOkÇm;f<ϵs˪U].O5PzItz) m݆ 7 BwW8 hu~!iqǢE\4K~~Y}<.qwD <'쬲Zn$CQd 4<_e' !܁6y^QG]HaukMˁ 4DD^}tM9%%J2N9W*)\rpYƺ!KSgܷR '|2*ժy&Mnvfμ s5mf=fϞ S\sn^h`yrJ86ƍC%e˖AJa/()qc6Bggf8u{@X 瞢O^.߆ԮwY7?Tn]R8ŠA0g{o>;0bt:1^bvCe{ #M<_|141sLI. mG/3 }Q4M(O:h"H8 駟F$n< ,p=/pmA>A7'ư`{쁎Q)>hqxǝmڙ/=(#LRxqs6{ p>j /`ƌy p!vm7L0Fh 0;/)ShO>;u]^G8o{.nf4-,Y1y9״/V gܹs!cH!>B(K:6T0w\$I;;%Kک9֫ r\ lܴ>#1w\8p O<v@Qh6cjU^݅K.7pl:{s=ÇG$>i$ǡYKȑ#<@!]?% (c|_ƈ#\ȥMa+/N0]tQ_ 5ΝBA,CGGN;4)5.\N; ?8v[L>vN8S{o瞸袋ڪ>w_aӶeL:SNuC⠃pf´iӰxb0ư;Ɓŋ#/:'NĐ!CpÊ_!s]fr ~߃1SO=ӦMSNu]n1b0@fX`.B̛7FgpG`̘1.R)zgvkЎsKh41 ᄏ+hu>i„T̋,u#sO6|K*I*k''ͱ*RVJ*z^p/EJ*$qzΒJ*RT/JגJ*$K/ZUdI%TҺURI%TR^TRI%YRI%TgI%TR %TRI{Z+rh![^RI%Tr%TRI%+^K*J*9ΒJ*8K*J,J*J*ΒJ*J,JZ7*TGOVX>'czJzя'N-?48̍ "?c AshP@DcE Jy<<7RT*TZ0OX]u9TRI 1 5x}oOBHDQ(bB@c4<B$I)%aeJ+DQ49G`J<`)T҇@:Kǻ$7'˨ã$@#}@ 4y Pk(,54HI4U=fD[\V5AK*:6q^ȖkF sTZCky "$I:;?$ |߇RQAaBT_RI%}P`Io=2 μX7`J3(4i<q0 QƀfJ"N Hzh,y,ד2;;RI%}Рh^H2 |h$V{?0k` `̃sZ10FzRyuqKQW9hZCIzs>/3@s.q4ϡI+xqw_i,Y>OaѐH[1"I8gF>Zq?sqzΒJ*]' oo4nG.1@;s$@ib[ x 3 <{9"#Fr䁴M)9pկ~Z;1zʸ010x%ԟ@0aqFk-]-s5b ` @jXi #3!X/BV#Mv;Z@ (8Z-"ǁ_ Ձ 1u8Ȕ@)ey m$ *;)E<( @RRΒJ*-ӊk1f"AR$@904T`ʕDU|oB݂`CeP!(UvγNA25NJ{@ӟy }WUGGC 0֨V#0\&F5AJ ;f J,ځC8s h0U~xOV z88HA+D ^#K2pC V9g<^ũs)::: Bizz1}t!Gqvu=JC)Z45>RH3 yc%pT@Q@p6vCC31 BR8oaflŖ@zx.dY޽2-g#Z3(@PV'M P@V113NAW@VAGC+&!Ư4 =d媊0 jN1,ǎ,>Pry~LG41cO !CCf!I$n6`H)7}h)c,U{pQ)%:::յX9!afȋƝܘ)|˰#n 5aq Ua8b IDAT%aq-9^!Djh!`!<'\zc`ॗJf;juùDY̲-DK,rNϤT,P$ yH! ol.| :Fj"MZ<ģk 2P %o~ާW_ MV%)%zjJ+xF3w;i(Rx'pマ7 .8z"<5@|F% Z;vᔻQљ%cfnW&'2d>9C&60@7F RgB(*^$D&J"!5Ǜos_;v 7;W[+twdeRc(s$qFŀhDc^ Ͱ k)^g$h)%<)Py]vV3Iz>O#hQ&]O|I_[YnmbotaHV(71,Z M5o5vpE#8r7ݲ帜8y!t9_c^7u!ӂ^ZL2}uǏuuv:F fgAڄUtj$-fI l='''ߏ*용8Gۥ5xN)E᭘\;<"q~ﲿ_SeH|ߵ(î]p]2)h4t]@#g?ƛn |!EgO*<Ϸ4#f2noq\w\:4kثՊvXr-0dqqN52 <:ҋcc~~DQ hz}\! .<ś1qbEqӈ0ءI~N)Ru1R ir8uyКkwleÆnm'8KqAg䪠"s5[6ַ`(v4Ȩ^/}ag .{V,+p]}v:ZVUMj>I0fme,^k?̨D\׭ʹV5!tvIϵH)]}~Q}_]JITPS}P+nTS5V]U[]˫?^]뺫\i@V׺V|n (*;uO<RJسe=8LLL01ؿ?[nF\*Њ\x~6df~~o~Ο333LW.,uGh"+gCy`G !{@En)HL#Mkq}8Ha@q*)VOJz,MW(<(VG@l`~5YVT+^r2td\W&ʻ[A$YpVUF-+֪~ݪhhmX\\dll4k*iZ?q#V۳GYF9ʅ  on ^8~;Yv:!c}YeÆ;Fq~=PPht:]"ߧxKK "Mbp]^=V>d?|/[&yyEa9_ڜe)bcw7'4aDH)}NJUhʂ'[qHS~Xtp&+5 qlE!\^/aVȷz$IR_:4!JUحfY^ڲ-Mdr6MR[\`^BE9bR+[*km0F|5]Kp88}&Cȵf-ҌBZVkN[+`F}/X-U^yy})" %N6=TQ๒N'Gkp5ceM,~Z֪NB8A@(+'"J]TaІBc0(aʃCi ~إ{hm꒦Jl+K HӔ ꒻BfA.E9\!F#$ ,ZFRW S#Hu( KWeRVXDzla+֚4eD XF^%J7|fHnQU_o֚N4qYBv?</2ic{ x¼-J]#Wck\3su\ٍlIeZ&q5j؈~ U7mB.ti48uWt'z[TWX"SY )L¶)ɺBRJ4% CЧOƢ(Ӻ)q=8NVSi?XI ÐNCj؇kv^Ç"_qd},zs*/ xgpb֍l8ƭ̉/Cg ZiF:.9ܓO>Es*8?Ax[ 6 o#YΩGkkzWreNg5]7 B ꇝ}M1Huu:.aq&nVIei֥MGPb4*g7I,]ĵuAu,#UQ*Gk%"UV, gE?͚v;s#UF&΢>I/я~N/忔ekFO>[fggyYXX೟,'꒸ם(׿~VUs?Lh]VScn_Gkٳgȣ>Zko[gϞ?q^xp}k=zEx$3 GREnR'}c|_U)P7~V+y8R2(2!$cz*]ĉS8w={o̤yY;$YA:. 3\{8p6m i D)(qQ5 e4J'tFh)@8Α(SuVmm;ٶu CmKLqy|+$3KE4Iu\#Q%,dq˃~O}5ϻ.vQ{Œ"'OOٷo;1vIX1 ּ`ői<m6z^l6yϞo_";GT^fYhr]~z| _2''\çz$7+6s%6=,N1yaPTt:>} ܾVf<53ZЈZY0.F ֹ͖R2EtR)Y^bdd1(>^{-_,qAͶnH(,bSح! ^Uå.W)-m_0!#-k%7oq޺غuk]qjQJ,]".ti[OOkx_Zf//+sc6m\lڡ( FFFL mWU~w<Ϲ\̙3gػw/n_s;wdΝ&2.^]$IBEH|Mu)&lX5؂0eFb6lDaK?sΑ$ nC+@\QIRqկ{Vv櫪b8cJaiVJA4bѠՊYmD"z}ԼQ]vQ VofKgΰ~R|S?P5ٟ(_@ud~=RUT^ZWWo|ޒ˿KiWIkUk=i0t&&&Ȳݻwsohѕ@"GHR)qIhg>ŋsqmCw6$ЬϹ䑇̙hG\8?΅ gn~Ny<+n:>JJN:+]/c9qAPf5V7V"vO6?.\M7ݴbCGn?3?G(  zdʱ(H ivQX\\Z:O<5h6׫FM~jKi߾}\x-[0337LrڵF m#~W~w]O_W׿N 7x#=5=d|HAZ|PvQBu2Eq?A/|{|RΦc$_?ukerrsaDZ,%ƃ~oKK<üGk×%zŌ OsAv˗cszBxqIwB,3g.;ɲU?'?ɯگ/1ԧjOO?IRּrb=/_Wɲ{Ge{|sJ)>h5R>_ɟ P~E&qեxy&rXkqZ2CWL333;w(vsY[캝>Q•)YpSyP zVPD[u@?VZa&aGعs'.]\45?-pj*2]L.ܖ zufj NbH}}{A6ɵBgOcŠ%oތ>V9GﺋZ6m1'NDMP KWzOz{X@e l][6hrs$Pٶm .Nڭ"B6o̥SuW]Ȝ?/E Fw 0UhJI͈&@L >$N^8iAl޼n=;G({M~LѠ299ɥ edIRGX}VGZ h x`,~}Nwϑq! ыQf|iؼymGTFٲmI5fru^139V60y0,+ q`%m0XN~hh0ؚS,﾿Wm J{5mݤ{tF1ęY(1|i[vϳg\rA,q6p;MO<ǡC!^8}kt:v-6F5GAv%|/qz1¢H(K- CaR:K=[U/bM%j$G'Wڎ 8Q'Q;㘨բU =ㄮal<ycR+B׾"-/Ξ=2{:cz V k,KR2Q3h;D:skZcD鄱^tԵYkʮ:fupX|7~7'>,..)?1-⬺t$3\ 4ZMxm7DaKغa<8w=JRMM2=9A[\9$qN+jnnzV}zZ&ã#$i8 nN ˬzW͵uVNHGlLMM !vcteV񗕤]2y58¥UOJ̢bA>7y,+@`&iB5D71l߾a ҁ,x.EQ a#ccmns+7rI\Ihɲ+V5) ɲ>gϞeӦM<ijmw\2 )y< f.]E!n}$h徦ܖAs6ŪJ),j?gqcU//NiZE<^4߽={b0rkƵrVʭ#\ S.S_|R5 ڭ&"^ǖF+y('DHϳUNq< C<3b?7p^c.01>N4ī0dz''OĕucCl8Çygj*VBVRƘzmD:׵v%Yo|+^)/ZΪk4U=GZ/#Jӷ5P:@ĚDZEQzCCCmb@]EQVǏ-[r]wA^d{.DQ¢[S肹9yѧm@׵r۶-hm 6l/6u]tR*,J ZQh_O}EZLkK\y^^Q#=ʿünu&MM&i-曑^/33q.g&*Z:.sss\tw?6YQڇ]>B ? K?0ru;ٽ{76;K/h4)tc͡r׎ dV!JP˦|:u%dmE? G(0vqJl ^qbk$l)U?uq&زegNz$IV"sd%\0ɓ'ݴn ʈ^((lu7sxNZEՊE5UpjA$i/Qv1VwU7ӧk*Ĺu% FiՖ+9CucEQTXUkժ[Ð~*4]X:UB6g~ ٻw/qy\>_8nSSBwD\bjzuɲ~U>OY^t6tyrņOrU][ݲe3f;&Izq\sRJp=@gy^VE$"R:zl(צԀc333LNJ \s: _@$I4UPFAmM<ۈh]']ݔ˗/AJ[z16mZeٲ|Bk5ﶿ,z=~OX~:eEZqLժFWZ-~]vU 1fNSV&h7wiZwUl6meAYJ)q|c>'O@/58x ݅58$s;h8pUN>[nUNch$q#]AF$ :9;;(TJٰIuK[*;TseЯ*g$7gi4Rh\!p|:'sZ"ooRդ߷v5ynlD&$0VUUrbO"BRmix%קyp+=-q9^n6C8pΟ| dB|Alu8q0 ٰq={06R8 '=#LX*gݺuhIԮ\R[J˥ !}F:6Ma-T 5j3HgTUk*m)Z&W"ď5Z`_W?#G?;hZup ޼yR$I<7ۇ)AdW"[6 3H K"{)?Άmptګ$IBĄahϨù$ϝuEq}aNceϠ,p\^-lR+}B^':Ck: c֯&J6|s#0a֭kEבe)$Uߗ=to81?_eh Ǟ;Ayh?"ٖ AuqJ('xټu'zM" KRA@xօ\<kѭ뺨XLR +M!q?pmՖz `ff xvS@ Z#$VNtM"׹?s5/V*!QKA>~,m2Taͬ*s>$sejfI‘2H\wJX]ij0DBpoBKyC7" A@sQ0Yxg?֚|׵\ AYPT9:gz㫖ה+"MR4Z-u6qx&QO;Yc"pKﺋo^ŋUFzTN=L_)gɊloe&I3@^\ʸt>K~( F׭!rB8:iVJ>2+Vn֩X1Uohs|VJ1jc⍾,ni=ȑ#9ricӜ>xH3<]gˮЛե`4Q+'RrM#C]屇%lgt0J;6q.Xha%Iʥ+<Ի0{d|8A葦1^7֋H <NJy0394E~'ЦRK. Ƒ,zkI^7h<7"tB+rr2"ͪ)ehSx*hڞ i+ߓ|5\ PRxAUd<c Pϼ}\׭Ko,y8ҕ|#=}/Gw%&1;; C캠 C!]j$ ԱHK蹌NIn4q( r:٫6{ކ$&GJ"^# c0^fjJ)D m}46+%|G).bebBV# kp~q~t~9&amUR +.rO%˲ZC=P9.~?__k=jlXF9蘿q@"DY&~z dI (TA^@f\%M|~<=^4~+A,(s^~~"6Ast^0)FI,lݶUW mhQD#BH7QΝ;9p)\I$)p80nx :EƳ'B28pWPY"Lm$.C!O:v^`t)+;V]J󏼰*oz(P{^'JUBܝRpa) ZR6HI˗Gv뇇[n8N:kWsMRTvJ^+7L+?M{*F.Ѓ ,ؿ?GcKyz P s %2'i5qcW%Iٍ(w=2AW^yvqXF7[ϢB@)QMgE4(]yV7)\'uShsygr9׉?Ͳkv4yA)[wJp( $G:Z,#犛u]'\_F+[i5#8axxNO(o߿"}tpzM< BLt9ԋO_U^|E)hFYGߏ(Ҽ(N TB(W)H9nxY+Cbcм A|Ẍi&֭[G#I0g% RmTk[J\〉݀<;lS5n!Y\ьfy?D+ ޟi'F8Iو,dZs 'N-[9r!)xGp=@B4"/Pڞ)baH*L[vtQ  WY%miXiaNb^t8nիر }zmkM׫au,ՃV!NPqFF5_d*eb˻z At[ CY*jH1HnCïh7ĭ7' |133s\r)8|6s8+Ȳ,,t&Y)gҊ\D!$EKض!-_˯YZg~ce[\_ A\#Q%Ծj61,jTtr [rvi*%e/xrd4%Vpd/2H8 4Dait;9R@t:&ƣx9q1_Η=n?HU رdz{X IDATn ?lp%&\`hM)TF5h4}lݎ(#h3\8s'Ng={pqFs!+]&dM'}Vn  ^%C~u) 8.WQqF\w=jfa5 JZ܋\s\+nJ,'t[;!tz4#Xxۡ#>xǑ|sEwEiNAs:<R9Ft>Cg}ϭ4[lF^`AIreǺ[8YLg)a@>Ȥhiϕ͞Ԓto38Hk$</i#GD+ŸqOjd d*%qBhQH#S'RP%A| _umKU;潹rbɢlQK}C0"Ԣ` yvI)h3 _({zzeEK*Q18H-^%MS\sMnbnGFqܸCXiõ֊y"45(eW#'RBfI `"$(J*A"60iCT3b%j]#IqI8A;Leu|2,\Q &QfIH2tœ>%w@6O$#Sd١PTb7",X#<3z]2rzvZkƌoL\f6cw.˲!"ڟYE__Fk\4&p}$P >A>{$x?g 8iLoo7zjp<,MB:(.qR *Zlׂ psߡgVDlu]f@Z8jؠyȏY1Xm ^!+iajg)^n}5믿~]*F>M5-!zCfp"b.0nP*rTd0F84͊SHAP.Av\2ӊ@(dڍ鋾>|#qeLk]'ACVjOFےRV5*( !(˴Zd7IgSwÓ<:ϟk)SXavy\y啼̜9K.3gw}7F'ws.g*q%h*&0㋃9V+L;Lp=n-jpI 9TvX=#c(.fKYqgp/s>#]^_46ݲ^oyAw7{cS?|67wBQqSQo`G_Ȟkz\ #4套^[G}{ɟg} : ƌC{?si1~qY:w}7_ҁWWs?"BeԏHqH9dLܒ$1""Y!u}j ^z5̹w^p =(M[$ F#2J2Z$\ YD%)%?(wΜƥ?drٴ:~q <{6L|*47bӟW^y+ 9u]wܑ 0rtmp^{[m=!d[5F_kd>*yIGeymZyfۿ1k,2RJ^{mN=T{on6\{-0'N<~Ituu_w@%G@-qp\(MR"I3??(Xa$hii{׾fDh?cqH I(1t}z^tz ٖU j1M0}8{wy~\WR)0qIE OOK/1o<|׵A֒6d[ly33nͤm3~J+dm/zm.{~D\M7ĸq!N9b\sMf͚ȑ#yq3834gu/MTb⦛nb5SO嗿%>,/o1Gqͻ.\s5f+.V[TG7růY0뺜s9L>Y^;WUlq„ L8ŋJ+/`RfmƌY.\=&v[tuu%4)KYeqZb".dU0<|f{8G9N;,vmȕN:Dz p7r7UW]ŷ?,8cꪫ{} T@kO9̹wl$I]w݅s ծ^?3:`]f͚aƽKL2ZS2c *^yvaկ.dSLqn77񍢒JAѺ+2Ϗc]*-b׊ލvaa*fmVd#/*FIPT KCzkfz+{ ̜ɤ)l&]=qnu0ۖiway]]]/[is^JQn ]000`2d Rn59~1Y!q27Ff\iO0w$*3FlC?+)8T7v8'ts'crR=+<_ 㷿evCjMStT-}c6eO̼y .x`taf˪u_c\aT/%dzLGHϱG,Yb(fV 4 V_}uZJ{%KVUW]W^fY@7N3f W͘ۏ7`*u\..N5r8t,g#~9#8q;RJ?0}tzq:UFAV+X5eǗW:? 8c޼y3QFq300P 2!bʥIJ "g&z)|jO/QW)??0g6bc5dua-(+5,YR;뫮bꮻrgF:[k~=M6ل;oafmꫯNZ%=c96;HtFESvމO?ZUW]eEZ*r{Ⅿ1aVlEY,Fj,;-$pTf*-N:"HqjQySN2FX N$YLV&M-Z"cnjkMPKNlʀ$ΐHF"#WRiqi#<=܃;SPkxgYi8BO阱v498:,T*Î8ou]m6:+t<-bޖ_Basѱ)K)!r𦡌u]2q!B[k$ %$ZHJ%VU,[q$VYzH_Nb:?r>(d%d$E/$MS_W4szK*vǓo-NJRmSG^k^ HPwwc0o hZvmL2srȁUB\B>fZ_Zg9ȽR8Ah~bFlR~ 8'(J(*Dd r5zѥюb*.AÝW@Vu8AO` *4Qheh_RHqBQ\Hd"$(i#ēBddu]ս翣w\P./y26U~{Yg>? 6؀5X/|_#,6$IhZ}ͷns+(*oq2e |:8tuuo=޷ey'1kD?("CDkM\T;XKiz"*=688+s-҈JBxB,KCEbXgvzuG{N+. daJfHP6BԖP1EyţS\Dnfz^\r%n| S, +nkRJ&#G)Qi bwfvoqD!EY@^oY=ΖTPVvyVfP)(*fzT*,uV7`pY`Vt*%Nc8 ȕ8#|PFS^. ƳF&eY!r\-(˲w8bi \̞=)%_ڛ}Zv|Wi6:5"hZ( yAJuZFV ˕0jtTke)IMfEBPYLݠU}>Z-RF[Rn {`9/%(JIӌZz Y:NF( Xn -~D٩+>֡2FW[NHjFQG]]]T eKp) }4{lvi'g:jkXua~+p7fڔ~*U1=L QJ*===,^*>hۅe"md3O{|fi]wvMbvc֬YyO˜;KsD1j~kXe(^/rṟGxLOu]84.GYL)~**r0 yl IFDŽaHV+wqG-p~.rGy .!j|3v$Ip4?a2̎4+9HGf!*Pi(DOuI3Yd QVR3oEP{nS.–y+Vfwm݆ܐrC^K/[8C^Ϧ뢔bUVᢋ.*ReB~.m%EGƲxj3ξE*^#?oWT*e&Mb46Cໃc2E:̐RUt}RWIC.JR EFJFjuJsu:;, RڬH n뮻Ȳ=c)PdmK,Ar?oW*>&DaF9Y]w]tM\p|K_^ rLqӍ3^3uoZ<`O?Vwc}cFJ;3W_8%K0w\6|sxwY|fnfm8qbѰB=@2-B:j %ɒ,J$)(% vj 2b$Fe g_VozMUϞ6ϱٳu]}ݹklj)tɲt8+]Uj=GH* odI ~<?|+}̳Nr$4xbV]uU{osa6pAKRazyUJ%vqG/ͨQp 9;vlA3P87 IDATD>Uab%9y Ji0 V|)M7 SRc!23֙B Wz ,n5 (T%,U8@JkȎ3`|M?$A0 Ð3g뮻ٳgkk4Pʲ>h/}nISnuY4+ JOsϢbv;Hd',T+I!_RO?o[^x2*?ƍ_2SN#gv3|wG8tЗ88SkBG~p]V2Z=Nhu%f)-M41GJXZ dPOh p[!NB,z}C &Qx#[wKC6 Иi !fΜɞ{ɔ)Sq]4Pdq}89`Z3Vyߟ7ޘjj,(\zUo4j73g?O6]]\q?W_^9s+kfnD:.뭷+2o .Rj)h݇) N}[b,LJ%JB2(&SGi5ڴ .'exbJ @O!@uIrPvSC)rjyww[nG |6Z!^n~ J &I9vi'*8^sH phnExn@`IT ł.$TyvSYe1B|ӧkU2}dyRVp6`dy=8P!R8ҔnTG=tUL˥#Y#N[ij@Z4FDn\n+VҴSv"&8eԊ}|cPs_gGr 1LB?0e۶C /vk_`"B0a̘ε7ĤI Kzf&%8lŹ ðA?,d6 ک-m֘e*Wqj6``.(mL,]Zq !ٌ<^NGI|Jǩ2NH[DYXDh'A .:w}|rDF cljz#[w܍_چI. " IN\F'@(a7M-J}b\9ĉ1{6s-kkl77y_Kkށ}8B!)](IqTi(=2L0=R $Yt,)wdTeHAL\[ HԀОs{|KueurBB}=+pEI/~O.;-7'kȌIǟ"ES.nu{0xS Ǐόٳg~W|/VB(7Ib:s^9f %L6Uw@(px>՞*n 2"DM\7FEo-wI^-Cf(\Ee y/xW^wNʝwY^ r\PDC8dF&JsRDk0"N()UAIK:in,jo/p:͊b\ SdA2D=Wߖ]/wkC=ЀVHdT^ڛG̡q|՘^`ٷ"ww%u_xyQOZ%lP1ک5Ŝ3"NQؠ֋ mv7AjPRm6 @6㌢.^{m1&MYsnu [n%v] kTNTg\v4rsŤZp)ɫ cpUҨ +;|I8~@_O/6zJ&08Eu Nbku$ [m|3{+wygӃ8^Á#wLdR+Q* ~Gp$l2d!i53D95M~+›eVU?8˛?$CkA>%T1a /wBɲ^Á20!D 1fĩ{YRe(e Zg"KY,V[{gy,ZPAjc >vZV}x}ֻ~BC%;{˖ECI%a]4&;u>YCpڕet CݤlHsH=jU8FksX0r/^̂ jJfM6)1ЈoXOsUhT_=6㘝vڑ_Wwy\~pUVɘ͢J AnӐR;'?ᅜp҉㺴Øx8CW‚DZcNSPA;dvGo(tqV)JkQBCz^x3|&ԧ;v,ƍcb 7,tMO=Gdfc̵VYeYg"Yu(u\Aߣ^E"6)ĉk4i38 *W oRbEpV~;Y̢a @&)tyUN7y;6a YJzd"gx4vd s|v i7BŔF ,Zn&z{{ nx$aJR.Y;1c/'Ofٔe$)4!`_ 8!Jj*] 4, .G-Nv2ir?^p2QK(Tq} #Fj](Tjd>kh49rd}.^\z2uT0,z6a>zq* 7pJ]v۞^S1zp$(hCJ^HTFA$17&Ô9GTeXMQG+j@:IZ9>(JD](s&^xaя~T@{lE[nM7 'I'B*`.17ޫM%A#̨T*DQDAW}ݗ]v7"I$T*f- #;_?@C?loKtk67xdeB/?l{i>r9cF}(TK  !t8 $8TL @YC&*U$C{pg0\O]t:=<#lvH\2%& K,Rb }nMwC=D(cm05I[_\zOX40v;MT^czBXwK {Kf&dJ#•6p0uۮ̚u ^$ rr' IQ*{LWr}H@ r3R-.}!P"5iel;󋛥S] Ҽp3p(Ώ:Ö|B]T*^-j,.˲b8q;F8R ۞xL1rd ff#OEo1?#tOdv`?p$:EIYd# 뺠RLs‰'E'OfW^syy,^s}ԧC %ZaDԹ1חɭmFt\ deBR*UhT\uxA\{ *UY ]2'H8wpf6`P*`NQzZ3Ds -'Bj)^2A3z8`~xeI]gZA l{"X3h{BnAsҲX8&Kuz1'I|A4y{֫qd2+<#Kz}?|)h#n3yd>:u}9V=uG"qW@8n1 j69v[)J&yXvv"#2JqzL2JnLZkM VDQDM&HED$!>-joV̳5yi.5I(ɓ$)e9G)U 9D}4|"x HJ$A'I"t+DVaEtu׸̙{7Vǃ>̫\HfN&5P j4iRĥE0yViU(gβ/MK PFfzZ341Kcn5Q'Ԫ4[z_M:&(? QLdD')0/0{Yde[7^ÁqO5H__FYsVeYF%EI"lJ)EWfmp!)$ 0rAɶ-]jw j*zE9.Jx93Á#c!%(2#F{r3nܸ9j(>Oe|uq=v؁Fv'ʗ{ Ҍ&mK?ra;6)$8qi 9tBxuM)T'q`Ӎ7a„T+Ag J$ 3$ us 3JGG8t6t"8TTRJЊo}xwwjlfYfYGx^B&vQi#3n1B$Dqn 2277n܇PGl8uu *Qҥ"$FhpeJBgQJAA #%4sEԃ7O%Bn: :=;Uׇ(p~\>FA@l| _gZzG.*v}K.>2vX;lL)E__#Fqgm{K-NH&+ 04\!H,1%\E 4IZaUoPi96I?l"Zk\̘1$IbB}/2Yk!pDmˆxoJUX#D(HYQ,AXV&Oގ3of߽@h+*\5v.$jo|);aiM>HWVqpĔcǎEJws"/;(B@}Emj,{OdM7eڴi?/+O?}scƌ1G}MlЊvytո[9YwOqlM9Xh!]vJ)͛Ǘl~\{O"|N;T .Uqe1{lVUmZaHww7K,!|sjDBiH3pVj-@ Iޏ2=+EHGU4Z 9#&/Ә-ڜkvMna 4eGz)Q&#jd&IZ Mx%[%H/isy5ISH '!%C)P*U\QG^/V`x}H*ؗk@BKșdx8k?=ܓ8 K/ǟGq=z4ipyq'ޚ.N:fmMGE qs> I0zJ|=\z{F^{qq'MƷ>5^;JR2. .{Od]vaڴi||jH)hÍXyOk1ZF_qMJ+ėeXe^q]], Ҝ։x~4uϡE@7x$Y4i5Θ-Dz1fm7GAc x;<dJqfcMVJ B!tNpIAJ~SrD& IDATx~OnP)^meFG@CvղK}5ηd*)l!8?.zdYbҔ'|2Gq{5__+=X#[J8s J./Yu1 A ,K/b=r hԩ//~֜9sxõ^7MDu]($Kubqȑ},ZXYm,^L۷`Cweeuo;2tED@P CQQ1j0@)DT*FQTІ:( H{m?v^@ρpgsgg c?osQ8qVjw"$Ռ;ⵜq9Cwë^ p@N:/8 JFH4R.#4BI}~>6Ǜg"h%Q9Q(( V8H1?GƓx{^w GPxO3`Qι,94zgINkՠ@ZKb뭷ફjJQᒷg}8A*:N#2o<>Or_O9RY5ɳtRkO 34Ve`0hЇ>Z+.4XEMEQ4%J)cժU,^=q\ryNV[u…RD݆*#ZkVZDĥQ9Ωt.#^~$Dϡ;q0p+Z񶌆khDhے$XF#M=ߏwH;ɏW.@Ȫm ?[E޻haMYʣxE\}'WUDɋ ~S$^0y:%/֩@cH|(-Y0=./؎/獇gOǘ'IU֔sk8x\uTpDxG}4zֳd4M,@8)̞3883pH~}"z*&R@2 ΂>{޹X#wKӔ];'}cxHZ)֬ I\{=J^ׯ; kk_f2MS֬YSF:,Z=u9Gejqo䈗Foh0A؁M 6&dXa<*!A?/>2cɋBlw ‰; ETm@S<3t[)F@Ig_pWtn_ɾŶfa*MZ-ILP*Inad@\#It(*y#?d͸kE6i]P[ODlh=e# Mr$ťK%˙K/.o\]~Y2jO<ʫ)4o뷰g͚5l vuW,Y®ٍg+x@l2\\}2xU~-g^-[u]p8l[n~{챼ofɒ%|o}>Fr'BqG9c^Jp饗_>/9~q}1sn*$namEF=VpDr7Bؚ'cm-,U$-stC E;jr L%O*5HezAF+.Ak `K[^s Dыh"*nd>Bl AŸh lQI> t "Ad&!OCQب|.VKH6"cr[ ȱ#[n"Ksu u'=O^"m(K?:_ @%tM BS5U{BRjD(iz#)x9p]"ùP ~YAn 8%0vɆX*5H-Ɋ|_8S#5o9Tf_il @1Bn:Wy?(s;Uz?鍜vXqՐ nAp qnjpKwfIX^R۱ߕM% YKW ۋNիhZkhIV_xA"FJȕ"e"Nxb08f$Dm@r9ـ  m]mک!s7[.]v%kOHYvͼ7RpjGBXC6yaey%UBB]5ŗ>y W^rsXoq6G%5iXKȇ9yʆ0g2q>)ns̘Z4ARRLA|yRj*nfz=J~Zn}ã[@堣y> G A;0dqտ5~'J\`Z0g Qyи%g A[M7y/h!>3>S8$Ay(1xB!Xn \WXv_M,YˮZA"eXVb pPi* [T]j$z>URBV)P JJJ|‡R/2ȼJK^Ew4Ƹ"D39ƘF_VhZmPBZpYI>NO|ߑv^L(vg"C^ 8:SJ.RDhR=b`oչy֞{pe"Kh(&8HБ"7 X\>CrEC Së;Hm+:Oj֬YVrKNkέp/0p:p|(_r_B4;}HX]K]!#B WYGuO`^ \hin7}yc3t0ymAFY̶K 3ؕnYF- Z+ ۯRu'L)ԙ9~4 0I;58.A&mM1o7 &0*nDA⑮u8~#WCJĻ{I35~N|"Бg8Z@h@ =w 8E#r+}L.d Ԫ p b9ZX^k֊GOR^gVFs{O"<(QYH!KfŕK fgFi)̒Ě/ QEDF XP1xЪa/5=3G Ts )ɘ;r&ZO9BbD0\KQ|;W~@@N(3 5jsk/oN$]X+e]8 aqbD$mw?̚ +`*֘J)TN2.ЌFrx_;R1ޱ`aNYd{/8nӟ$ GѓMgt[gK[^NZ3#yC^܂ )EE- ŝ١o2Z>ޡ|0¹ $;J`94S'B`AxH#okz1Ű 4NeѢGS,6>ҟZ ̫ВK%V׌z &5G.ɢv$;&UTʨ}?3 1:+o"xQoD1Lk?/y^`2KZp>s]nAs}\D"AxaA"'jAVw ؀잤1p&i# "VPl7T/XwՃDW/c[ɽ*`}<x9{.ذnn'*pOh8I»@< !3lB h%8$s+&naVdvKve/azf04eD#0󧿛@!Ya)9:%[s rVV^A)Sus &׎ܽdUVU*4k)L̗Dr#`5ZoOPg>މR4Q)^n?^dW 86y:hY1c aY؊O}ZI%sBqʊB~~vOݑ:gs@hIgvϿ! WMjD&4.籯1 ~,IQX5D[*qzHaip1VanOS d A tFD'Ży:N6?8'͊t T)pη~a<|x%q-QGJg 2 L1yAv'$ډb!IAf&,/YWcQ#}Wj"B1|,Zk;@F}D|_G51#?gnMgaUͶan*BTw ۸fo̙s"A ?;~Uƕig%Յ]1ƍe^36 "'W>I7-t#h >e?$- )_Kn.TRl)Bx_ Yyh,4B^9u:7A Ly& qE BL벆 g@X@Og~&8\$ AX%"ĺw#9}glPLNoGxP ;d4j󊗾y,22)g'y݋`!Dd!eB5 r+]t}›xOpwjQAQ䤭gR avdܚ}o 0Ɖϭ2UmUU⠷sѲOr$Vp $cmA_G 0Ζ@<(|TVEV/y;p;2QB72^E0Da)F &ILY69+}hu.TՂ 'Do\RSp?jkQBc,JvIp 9}c_C$TBp1=;LJc/[b8֣\EaP2A4p@ }/ۇ;,Gnrd ]KQcɕ]nͪW'= H[ ܺn΋^b'F2j͔0$"!S,t"ɋgU b&Pa,":87H/``HjN_ЍRrSȘ%y%Eb<% c5ߌԝ1p98ǝ:\c{$:AՒ$]wP)bc&zK↟3rۯsw/눳eQC\]q]Yf%RF89+3ڝ7!a+2Ra'9nG) K@`CNc mm1]>UdD]kY! $!h2 APSHV5wqyh.,C Ukf(˲C~EoZbr+"Ҵi ^K~赡cbԡ4n tPoԂ>)-8A8GKECy7^է3R`oXE# Ji\%qNpCC1ǔ͌8 f,IL6 H[*S? Ta6I)$>4D|7gOFSYˋ͏ brZVn#3^t:۰jlXIg^ s  cl1$y8D>9Aj?/,-pUkICn /Bo B 6!q+"I#b5"jc(qBɉa";ƚdQ:E^!pƕLH :' K`O+.ė(o?4%M"Q:q$rBp-g WG f1 W gO(6F^=AIa4Z8SYJJKQEtKxCQS[4ziLAiPi,+#M4eĠh4"IZH YV D)5˯e뭷旷>97q`QOћWAPđƘ8ENKFÜqNwGg0bӪR[ TDya1 Jn.^0 F)'c9ŏ?1=2TwJD(3d~?x8-`SbzwN=x^# }1Kĉ&IZcfmnϥOT=IF&b-8묳!pKIVӟsTVdNuYFDb^ķuoqO寏|?inZ ქ?dHG.~b8W-DkגXSiHEXt)˗/gΣ6|em%sLMM^/g7bn n(R`ժDQYb(S}xVoHM31,Z-u<*ٝ<[wl]Y@9o1>$gq!QY@.٨@MyqNREX 1y+VcAGPJ:Um=WZN/q s5qLh Cvaq=}ѠO%2o_९(mWkD~m| -tS$a4jXd RJC$w98n8ί}kuc4*oc<\GDw 'q>'w{IVOU>$@H&''Z'O;:Ŝ/_'DF 6Vz}SUy9a 3SYY>p3lJSj<q<g-\1bCNXfIzZk&7GCj8(q1V֚#o=rZ.:K,)?ohI)%8938#"I<'2R,]q? ~i;Lemn8f…gP(.ФXq.\ {(p뭷ug(xɆ,\8-8Gڊ 8(].C\b؄HjՃHacC4-E:4#zfKr#q|/ 3 ֌R/=Z- {h JlIϟO^ 3c9sk,U"gT~/ 7ܐ4M9䓹ۈロ_לvi|!8쳛{Qe_%W\qozӛVnsh@ehF%XQ˖-#DJIecXr%VO#9 ˷]aLFrFE9A B uy_7u Ciક3>'>qկ+J F!I$88_.&%BeedB2ƪc 3dfʶx4*f*?y Κ{`k8+ Yz_7Ɓqkj=T~y%|+&8pVUF#|0Мq:`z*y>TgwEۥo~׿EW\g 'vmZk>OtR/^9[oh4⬳ oxN8M6o9#g}֦8ɲ!O|S~s=c=kY*?yC1V`Epp_}li-y_\hKh,0>7㤅1-Zx;dsFj j!J/+P l5$'ң^{? JOhm qSR5!x b?[|lQzQPJ^ 2$7g~E7kB\t ~0* sVCc#F3 |<E?YJ)$!2!;*EufMqW{f7$ am>B(b8211h4j>neYӟ霣( z((n$q)`OueFP85җSyHڸlzqDnބ''o tAFS(6^7?45<"NƁe!$"QB5 UPg*W9h0H cL(3xJ.j@\t̋3,QISwoQ"09X&lp,5$/$29a%$I8SvVՀS hJx jZk("sFQ3TsI e !Y199p8jrJMG@1 F}2}nZMc M90E;0+ 7$?"݁qP` <-͸[rN|m7B&e~oJL҄PP DDea:[%%@](Amp-bZ*(,= M'ޓt QuSv{XS" ]+˩IbHY7!!HOAPJǚ7ސ[Ffx+uhm"Kk~ALLNN6{O!2 It&@_ъta ># q4{EyBHFct*(C딑0]!#; ||BהR`Ç=k4" qۛ0)0@VH#ek*="Ebzt*Ht$ɍR^ƼowhT,Ɔ) x%.ϐi[S`8I&tTk@HdU`^}s8((\6I5kjیFe/dŮ]nۀs͛ `=11Ѥ>ԪKuZwd44ߡ {Fkn/•E$'$Á! 15r[sE׿V J.J SQUBx| \~Zy<}8&3[k/)~7x;캄￀9nV%CoW‘o ޾__?zM i;mf{fF59t$irz,J03,²vͪt!+(| Yiu;kcvL|VZ, _!&+Ri5w%[қ(ly3H% ћ5k hd AB+^ZP&Hzf $x`r09IT x^s#s vfRX>!c~<_![ʼn ".mU;mJHT$☴2@6.GcKUtbd@'y]Io-Ā '#A5E+V8 &$<^.(P6rK;݀_^ql]3Š$fݛ" 7Jjd=[]&+4zN=]UV I~(4'C2o+(J|rP7aPO?W5݆‚N2l-BiH :؜V;!HW#t@ yK(}!?'4?,5 s$I@EIɖOْo(|^hW{h##a]X~}bٖO(`!J#l,[fM)j|Uoů߰tҏnpeKoWB11`ŽRMI O o%Sk%no[l6RؔOg?7,ux^{ 7E൯܆m8h&xVDu_϶'E:oFR<|?klcFhs!I=*@aW6$Bp>Ga=ag Qbm$X׫ẐXN"È5HeMHP2'b sl8L" zXF) Ko@^@%aB @&rbscC^?v6ìZ\~ۯb%K7#2pڪ7y x B 4 zŒQHB&'A/f/p"!$g7%JRmrlZ`>Čmt: X@eaP% 7*Ͼk=AF#"2t  ?&#*(N$j011ɗ䍗kpH(FJ<4 (cUu?޻N%Һoc5Lug=}{[bEbB`)\j#CXDYRH4FPs8&p`D5)VP I4 8ig;2Yw ܏_uwиѼFCTըsֿOQ[ Rbw Uucr E_r7N@馰XSW2FǯT$9l_q=@Ŕ$O߭C7A1A!Z{{9c.5g6۲Z H8* meeK<>@Xup>|m1sG DHn)|P} altc-ZWX䚲(%Ѧap/aC?I \XӸj|-|bcZ<ƎŽk;9w sҡS kFt{%3_G,p0ʦVzdDRCK!{dHEsO;-WH6I%`^~<28QD"zsscfv2t!2f.Eu8ۤm#Id}=o?XTPT( 0+zKR¯8$D{zo{&U0JR9,8٩qA Tmt.O^qm8HvƶJ ٌ(@( CB)qF!B+7wt"Pny-i/ܹ2^,=GFHӎ][}v3'\!!Q=rt$btg]5)THH. Ua*:ߩ/ E-w ?әI&(KM^xEL7.DYJҹw5{o~|4 l)\+qnG![6mVG IUH8eQ2v%XD} 8;3* `<@FFꈳ_GfnR8! X46#@2*6Ӌva.G&fgsWpU0":%'["G=Z{K[ rA48G7~.)P^)8kd`2";P;,`pX^-q.UeV=LEC?)(-&3N.BJ"iFu{/lN;OYnr㒧Q#7B-iU[|\} BB%Vcl `k_?mkߎ$]eH`krZbl&]mv<_}cr.F[ /r`3q+؛+`eD4itQEB&)ʼn lxTl8[nIS.A'2J cb8s(!Q¢(!)=S 6Zsz^ [Lg&-?g*DiH$I<ӄè-do}wy`8*OD )aSatƏ:Go%M(`K+.\<(`ffk!y^EP2 '7!CXÖ/lPibDҼقmkj7o _dQϚd0cA&^)&}O i0^A( M1LsBD7{.׋8k,;&'&[6}]oO|U6Wyub<"GW}(:kO, )#Hۉ>E(: A|}V 3N{?t\Ru <>F s`pl*|{W(]O\C9?(5v./Q!AMY$8Kt:=,Qm]Q Ar+QjIkd"&zmH3=bXy8E::}16Ӕ^.˱V%)[siOi'rKZ熻,~J_[|la0夓N$YTN#%,hMߟRsKc`ZlDܵ]IH %; XlZo|6[\awDd^ֻQ 5fn6%ۦ{}g ՜ %#ÊUt5/yK#E$QB" B @!d=y[lۆdM#vezz qx?sS%a u laؤ潶Ȑ>ƓDMnUpXSVͅD!PHwb˵]3ĉ@J(˗#*7N䪫}u@ pFۣQcK-~ NHts~/`]Y Qy#hr?x.)p՜jV\H %Ns} _V!XQ3Gst6M]Rl-!TRC8q?Np(@?եwԥwy&˖-9pȐD;MQfIůn/"MtE RhN"c:sH ws2&i4v45{#O`3s݀>f]!gQIG3h8 R䥦% FChzg=g%ηN`Ģ)9u)D+&M= (!ioe~!"%a8]%! 98$)߻Y/gu#P'giPx>u!Rio#$B8pV߶kXp2t!q`C FScE Q #N}{N{u?׭KW_Jb%)F[K5A!hm|k-~+~(0D(8K8^Ln" rl5X *cJA9" I]ฃ8{Ax'JT5eDݝˁn۩!Wai_ryս}|ç0;DVeqN*y)eKYw|܏2Rp BJ6L' y#$$dYFiM{mc0h u%2(Ѧd)NyKCuvB>)dy'n,g8L'yTW\ɪ#6(H8WDI.<Ɖ- PQšA;M01i[䏞mB7_ץ9̳{2,4$;Mi,73>z _}#3.'O;p# ;rdYF*F,*XkIPY#" C&&&(K~:;d1YNC8m"~ lwgS6;q GeDD*#9G 'É'[dLt)` OZnиǒ_E_3 Tp͕?NR'`0u]q(Z =~9Y~n$ \ s+q+xZdcpaޅ_ .O+(SG91_cZԭϛhP^6xPe ѝLKpJ':#t*Ȇ#&ň3v!&YVG( &DA6y/'p>IF`HY'$t *ۈ$a8a k5BHLVGVәw)|M .]NbmlY(|7~=ey7 SHAH9a`>KfQ"wHr]zǗ󆗿ADʽqzW0}l=9hI#2blǶ rPH1@Qp;RCsN ]( M$d& KĘ@ s(׽m~aHYaHa<6%unpF8<g};JP"pH^?5~rH/>rOp=o+i7K@=䰕'%%+"X5O8g򖷾\Igq S8G8gPd44||A-# Eك.j Qh4 MYj>'O[TF8 )q7VގF1Vͩo{9gGXmF~93o7TP] K/ӟ4wH!'Pc &ې8WT'ʶ٢Y( !A0\>e*"DF9$ Fܐ#C naeOCۛ~`(<ω)%.-:-7~k;P>0ڝ"FSj"gbOQfh!ocQf#߶p E%C)KiSHO@R(D|jU4(2@"LV,I.iV!)H:R}d(!ڂ\(%" "(zAa',xh9G FUUČF#^s%Sm[R+T߀8۫6q!eVBs^iZi,[u0d@A%~Y WY.C;RmW93Eha`-9R7cE%R#hP*F"@й&gEż}=RRn`@EU'`i0eCvĩX "ιJ5NG6fyJYLN.b4Bz21%a(uJ XOH;A)v2C Ɓ8(rSPA4JIP1QJacbbш CE^c]L2YAZ PXW '81 r,Z|svIӴ!hDEEAכGiH?g% CE!шnDZ$>WR2$2r۽ <n%6;U~ӛX RLOO&!Vd&,C!3"I:q'S$YZQ@b,M"D⽃q&Bʐ8N RIR Q2G$>Y& H1phYZXaQ"N$QPa"ź^.AɄES;1;3Dfbb4MCv)*RIʲ$ CBA@Y>v099EWjzghguIwtI7q: &±i&'9RS9ᇸܐxnŵ%-G<ϐBv0aPҢ#,))k ~h10ڠE[P)nkxOtB4' a(IG**J`F8`8YI\UD:g݌s8Q4Bx$IRMNX#5ifaYF0TRgszop߾,X^< DՖnP??;wk V[ $/2U^yե;y \(4K(}ܲZnj._};?e1;,^Q뮽͛iڑ 87}} Xm|5k9#޼N!Rn~aFWѧo\z?fѢE㜿|?A6< .@k{7c 7os!s;<>4k z5]pf~F#5ݴV&>ך9AX|9A|^vB_p[Uo#DF]<9w}//\u?N8J2球[0qǗ{Q$Sf{033t:y1g>s?fg#_}1|kO P$I׷S YG{Pv݃vdbb<ϹXh'|2ozӛ!)E/bjjpnƒv" CRxb7˹䢋@)+@1h:W:x*>Oh >}b(`-A%+RT(@ۜx)QFofmK8*u8e/#r=D 'KJR(C[!2r=D YNXJD+DZ "/ T(r A$y^68p?5W^`8=}/8GQͿsHx͟BJ(Zûs_W>˛pbF #8e a&TRAaw/}S\uU,h@a RT,h̷[mi*כPjs-Z< qʪ&& Y6" }xk^].ܾ\16\veBqHu(h:R߀,ωCJ7ЗeNug0ZcXYhcJ"6o~IӴQZ355E~9}Q݌~-4A9=8N|~l(nEQ #PlذD_o:GvA-,ZΖ8[nP/D333z=W׌ijeDQ/~ ?x0$"<;,6mbѢEz=}TMN'IҤ|[o}ݗtڬZ7LU=6m.oxRt\&&&8C $IZ7W%_y; *ІVdvF!6hm)KC!R[J)XXb%AqW`RhFFxZ?s1wc[٢oT9!7pO}S{ٰa7po$lEQxb~aN9唦2 t:ޟ' }u5[J͛j*_v\qXiG<)˒,;?Ca=`\ J֭㤓NBAMT9֭`gƍ\s5M)R`w/_yĔJ#,)qL &t:z'EQsoӃ XjA{Z6PUEm+OZV_}}>1/)%i׿yWqǢc /$".V^MYZ;hD?#{.sN:瘝9GF<9sx?>K.EW\D8yK_~ҥKy9餓㢋.nOO=PE0DIcZ[[U^򞇝v`ߎT5Tkb ag>:F&RI&ca3a6sYoMYOKA$ͬx=XG^Y㣏u\Wee8皨q|[^R`-QGzseB C&ͨerBz!ϑ$ AP5Bp8䀽!ͤ#$Ipҿy.-q"`8255Րdg8NhH&:GYm0{S ϋjdffٶiM6tRvz{ֻ2$Imra3 RjzfCy{;ӴDj̒.uO8⋹5(馛ȲU`ժU|ȳ&fie*gpj˄0u[.-qhgMچ\R3E,ǚLkLό9ɪV4ǣ:lkFCZx#zC: *URMu&aTU@)p7 8G3~4c͚5΢+WViseQjˊ+XrLQ8qC-qhd_Bx!m8z;~\x8fŊuQWDVGee%2J3 \}iVpB(iD1Hsjy#kGK-ZئP8rMcjtu:~@qAJXp/]`qX]EZgffJqhbBV=I`u|G#Rhᨔ}aff:6e^ T)S:5#v0ƐgoaHV=9,˖-cʕ'}K`@ƨзhZtM\SSqu%-ZlS DgC3O(tD'ivlܸ>k|^qUc N Rb߾ YDQzerqz衼QJ135LRgA*~S@:׼dG٢vS4 @#zaY6b4;`?umJ/es[/`u(]Z#-ֹT,mzL;IqG͑GBr5>Ju9 &=hDwxYxqUŶGi6lb;8/~s $P lA$"E)f}{Eu&4 󦁜s~dS)gEP(lک0dff Ad>O@s>A fzvs?y!2,AQ8[SJfggEA'q6# ,%)sn)%=$2ugF9$B(ߺeut,QpYg$P,NC=Y8fbbpؐUĻumd٢œDyG}>j*8սwη Ĕ=uokce$TUWqhvNpڛky͟x^r|[u iLmo!U[q7'5ޢEV! 2P#Y A5p>!݌J'O-0%R*?7XGP -Z<9hS8OdX~~R7R~Nhă>Lӓ)`vhN R=fߎKapܱ/5Wt*$8'Y8[hQm=;iټy36lhM;;{yT"(J=&[{S#|.KW9V jE[Jaڵ.xs=G_eMt3QuY A*m,Tq-ȲI.2ױPM J)F;cKZkzjSQ==Tk.+e8 TjAyu7DF-ZxrUjZzuNZ7$RJz^#(lj%X󷫈W-`Q B תF>ib}]t:`[niDGvq gcW?_ú]-yUoɶUcŘz]mXmGj|">0/ AJ)n=Prsi4/Y;C8G^)^J&&& MĹzj֮],/{XvF%a[cۙ{FK-Z`8vs=yӞFMn}fYMڬeƍ| _`4zj֭[j(&Y?V(?7ۗGK-Z JpJ3R~z֭[},ߚ.lSZS)Du|YEsѨ3pYgevRXl˗/g]vAkLMM1t\Ui{FK-Z( 87d[Tsz8瘚gٲeE;XhQ^͛7)eYz/u*y)ѦhEYi l08ιfN}rruz βnv5dweW8&Ij|[lѢBFʲQobvp8d=d_gֆo|0Q1z^SHhkBBl5-qhѢ0t:MDE6lhZ09;ҥKZ3MS>8SzR~E)ӢE:96m{i, `bbe˖5Z&w"=X*N;4_]߂ng-65ICYowZVO¢(`H(rh7:b8:%ˆ4BG" Qa#A(QQk+:E͒F=Zm-iACQ6 N!Ax@ s"HP<}tm,kY,ˈx=')&1cK5$IwN;pb%1!HpN '5ڔ"Q IEFTf acA`0egCyIGo8f4᥀^< Qjw 'ԅ ?)=VL$I2y: RCsrDyFX*ÒN%cfVa/*pxUSd18DL|Tmg[8ZV )s^<[obѼhJ[E +t$+T3B%),YZJP VsÍWr͏(x>C.oyߢĘ@ J^'QռUR!q *q;Px-W>H!h9J[! o }~~2Nx|#ڗou~+h.%R > l`Yew) PuB# /3'ÞwHpr:/L8u,E>B)A[(;3^g>z nJQ|<WYN?˛O:QSg {h 뙼i0 w XY{}ZmGP!1LG(*/}9-Q$Qe+x?>o~t;iK /=d&s!M[6NHA]!!4y^&],罧7S a%qgTeIA)fc3c<Ȉ15SkX {l4CS]|1?|FBGġD֡!⩔{ '*cXk1M:t  ~>$ &9D0.{>^crq!P!YE" Ui`i%2t5jݗe)Qq ^:HxBUg$ئZgWQ9~r^lz@(G^ཧtyɮ\sz}:qBQ"Q`dy/dTUnC^!\\%M`3x&52f$\*N$|\߲/Y=WPd3 wWL;GG-*cyhI[1i;x(Iq(֑,C aZR* M5I468?> IDATyu7NlbmnnNP Y^")( H) D(g572h' GP(ըHaa0RYFL{ v+VC.)5y^⽠E UU5 ^Nv};Љ㜜o Jַ͉' ȋ I^i%X'pV Us{:| cL鴧(2t$ KdQFi$!I"1TUֺAZ6ynQJ5Yf 7t+Vn㮻b8"—P=y< }8}LP cj}WTA|S- {FÜ$BcL/Wr叾³w\]#@iE,clH&xt]+1Q9-h@ ,K$Ih4׃BӦnv)tX[e#DyF!pk {O&6,IZy΁_~&ZN:@' {4Ei%]ff7!n gA%6c5d)E nq C4Eh4b-`-D F3V GyYrB" nqNlO/(ja=Xuۼc^v؁P #<,dwp,=bnn/}*gB 㞻fnn!5%SSS@7Ɖ@D[muZU~>Jў~rWJyOQq@H iBe-yUvɆsDQ?pш$i+5Jco{vuWͶtO8sg.ƺOJ$I(yMb5qs0}1qd`@O]PURJوS eAx銏2?~i8S>b{{cA " < H 1B#f.%HxsCբo|7gm%K'Q0;8mc%/ xx=q49Apjܹ[s~IOc}f}X#so|3}:Nh4XX:n@Vڂ,-v)KY|yko_*ZE@ Y\@ Zh-qaj'672iJ0US#v[pQWt℡ Z[Ӕ4%jȊ><+ڽ)K8a4TQ#Zu q$ D N+y7ē-\W8s[ kCz֐p5m@HTZ TPtBIiLJb)Sxg5 " •hV8PPPQJXT0z qʡu`f'P>ƹc4!cahTZ1@ 3cY9ZkLipaۣ Qk7g,BQT"-Y Ou@ QZ-\ɋ0ǁbZKh>q'( 1Dp.GExի|Xf *<$I^kUUDQDH4M)CH}pHq#b0 ("$ѐ*(mŢE re…x7tsy oʲ$"k@xh1i7:3Ǎ1u 34֚h>1".>`Zsk袋{(f4bU>g<{ヒ~(8IGJZ3ֲgvj6eY"<ϱ- kZn * qGr +8}g{sؿAUUlhffg5: Ȳ{{,e6N1UEe͚5կk_9ʲ$MS 5lIza!GiYkM(PJ `-N'LҫJ"B8b^ce#s,JIbeRʰSG Clk3( e@S"'37Ki߃wC&֬Z)+F>lѰOYdDZ"'5aglV`kJ"CHIwm<Ø 'NsNC>c^|?]1LOOԧ>[V'>΁=}䣜{<[oSNAJ֚,9蠃xֳvj2,2Xc" O;CkE.®1%*³rĉ9|8 yJh')ޙ:$0eA>Jb9Hͯ o UiTENgAI 9fV?I6P;Φ?©%(>ϓ$ ~?,H 7x#\podUUQ%+W _QRlpDYK.['jBVK/?? )|sq~K&D1fYk> 4I XGF#!GXj~Iq̯k.R{ KXhy3==}8o6~+V9|p׉o)`86Oy~qk>`zlƜ|\| j;0Ɛe-j~~(ifWfПmZ"nt4'6,sh{m,'M"8/F皀<RH[(zJ!mIڝ!=ɘ^㓟ΛtK9^|c EQ1NU+>xHb5%,?ǍׯMoz#^xt5I1%v }8opi5aRz;ZbL8,˸8csk@=W2{| +f書袏|rʲd% X4 SO9?O~7螛+韸뮻8䓱rgrQGi wog? XLOO3_uk=a60=g!Jfva\tEM[8pO%N I !(35;s9p=t3N eI&MS~W n&c]r񟟻W2Oِ3x/s?FLYF1J8}]w'?q{_G\=NO5 n~pss%}(X[\–[nIe8(`8t&eY… ɲ qnYh;C;zuq뭷b5y !*QsCŘTq:`\ļzz衼 '@jmK./g4-]`M7jE wtʕpoOo6GuNa;N;4|1C9ZNwdvcM6/|!~g<lš7 I~0|4ĖU6Gsss!T~!ڲ܌p$O)tkH]'m8^Jtwr2h!k=9vۧ{^W𒗼_!LUAh4B@q7eQ-[O$<[pS)ݽ3-\8:J9WG$,ZƔ,\puU3}.Ǹ(zJI[ر ¢(f8=RUusvf5z(ǿX^җr饗r!Pn|;! ?g{O<4(4MSv[^ワ|)]Fj"vG\ed>?H)y{ӊ#LYwlpnt7WxvϤ&n ÷/_HxҥKAB)Tgm/W\w{{fTUA/})Hk Vjv~(h-c BFAqLۡ]o$acٲe1."."yfk/~>!yYt:4TC1Z'EkvZo~ʗ1,\!/V[2 &MS:TZ7QDǯeB\8[zRJ.R<6l2sPXK&q>>,_|?9y<:qd0U-7r ' #(+(+ 2 P2DquAfYgŹ@C+L@d _)LEi uf`BGCSGC ~2Ve9qgȑG#8%K41aaŊ/%\q2RjnbWbmЮ)B ÄN^PUx]{y<1NFBEQ4')5!}ժUt:!m|+pyprcz_;yffw: c,#s8ԧ /}6tS6dS;LM-K_*zS[/;qYv뮻(- 8榛n)Oy qqӂO}׾,XOrsWAV/>TKm?8Ҝ{{?oۜq ]/cN>dp"iwޙcwq=W]uqssW/~1x ]tN~xUX$I)eY|瓟$_W9cl8#_7#ߌz 9S~%_W^y5yeXN:ΕW^|\s;Ui T~%i{r@Vǰ\) 6\_@-?4!?MM .3$| ޺ QlU;l ]S>Xd1{'\xᅼ&r z׻hR|</|1QCe)ʌ$ yVl(NU#p|s%ix_7u)Wf㳟,_4RvmcjXz5l I8,c-G:Ԗ Z.c`W=[ouu~9糍seks3 =*$4i}j5EY3`Qg8}0ƍ%V PBT$dN8'yV]v[^{aB|r$ _J:7Q9gqHiqǿOW~v͋_2~_q$iT{ʢ/~1߾[䣌뮻e˖TnN;DUU|_%/FWWυ^gE__ _cH+Vp+_.VCYk%"$4q:Xiu{l[բԩ.BI(6aNycGLMM1sCh$Iq9PJ UU) zډz 9Cաߟezz!Yp Hɝwɦm N+QAӥ?7xdE#)3}$mA*IYU!򜏈*/])UVyy$Yҫ2 0E\LY"j+QB2H- QHI177ǂ kN9XH*Ś:I6w\l*+KEg,q8qjmN'u$ eYwr I/D=5|ǚ@_F{py)&E nTUEۭd!XdLiKVDIqcR @׊,u`lUU!&"JcRRC+nW%Ɔ\ڃM!JbO`Hq IDATB2#Ywދh4Nι7.`)\IմD(85N7O߬\WaܿP_j .F$Q ^oA ,=|iJZKjZ6EQ4_eI|\I9o RfʕLO-r#< iҤuuϝNvxTby1֬Zyht:M5(_4:h;Q0<4=i BY͵\jI6ƸڝH*%%=U!$W1 $ :RJl*ytzfW48mS1Ee)*K;E1Tzqd=EQE(4iaM?f{5)HyQyv(Pmycjj*(`e Diʂ6KI^JN9Tv: $6:IeEgj /q܀ʸT;SDiN4JJ;ڝN8ܥ`@ VPh"o5S'H9FN8S5'@Փ|ڸ "]DS.UwϤq3!DZn"tuТDK)ifo5]h]$R&woR !DِnK>G8"J(&"!JziW!wbe s`4nB077Goz*T"Xۜ tl/JXKw9xʹZؘf=I4̌fB)QFf|ύ#4Zf1B(|Do1pV9 |jػLXI{(ˋ %cdI+Hh13Ng* А$$lqң݁Uk5;zóy@Mvqx :·<*4e9Ja#iDJ STx"+ mG?1?h"#ykcq^,Kd6Ȍ9^8 c2G(xĩf zӫ:'sbh-Ɋ o%qG G1ؽzfuǁv-ߊ[[DJc!Og ш,e/{Y3QZ<_GEXs>I,Y/JS5#8iH2TE:qvY>%0u Olb,$q$ʄ'n ?R}2E=G~;nf='/vV8d ԛuK]D*FPaS7鰦`75LB<<+*K'03X8 hOn38$Ie]~^, Roi2 2P0ƼR>(xOlbsύhwfj v:LH )~rj[~3&qQa*K; 6b0;/ېKtI!o8$]Pdwv3gNQ;*cv迵)5$UyE+6?snӟDX C4& HBYu&=i MiawQ uEM"h]-AEkx̀E8_q 'ю{~ z:fB`*5a2!Jn\?t/eJY?UYHG۲;|[߲vdQPZ߱[:r9(#Q2"rtr%|S rđO/n:5s܅c "PUͰ9F^0Ъ=w )]q&L+]VX &ޙAuĂ}C%N\Py!!d?3$MSk'pzZ{L2t !mD]CᡬEg֍|ףaP]}}b{dc@B)nW2t(28Cxpӏ|m#b9&[k*8GbqbGlxSdFm*c͚5HGA|#5Cacיvjj\G5aQ9N Icovew7\{\uU89v;1rsV7RKA+Nus&{VtfjƄ?1t{-fggNa1#+dp E Y;<|Wqox|?_W+;N*QuϪ)ѢBXotZ=57nht1w߹2H 6( R>Wc q+3GÉ"vmacEH)YvuWLa7 .*qQDnEa:a`_/Cv!Ɖ={l&)aPqaNP}SAP4ٔ_lqƱY}rΑ%Y-?;0r\nuDeWc;œHWήTeH[QD⎫L-b1菘"2ň^o* 8Ƹ b%tRT9RyđGFiBbf*z^U5n&6OͱrRT S?i+ GN;|l9rʔHp( 6Z$JgƁ4-գ4%wNE)+n)I$fu;ڨsB_c  Ii5UyAd|K7+/G%]c8οq*%fȪUqn;vmWdh 1HkR8@G EY)Mn,BGFlyfbl5Xft]\eC|],#<^CYF)pHW D1{n_?q'4 !dhզOD >we7ے<~ٮl5H#tnGxqxQ~~~/vۧ$wuWnSz#W>ֲ\9jx8g;) bJFxdZQ(zA6};3h^NOl^[sMU=zݾ|оXCI^,$)8@Txǹlu E|`TLqA :LÀ)biD^ ],_'>i<ės?OR%$Z ΪvJBwlu0Ą9 7**lr9's#S8Q!5vtf(:bg%Dd-4RAg%rZ띫j N) "qB>E;E>@|Q 1z a NcݻP;Q B}Ujڽ뭪}o4<ƣ3z݂$cǎyaҥZ-EqйMä dR ی4?C uȊ}<=IVCJɉ'xIg,[\grr9g#%I>,Ccy^"*|7cʕH"Rz pNb%mLZ-0x^oi"M9[OC 8zfS$ , Jik.֮ Xf-#q/Et-FHc3ydEln}{e"AEE11͙K#BpI,|rN>CӷK"؁1C.C l^,ͿZ0RlذGN3! C'"΢uk7d(we<)HUf( bbҞ- ɉG@)(I8FEXJǎ#%X11qk*ݬqϏ'$֔Y^7W 8]ί ;L9[AsMJIi4y^{߹s'/-[ʲRFA+J"K/'IVlΰqz|c{dYRzxA V>;vdg\Fãׂg"-HUΞBTL`dk0X f` S!qSS'/0uakSR<ӎwYy(l S'"tY#K顤OY&ÓUo/GFFk^fOe^ ۯ dC T Ob!o-RJaڒRkjoܹd-fYt]v`lܸXZ",ʇNEE(KKi<sK=9C*!j`! O|ܠyKڼYx1]geETT4M9wţ>'l߾W=GL3wfAH3<lܸ gq}_y" %s1w~c@ Rt:=O“N !Xd 8 IpdR)[NWw!;1p>mIiJ. )^,O\,,IӔZ4i5S\k^0E7=!}I% @"=ýK$M[1Z QLLl9}fkD G sٷ0f0; j-nCNעtC CB|/@;o>/6^{ N.ФiJ$N33$aL8yr;~n@eOE #JSR\8ED>V B\e~_O?s~FFfp<?[#.:l~;O.'gt!g6X-LI'ݠ Ν M{~8?6r|'J^)$N BT0Yt.[سnFR h,Jyxՙc7*GA#ʉ&+T`M՞Ac(jUSX#5SZC=Sd^RTq)e $MS(<R_svc*Q\TH1 _=*6lXX`A (2GMC,>䜟ر<0[l×?,`Ud8P.57Z"/Ѧ4:vձ!Pשj`0klĭ_>}y y(HTJ澝52;;iwC mE`Yq{PPQ2dvA!G:qc EbGž|-y3$Gٹsy5s󑲁.%ݎf!r\y"D0::eKVvsff3;qPx*@HKZQehk (e*QʕVO%]( au! a9yQxjO#p䡓DaU׭Fٿ/8#aH(|R% 6&̋ q'KC<]gYwڍq'N0od\sBzYi5[ =2]h[կ| Xl WayN8&AWQ*˒RZ.-Z4(!`=t } qF3::ytSwC / |#NҥKX# #[9rK~z#Xi[?sgC+!BeYxA\Z N>| 7'!L$YA%yVrqi/M{ =8)t@^;G_ȣ^XhJUvF32C;wkƓzj\JZPT?3C / ") ZEQǣ-0U9(3""vڇa=I-`w Ff/4[x2fffskYn3:tfI ;ZA,\wiڥ*wEk,]P㽄.18 ^8'|| iC N#.V}QBmEy((˒uk3>1iPJ-EN;06e5liY.9p`i:RϓxJM3<)Fqa^}Z-dPOuvbʶDX{ H!y 5 TGzʴ~h/y%{亮 rhMix ,!i!iԞC:ULm4q VE+ٺm3i֡,s.<$ Kੀ^2,8a̲a:z[0!젯XـĞZshϦL>&v%Mj |?@%.yZAi^ZPyWƈ~0{` RQVCDk'qLKHPzA6YF$dc#ϧ,Kr IAc`ˀD^JĮWl4֊I\02\Uڱ|ߧr)eY](jym܀0Mm'oa`iH2p3~pPV^,!pf`:DfNl6׾^ExyΌHbt%/z4Rعky9s<6nL[chϳG՘iRw V;(Pi gEz >Rxpz\h)Kq366$ e4Kyģ\uUOpwK/^Oߛ=N!ڸ EY`' 㲕ײvZp5so_ASdYN(r <8s<Q㪫ވ R0\yBdy^ cUvGQ36P UŊi-NlzIyv8աe>|?ѿկ~5/x 8voݷ3/9Vp#>L?^rݰ٩iw ^??o6,16 j%yyIԤi()QB2ZonxK_/PٷWz+}6/_Wie9(–H)Zy(7VeKk^?~c4IFFcC|nt軬g$]Y LuΝ;)L{Z-n$ I{EE[ތK֔&'+:dYKIdYk<87Yq+/hVkBqqzY\8:zZm, yz-#!OT|W]<ehι3gyƜرwXnI0=ow\j#0įÅlٲ]>- V~ .c[[8hgs]wl2^Wr]wry~~vk0uG;OY!t)q;bbd"neWGX(XIHḰ(7/_A>]{vTx6BuM黡R/9pV*̯SXNyECe EXP5 hEa>,ϱRD!IEKڃ>8j@ѠldN8PJ^W|_~=a}f#DK~\y54MgyK_ʇ>~7Pm|sY?[1OBw8sw5lۺi7_y|#ᚫe/{p'n畯x[lBxM7'nf~^W|y{8vz+=kWqmt[m>Oq^3 RbVarvyΟ?T[زe G9)0! i]neli+ 'TUVc•_q/o\XlnX"= x @0R!%#d}Gcԧ>1f)>(wqVeEi8snVD1B- ?g BHΪCkc |ӟ裏f;ƠZ@s?'o=8 /)_zh@L`'jO8r@ 4W\GQ YeWڒ H ^s]ʕ+Хff$M j⩀S31q(Yx1cse;](=;6nU8EIR36oc\]J̝ 8'LNNl6tXkvd-DoT9ys{;9~8wuqS?S!8|6l֭7w=vg0>ӧyǶ7Osŵ0 Xb%o[n˗x)wA.?x/~??];w?sO^`eq򕌌~VcOqi^_W„_峟<_d_u? 皳|sa~5INѸ NrC }(WN@ŵ?+$Uh4!0 _N? njC+~PbCDQDt[UV_YE* IrK!kd SSS_vgv@Sq~>yA< g˦gg'A)/ΣɲP 8w,-[nbCX?ݙ! <Q,HV3%ُ.3/U˸uc%vpϓ(O`K9p|,)}jKb$tJ?]M݇ܚt:7 V!/d<GN1NH(N(-άS$Jyμy󘜞!*tij1Ei4Oa kN!K[ZNEyYjGmʲAO$NFFף(shR)4Movbbi/Uo`%2VE Y4d]]FcS|uV]$v4 %A(Hc.Eq܍ٜ4;wdmX##|JIxR@b8=~i6˗/w^5)Jn<9(PgiKtwIr뭷RhGk5fgg C( h:5Єa8!mNEt:-8&K1vS6mī^jPdYF|?`ld-7 rlhm501Knp<ϧ0!-rvυ@Vk.YiKdsRh r tMo@i souBB ? NKpܒ0B)Ņ)nObm}8r9YM O8^tpVsiػw/۷okf…qp @b )< ;v q8ř3grW,q21)= 1 rU o%{Zed9sFF*SGj˜Q,'v$ |tQ%I)J͞蒲]scQRX0"O3CHKY`J0«43e^5@yy~E$e IDATYuڎ. oyiڌjDQDzbz"OG8m#Rx~T>i^[` A&  }\R[ 3RP <&MK풦]7wŋϸ33MŇx;w~c{0-oM~?x+/Ç7s4j y#";9}S{^֮]gRn 1֔ &^ R  !Xx) ͛Ȉ?}BϧlbH&NBivNGZi$x/Sj~/|l޼[o_yY?[Yb_5[οtsx󞇒>Y ^/~/AII=K-Iػw//<嵯E_r_|5cú8}soyssmxi Ҋ;b<8=?( P¸^:W!YV`lܲnY?c'1>>΋_NzyA`-yAHY!ݤѐD(Y~2}4+,3%??3(ar4, 1P-khN3gZ-NQD1V|_BXC,?ё:#EQn Sm&^Wp7׿*SxB?B?09.R5oѩ+%n9?#k]XTC4_)zYyNĵ@)s.|)@E?a@Vl 4)mI')q- YRy+x  [=}Oq.]D" $iIi0 ػw/G%VbΘ+/W_Ü9sfƉ'xѓ\.y &/L|rJ=BJ,5JjV JSTnEtq /F^^C^hXh ?jt{(/L?uk֭'i)4Y=ieeq]vCKK{q Avt=8Ӎ'e[ OT! }BI7گe豇9I~cVٚ"/3'nF>yJ;g?]In1}.I H'@d` 9!qv (!R8"% Nw®Do)?_gl=7pR& ;Z0Ƭ3 z'^yTUKR3G7:=[cr+χjQ9Nyb14X8Wo{-D{sJu+Nqk{ ,} 3Zw{= ?Z:M! ZczV !Z6ˑuw[C4(W8S#y5q=@s<3:Mamc]U']G\_|*9h݂ulckJ)PcRHdyHFRHc y'-Rp!1ZCZ|w!@^PZ Wm4!*v]!X|~(~;ZROF,mߎ|g EQd?Ofx'w8a_j5cƌ瞋[n(9OR *&L.ęg<8(J+{ 1B׿5ƌ'J)UPc0az0nRXgءyrƻ>CcθՃ4\}wEQtmۯPD<3{yctAZ tg?!c\|sT8_Tv|x:ٟy~4Fn5l@H "¦lqW͋iX\#@?X~0rRZ4"TJh֕su*m4Zi IH$:f]&vN'ĉHXW_}uHZwulfyZf$pwz^fY[Z0 ,h68G2 HFAyPh(Biӎ=x:@x}'UZ 86"}赱T+u^kKAs @t!ÆP8A^+E2 ֣ -!+B(Q> !{Z e!<(J%V6ڂR"PڎJ c ꃅ 6 ڍ\cx V a` 7f\ven;x1{1vص`R89* pnA|r$ 4;;< q;>0fg?É')%n5 fjw}7̙ "2lg?JR"/r<8묳pwc``[m~O\ z_B+RFÏ>ҵOq8?N1*j͉3SV9Z=HD}oF`Uu޳8ѡ|ljKAiQ w}3.rء=hI3C`(iI3Q3m!K.Vџ? '.\ :Y뼾*¶u2D-FҒ7R z?r5R ߿=%1ݶvj6c$ZbyZYJn%0=K{ H8cyje)i2L[ton&0Pac܋.$G$"I?@W/wHw404HnmL!CD߹e+ӂ;~G|3b2:cZ1J]jVP ۀu pgEN*@N;}"ʊN>k];4"7/ 3׏>l㊒<4"1𻭶B+:K_$IbCeG{4|^__"z0>ts$Az镗UhOM`3On{NyW4ZЁ9vl>WjW'닩S'0:J˯B:H8yfmnC`>dH{l$}wBm#ʂnᮋs)!<5R +}fi>Vip{"KDh5m9Nzd 8S(WU1[)%@J"q1`[[ia+*{Y4 pƱd'?V d vyg@___? (aբFoo/ZrTQᨣFmF>p! /@o~c8F&l,ϰ[{O1P9$q~| 7܀]Fƅ];suk뮻Å^k?7~84矏f .va2HmRoK, m9w\[nM#WN>d00mڴpm/"4 hnAJtd.03F$#<R'_8SQvuWL:q~zҥJᗿ%N9dy]wS?1XI%o )c/Iº 8 w)A-+JBynV :Ϊ*ZVR2H[=cs L7V;̅@oO/ U`…h6@h,\2-Zg%qADqlڮO$mpʟ‚9:FsNWQx(+td\c@ec0ZC JK/cÅ~퇑#G~+ ϟ2` 7R Jvy]w8C_4dpI'ϰmr뭷tM7!˲dC=QFa=ǃ>}{kw$ n;@~=qW; @ .;O 7܀QF!I"wL#̂ Rf`, c Jf3EVAيcFk*ÇJVjVJ YzvP 03N$D륄v^R Qfg̘ޠm\Xu*;5 I^"@Pjc7UW]iӦ!s\r%9;wM*_=x6K1޲"|YE2kZD!&DP3\.>5!I\F*Xp J)K!vd۷Gpn;&zzjs夀N:wf͂y^ I"iJJ'L뮻Gu&8__|{7^~eV&M_; =^}SLc='kio{[:0eY"*h6S$I"dY*E!e Z"(e"RQtZ Bql8ViLP !6t -Z`Ȍ޸r%;3|yP@DTR j5ez=裏Fل!V"M[;"zvMs^ؤ cPUPPJZMhgj5cvT cPUo""# ҥKA 74Mh1qD(e{G),^߾y*tC{c~駟VdY1r!a-wߍÏ8\y^{ BN[[oŴO|p˗:w[T:h4ፓEQ^j˗/C!̛7/X)8ؠSMcSU;؎tE^}љޞ^dy*e>o<{߽Ay#.fUڙ&]!Zϒ!GlZ Dh!v$8ꨣpWcĈ΋` 7u#,(4Tv9MsTk5!lר׫SU~vqt+Q\gp1ǠVDx'90|x}08c1bH)PĘ9sMq3h4Rr\~Xp!88cԐ1cBR#?yn;7R4^z)֨T=z4 R $IaD.8o߲Pz/O;H8ig{BS|`AX]O JJ:~cO4`?_(V^8V_ h=voN*{&&x4u y?&|do Fo(.?5/41.)z} ED{:B6k=ppYL9.;99stP7CQ60H3fCf)y~|DSbxꩧpwZٴ 6(ZEqwc|OO.]߉'b;U wqgv\O4yY@ ٳbe80k֬@uzV&z!2@dY8j`+w< "n1c:裁3=z4ƍ{ w-\޾>q83#Hbw)Sp뭷'@$6mΝO#,#%}& tBaIQ q,`2a8lA hݞEzMu6ܩB< M1^G퐻 @>J#D-_ii3(y>|9*e\Pm?~!j}2UW]OydR<8p鷿Ec.cwe'v~&~s]]gMWkLۯ:;oyp[!yt^Jus >_lniny/faQ#ӏ"Fi<(vaK{P yQvSwMJ9^x=;#."l{ IDATjRbɒ%;1w\L6 _WKqF%޷6_)'Ï+ Ol-k/s1NJ-J[_($ixͳ)Bpکe6TiJs^J{o[DB Ta=uPHS 24d8h6lKJEQ(cdX^E"h;? RXJX*{`Pg;v%êN6 qzvlt$pnouX=|jl^$/X)o*) pSUp;|"N耖 *Yc4κ u3@tñqe]ܭ$xk2oP}k?khW=`*w:Ҏ- 3HP9V  IK;0tޞ ( Rn-$5g: \ x1p4spYM ,`\ZP)ZIb@&DkL{ WQJ[;z| `} 'B"h&Bh nHȒ-wpNŕmd hxUcֳ5`RpeX:؀Uhf/Dq(msc#dLwޯXױN]c_풭a-c=` 3|` q>B1P4{y GA  d3 W?OEv527^n=2do[*/lSYw~ 8nkEV&|kbJwn_(ED"mtd2BnO>,TE<#zdCW)99ޤa㕟}OvX ͔k9`}Bk\V#K!BF{& m8a$ugbɛ C|G;[;ѩ(T"d ōR"/lq,lf]ea#K)0\%o4A$U >4 Ĭyib.v@0to6`Y'{1wrͫsیU@+h;;/"i^Gj![#9[mC0#$akO!kځ, @%nkKs;N(۔`Ͻ( T*14P%y;XzδC{hr-F'H(T(O-)Nܯ1* z%>M,ޭ6{z7(4S8d1V44xiL=(#%)E $(?֮mo]]!۴ Xju} ݼm"h?YưJ zbL6tBI(Mq"'VZ0A$@dޞp u^:yY{Ac'"Th: ׷D9M-IJf@쭗•^Ν|2 ={%1j{ꂢ(PUj8LW4 ^EaGyF3sF~*EW2X6c `.`bl=\a6҄Vtj sz@lA2}VXUUіL."'(3V`VPǀ3qYwvז?e9V}\kyCFZ"h EjB@Uk+ `*I -J",odh Ͼ۾Wm144JLyap%c)%*YVoƦnAm0qDı)~k1( и;رczΞ=Ǐj:u9̲w$OpnW^y/2Ǝ )Ҵy睇$I000Ç#MmNwމxqS6`un7Q0i #nk_*yn%WDe˖W_ŋcҤI?:8h⥗^’%K.A0aՑv^{ &MSO=W^y7t^yg}'\a4pM7aذaG>5^ R@;fߖaPk:@n ak~(l8h" c  aɊ-vCgP0.¸$ NA?fXC}KAp*`HdcU6x N ɭ@tcٚW(b(Ȫ;8)VXFMAA੿V5/$7` 3.Z8ό'|0F0FSO~M=̧j % `6\J8( q,po̙3$ rıN?НP DX(.mejmbaˇB[{HRqDCg~qJ[χl$*fXh1>}:ok[:s`qG'F}߹`ĩ3Omo#P8s9ؖ1Pzǧ>),\0?kY^mǫ17Rk Bׁ4CljP 㲘 |]^O+⎀X֥^Z`clAR΃&{Cx`}R$b6Y@K&"n;ۡ}0%8(um6 f>q4䮐1,P O<<~uSc QR ALGy$~CEj4/BJQFW&MN: Q3>1̛70i$|E yj%A5˖-#!D{qg7l`V=V8A6 Mւk-VmԽLQW`q*81&LaycDѰi̝BC)F6l 4:q{;;8l8V+0K/-9h-pR@.+ @X4+lql\k" &Mph`C@Ftfd"4 )[@' u^NO/UP4Ǹ}}yNyazϨc%k2ws! #:Pdx$ >c1fpp6J:rU<_@Q$||#!x /P63"\uU8#jB$N;^xp9`ܹx71f+pqG+͐$1(W\<?p8xEa 6Ç8==5\}s~dMp衇BkBᩧ;FOw74ZkL2#F@?:( 08B?.2l&dMӟJ)vmfP0rH,XEpY1g ¹瞋?Y(/&Ln!1mAVgo^wCjӧ^c7g(bh6g>6;vsiQOGRAʜ;nΝN; /$R"*e%\D^dpi3p__KۉڹvqGwz{{}IfyO9%xv Yx?:VAH L<=4G=EQ`vp]O/=X;?!h,y3`c.PpUT  cua]M0ƥh[ݫ{{NFdFU`t!G}3g믿}s+`Ln Æ 2) *U$I䨑z}N~DD>}:|IXb͛T"[-NOoe˖'č7ވo~3(xb831ZA4>Mj. tNϟè 6Cp4s~>`J&Rw}O >}}}6m^zU-q &\뮻{j!Ѧ$I MR3 8ߋf3o'|Yq7b̘1ժAjN;΁FC9W]u#AEL}c!,` |wD3AA:fN`Yc8p$Ub&rZ$2 `q衇 .R {h"cxǰnرc] iOt^?[M?ƢE@TΙ3o9>򑏠QMhKrZf)+'?I|߇3gGoFN @W8H^A^i<ϻ|T !PWŗ/=!(E0uT4-V&,YSN9?jw~AJxCCC IDATk0nܸ(JcZ 8C-X20Qp=yް:][Vv!3 rϯf=Z Cpx=4nJEv[1k㴱qaff+[ήԉrPL4nzn (=ȝ/ﻔܥ>A`3#QR"7 dBGFȳ L hmU-x≨V\y啘2e chRj5k)1N;d=j5<اv>l9EQ~' I]w&O>QaM6/~ |DžaPD7x6\a„ kv4m4FZ-xM^o!81f;$T` 0;MCkAW_K.'|26|s\p4i&F4ީF30ưd?mFj<oOOFojZxc=ĉK/_|wa}u"Bk]U[j*9s1!1aw܁Ne}i}ZZ1@7npj;w.6hPƧ7?+1j>`lWG)h lxWP ժT)%lpN@X~<"Z-z8}ctIUKj9ܕ!c(% P Jh6fLj*HAw*1̩5:Ed##CnlzS^RgUI(s7 7f(vvj9v 2VA&(-uft$GX%O/Dkbn ;Gf2|kPq(n}56e0K7 e98la HAȔ^.q-ࢋ. 1cǎŬY(>2뉴Z!K/K.FYf1?B`6)&M=T*>O?>z׮jMnSͫv8ZF#3344DıYj7I1+Wdv&QrgV!ͨZ_=؃=ܓm;NƈNS}NIh4&m8obŊUAwՂ[:44q f867I2N;4>o~}c 20$0 vVO旿%O=8Jуod7'ϋʃA'%ͦsՌ"ͬ<lh">XC]\-iV U=c=x!~pKxp} L TNTiT tyPjdy-Nh-,Nک;u{,tBNœ&%KVҠZn^h?x&&&㌫}{\tE\zl8AF^nUW7qwy~a9nF,X0/~~̪gv?N8ד$ / '@կ~[n!s ;^Bf@4:i[ K_vZk7s_෿-> /7pC 'd@capp?d:sF3f"/xe&crRҎH~4VaR$1N t[ĸLD1!cJ~4P -Je)sQ0w Sh41Їa*H \/*=)Jrrh E;Ɖw/<O=(BٌBJgH"(2Cqr V?bbRxH|rjȴr#cx'U.R{(| i+p0E\s4cQ-O/~\8xҥn>M~]B̏8< "M\.gC̅膆+%ڟ}̟?wޙf/̝; p\ve,[zr'2|Xp ';Vvl2:'+`hh}ݗVObppA/{T)U~Ḯ@Ň?ava-͌yŋ>}Zk=\,X… i6|+_a|[btt9s0w\0&; /}6XAFE3_fɒ%U_/vޙa fYŠ3;M:,/F>Їw}fm5krꩧy-_-B˸ߧj;fuug+!@Yι+VШժЂN?[o]~tIDa 'W^y\탊%sWpHaH^VbuW Vzd zmw?A jFKzL"p#T#w/wQh/*0I9##"# Aps'zj-)S$T;ܵZ-۱~.v5wwv{J5,+}v':cV/;!w4%=GJMdy^aHv}JOݸ"j~ TpA8OQ)۵]i:m 0RK++rI֚ 6V[111QB<)|v;}z2)m[T?pKXH˝ 3< >4^w]dc6y]d4NPd 27@9Rԃ47 Ҹk]FN`=" AN*V<='z0C RDQ)ɲW G*Cgp.%RDA)H Wf5#HeF. gOiλY2cQͰM%1h F Qȥ$ȌGu )hϣ(]-8CQqdEv^C A/5E^Ȳ|΃TR]jeFe -lkPve0HkB굀v!D_EQ7ءQ~IUh5ktfըƛgN$10t=*ϩvo.# C.hT#{!2ENhh4 33 VbnzFQJNmU8+Uťס1Ъf4}BۭCBWF;@+IӼlKKZ-`b¼n/-|tH] $,.Q5zA]^ܬo5STJFD'&)ݩn;:&'nZӬ|?$IQȩ1WG|]aSf%/EBp\4] 8DxɜLxLLt۸u>C{=yv^yvi'ZC04_M^6œ9shOlbbb00WF-d뭷z$Eg>ksb0ˮXAЙgiŒ!f̘"6*_~۽<P Ϛ0:xh<^3Ѧbٸ<BdӆΟF=q1<2< IXjrF&l9Z7Sqⱗy!j1}t&_!z  E^ BNk϶<^@8#4u&چQ vW]ǯGqfqv񤵦EDQDVK ZXGGxaec/z:,-(&:QwZTv> 0VV,3zcNz-KͤU'I&9aʫJ8Axf*5|P™W1C `O$!Db`zRA%?_yi^uMzJ>P׫l-|vi9qNC26hz=20_.y˂ڡlVetXaIU'ˬaґ-/4fwHRE^dƐt8 vJFvGבRt;5oTbW$IjQH.W u 3@x =i6SE* nQʈ)޺n84TBI>t"KRLm, isϽo\36Y<]`6ngh#{5ko{;vZ.BKY/WW^_%_XÂvF=<=Ĝq 7Pl|cv~džQ~bd5V6!Z( iD 6tȬ`4220m&nPl+̭)A~y!q^}U.Rd!KJ*pL*YJJ\Wjz\I .e˖ 9ti 0>ѭ$cf% $ID0RLӷj(;eIUnji0IY҇,:ЪӋ3MqL-򫓩?B+p$k(?س>,MN:saȩZ] &Gy$tún7ra԰`XB6nghh 'W8:yK ; 6K\U{,ZD~H$Zy.q&3H3Z:yz D5Si-Tn%Ԕl1f IDATCCMI_ }\.'x "|L&R!zOSk9a[ۯ ? GDk6 o[K˛-!F d ^(*}%3eԈĆɜf艮oK D.2+X!昍㘁V*Ql}V a8)sZ*Ԋ,$yp* ?t:yIEғ+$R6`[z[^ܰK3.Ҏc݊$/(p_!Q"Uv(5<ʣO+AbwP7񳫯'60wepfE0 64%Y.a.Qstb%I G!j)|6ԳqclS%]!RQxx!3fmfyq8ws/ R7dx OEjH՚dRЕ&"hPhPR0M#9Y*2r10=[,U˼2Zկ~mن3f_/}K~)}YԣN:(~^{Eo䮻*=qȦMcxǕW^ܹ[3::vmW-SjQ׾5vaFGGkh4Eŋ!/[oeŊUZCFaȹKZ,|e޼y̞=+i(CCC|+_8gy&g8ꨣ*3_+(e Xx_<4ͦXSN}{?K.^qA/o{ۘ>}:zz=wyXh,\믿 lZ'> sow!oy[@d6㬳"Icyٜs9Wyva ӈ"8>~:vITvYqm1|Z&㪫VFi#JA8Z+00W 1 gp=p)^~PG B2s˭9Е߸q>atM͜1i ̙%isG>wMrK֭[GL&E!^Nlffϥj]-OsxN"ˉ!Q po\wl BKfLƋ/֊aN8[n"o E,MlxEaڌ錍)EP#[,e^(OVۜF3ڎ4q[#G}ds 3iS[oJAF?4͢A &$wgm#3B4dU)-sj8<|DAT-[WNij,rrz巿mZM6q!8[o>ZaRjfϞ 7ܹsyڵk̀B)l2: 8ŋ>}Kt:N= WCfp ,[C9O>FuHӔ%,^})%r _|1^z)ynD4z!6d!`ke`l̤ FYgNS_~9GYd s _|ö|\r%tAg?W_>AUV}M6qqq嗳sO~_W$r)TK(4#ओN[oEk7̛fzEf 8Sh4t]N9d !vZo堃*E\SP dpG~:￟}'|2Qꫯa;0,Y'\}\rX{rE144ġwN8jPGu]w;3< 7n2)|}ma\T"|xfx>$DŽJ eLy1wY-&inO"522s Ff24dffy7h0c|fΙ'B^y}Rl32,OAB t [n=Ab|d&m3&.i8E'ҞRxQGG pl.C6#cZ p NЬI~ xcdyaFiQgkМޠiC' ֚<͑-%t Q9xECVF v\4C1"9dERJ{'5EҨt TtR{a;11Qu-?Ox;{]&@>> V7gyZ$7n|38v?0\y,]889C/~QDOݮ(RRw]~aH)>7 8ftto qx{n^9xFt=+SO=!k֬okCCMz'> vi'Rr!׿'n1^eɒ%$I^Es5r7_Dd\vepWT}WvAZ&F++0wy'{gU(Z륕ufáRx[VV=D5 WMӒ|$I.m㯸 >pv6m+czNmo{vO?4nzq\a0>([m[o5 w܎n)HWmh DžiEMH!+ %Lx.T= čH7$>2|R% 4N@7S$RHA])7wɝNO 'DD ^k#jMf[+!=%HCdO7S$RSXSHtBzR Qk Ip8grBu6t3کDyʭj'9ʭo>'lP8)L%#.u2R BJ;8n`B!@)ȑYF&cbUo ]Do|wLA] w{He ŵpƤ"L3zD%1 O,%K ^!:%Y$=\q%C]3y9c2*|Wk,ti%1Z&+(_f`]v p 0vx.å]1 Dܲ$1K0 qh4"MOɆq] .Wu`VmdxEA& iq5Zg9J@sx' |Xsv8qg8GyYʪ5OsKZ;y'W^sn$s/D]AsmrŕW!Ozk׮#I2j.xROWdǍkVDv@hqGf,c``3gVB[Ʃ(x?/sJ"%vb"RRlF O7()+*Lu sg"4FY &֚? ǣ(Y!qtq|enq0(@wBB3Te=f aciGBhQ9JI~A̳dA- @Kkϕ%10"QENa*& dg=SUfToR"&/=e# CB:gu1hOdarv`R>*J^P^/#c8KlV\||ӟfhp˗o}VP$0 9 .]zfiW_O^qHӌ3fpEqI';~2w=aD׿u3+   aI'Ĝ9s}ݙ64׾ܾOYnI(ŸJ&:q9CR yΣ>[lY5kv{,ZA=X7U̙3K9ilvSz^᜶;i5,\^0{x'i6vNLd}:C=}{ ^8 .'KyqlGFFo8\rafӘg=/)Q(CB=i eF0ɤ=\p.u~ghu:i L8v]jbV401Ѯ{Zڢ(x?@``^1C$IJ1m)~F_g`}+=%6֞Ŭ$0CڋQfftI-(<mj~.Yfso.emRMX0 L09^E)ec&VevS_2\WTNjm’GB U6*zͣ8G&ʞQ)EG3<>rH( L\Kqk4T:YC#45 h %FQP%7]W@V+_[mv8h"ӑlpNJ};O?ʰ#WJ a%l 8O%VK] X5q5})Dɚ,3Ӷ2̙MצwJ51>murTN1u6?}.GpF*;|y\<{3≍,=l,Ȥ?oe0vy޿nw)F*{QU;;uI[ICsNbq[vOf) 0mdW;Ҫh7Mၪ6֓?k;1Vv'_ )R\2+]E]v܎?YdžN7!\vmvA UB媉Cur([zm˟/]WjVЯ ٌ/+;?S07e1}Ѕzs?>;[a/v8f`?Gя~DZ7rW011ʒ%Ks*܆ cLLLp饗O}Zf֭[P1ǡQ)n&n9Io et;O<&  oe'QX ,I^IuUEGH)B )FU1E!.P=,.D"(d\(;U\hU4R)3Rf'dA^F;#YT\d3 YTv?hZM&9.gy-9Hb#%N)Ef Kw_ZMq֮]/\ƍYx1կPJrJf^yӝ]=G 7FV8dy?xס7R&\ɢLv.R9JRH ő:cA5EC8O.+%/bwz7aZ+o_ZZmʊ/~/w+8;&2gp`իVUplngwCJvzF:~k/4EI@Ż9[nZ1㔬~!(z-D+Vs$aHZJJ:Lvau!ǿ>/#{Nעbil[@z޻Ft̛? J ڝǸ?aso2l:w 4ဨ@)3R٢RG1GY8vBej.tӂ?_#c2®гLl] YcV+#P ɊVC"JZPU4/hߝzVK,`!4RN<̉'5\QGſ뿲rJnL /Co?)˖-OGeq{\s5չ.k٦|rnvf)>areK/eN;q饗׾ֻEiNӡ*׵^ˡJQ\wu[#,iμ+鴽Ù4Nb͚5| ˬ\p 浓/@0 |n<{"tMoшeAkSPT0>dU0/Pΰo;EAղHdr5Ej^pcL?61Њ F"Lm2#1J eaI UGL#F&jH0!H[\Ӣqas#wk#$J rs-no ]UYjuI:31Bm 7 1LP* 댁#o#";~_O3C<frr&s$eYTۿ嵯}-UUOO?eÆ Co}~˗뮻zKN;_-wg?Y:9?pVXiūWsG򶷽c91_rMozW_}5+s Gy$7[򂗽e!;o K/4M9s1FpGb qMOL`RxE! E#Ȅv~BF $Kwcc[2c˻ʐD éM4-t@(cu =1 mYlmZggƵP[Pӻ,"l@|GcDSY-Z?~~jBjZUX+!7Z T+} +Nf,׀)描QU-iO{ek׮eɒ%#& vm7zfS0Z[aѢE{9dY{^װ.^ǂ v|(dnV>ϳaq:Әp$uQo( ַP%LN=OxZ^Qv 7|g-Z-N?tE>kֵ5M9888Cԧ>ŗ%gxVfA??)ַ8묳N.kE~zyӛļyHӔO<:%||_o;7x#^z ??gɒ%p"kVlql4or-p9owh'eV`\q< lLSgyijc {QV#t:5[gaŸ";YdeK61f( 63:uOoNcSx_L}b[J<1{ֵE|Q9a3[lZP7Yw6f<& Fړ}ޕ$!RFBJ8DWHHpߠ?7m:NH4?Uwm ,೟,gu{y㓟$K.ejjL2R5O Ec-v{Iem#S"t2ӫ**nnNQqꩧh4B< C fXꞕ͸ISFLPkTP-J ZEq֟ nIG0d$Q̅IA[a>ƌB@iv^4-1F `z}WO3scn w h#տYjbflXv cFYOjYv|GgpQ'L]ހH'W8*G= d(6^1" V[ Ozm% EMZlQX] rkp@ a駟n+aH'(i&14Cf{Zk)iDeZE,":IeYM*䣑X1 ]BQ QaHvi]Cq7 pIEJkwvwQ37S /@Ⲏldǘ2īϙYtQ k1\xPVyQ10Mi;6 A|ge|_npEle2tJiW^g?c/;0n&"#Cnf{ d^g]u%{,ߕw[_>+ h7 H!I#?pF?e|7ZwV g}>GGH)=}Nn̍sdi,)[!B/x9Ҽ d-䭄􋞽bb2H,$F2vQUV%W}Jƚ  'ƸUN hPw 3ϠH:}<_E H"0z-{9Lu{ϭh%k|?y1e凷Uxȹ=w[K(P)æ[yW~ի;6߀W};mT08scnosx4U7>G̋}͔„E/7KR "D8iZl޼a Mcce.mΒv"IbRJ>3 Xz5Uv9x)˒}ݗv; ,`Ӣ%S[yEw{ ;(ajj/fe\s5'?ꫯf0n9Sxv:)+żqLNl6Ȳh47+Hk| aG`zf=4O< J6o8ƅخJ)N-]ϣP%RUPqEP_Roްc7L5E4mJm5&&&8򗿈!|0/E #L( Iݮc(k׮e3-[kUt|7^brZ{TјbxZoN[eylsΐ}N8zˊNe!j5i[e⟮o^1`!6l@JIt ^'7 /r'{*iqEԌEh4/TT+ 3w t& Â@@`XoOcƍ&'yމ.K]]RXWp |ͬXPi4 r ?s_%y7fIAE` 1<(>wo07sÿ dyRhZVqNiZܘiU{ELkŅk;,Nqg/*/`b,Z0HtZ Ba}t$pH>pxbttUV7o|0?onk]"cw믧pk_ϯ⦅EӺʲdwӟ4g}6FN;O|nUyNu^pGp8 scn̍1>faq ɞxt-zCю2gq!ж a2$2&; Ӝ(H ZAZiIhX&+VkewglCLtyFӦ,+LmXUQ a(shXS kjFt=7em1\CR@`h٣Ϡl8G h0 M $iZ~sv0jP9qd (P$K 67x#QV/ӎYh6*EVBfs6,AxX):<7|4WZAyWcxF,+8Ց1YLkƌ2 rRk5^%ڇ7y a@C֬XJkvn p5¹3K-Q1ePi,Auk]'[QY0*Z\tj ,DDa{=g:svpBN{čh^낫{ܽ~\ܘOB 0F6QQh),$Fh3֒.5_$ˆLX`1' D@PTl{PI۵_RDq0m(lWtlfFLAUb9an̍WkM  n3eYk( Sy/SwB# @UD+ }+&ӟ%/?N2de!8b23.3 X Wh# ҈ frĸDW$i(tt=ZA03عܘ0 T'*)Y(N/**1UP *Le@jBjE! $/z $cِ̉3 B_K)d1b:0 a`=Ɍ0錏S*5¶ducEz="̰scn' B z!-C R *2 &HAnGoՂPFێBRT.QQ IDATڛ.#/yEElZH⤉6TP*Pif>^m׽aJQ* aE(J60JƉ0T842041(=l[[ܘ~R(5wjgl[mb*+l-G e` \`;P5=,@ EGX(tVTXೂ43{N™IƱ5TP4Z-*W24m3_j5TծMZMOscnakL#QH0 fmKk^ jjrIY2gTf {kd$a82|R srF-13הtXh1[eQ0щJ(RIc$up 5'9 xn̍;hopG\")T)zZ }Ì,@+*)2D1e,Zȩ^)ITii^* gVmJξFUB#=Z1cꢠ{1SQat)s-[N7*BDeBQUIBQ5X\Нsp7tN/-jքp Hk~$6OUY#S:XYsnmFA=2ɖ~J%%F <# eV)Fb$>0([ގfcƾ͐1뀫6%י<\hwl@<+@ HCL8 y֟3%{ I0n 5_T 9ύ17Ӝȵ&\P%Tixmya \ BTUJQ ԰G3=vۍ^|b]!MM@kչY 2 syJ'  !:3j:f{bQ5QJ kHf'M,Xxk&І`[$֒;o ]Y8AL[,ユh2%wfh̲̿v43qYv]wW=E/ZP\1&wyNY>len[{VUEݞ1RE &Sj^n zanPl2055E$>snh6$IpnWW}O羧ƒ$AkM~2pϓH0 IAw6\=Yh!iYJf}SZDZau;Zk!VnmLMM/UUEet:HnNJ)nF.9v J^eTp :BFcZc$(2+eJ[a . fa1@"|gF+6 c.h"0TFŰPh!e@^T3*lfo':H²p~nS{, -BM1GAݏN`(.*JEfAp˭{.ZU Do,9]B0d0(bFQDl}gDQ3NaN ])b!'& Qf }&>z5M0Rzhy_G ?;[L!W^y%RJ9?vpI̛7A CM,8}sAC}͛7#cDZGoBVKF^Gdf vI3`4Mt:3l4ellld*7Y`%Z-ɺսMu]/˒NI>v7EwCjev74MMBTMsnv`0npTBL0ўOh&PX ֓+EQ U!ƕj-ʕƅ峺+00sc>,qe~q/N>"TA%9mowv I,1=>b=K)- /Zqf۰HĘ`ZL Mr%Ho Ҋ!* Ԍbs4+>N? ]nl&l޼gGađ6M8۵{7v975ZM/0Y\UU CeA3wgYV,R.0 `eP Mϟﳨ0cbb,8zIӬ?t_\12u\pNӔF/pN~f.cv,Y0ޱ14}`˖I??[>VJ7ZJ C R~Ln.Kyj?f$ IP `\櫵իBjժ3Iͭ7o~dYƪUoh&jlظf!w#)#Zy ES+hB xU)ʺmj '2s[(b6Ͷe.VU,[H?vtRLPCSQ r{s;~~ugp(pe9[y;x0,k;z#2VDnRIxQDhe.eI߳y/<90(J>xwy> Cnf8wxc ]&&&fмv\F:ghwt[ϟ?n3c xz=}LLLvMe3 Cu""CEek`rr_`.3u&z ( 4e||7w7 "˄ع׹6y˔[bЃQxR}cu^p`0ٴ{;1kP^8 u;ltՍ~ϢE W>t7iB3?t:swϹR)UT>$t"GՌl[ՌaڹWAi$!(펝Ƙ 4#[^{=QnF>qlz.!i⋛MNNjvw} o`^I'3gxLuQWː\tE|ɜp `w7nñ{ҥK lBC9 o}z 0z*B: ,\^2y.2>z5W,7K.$Iv_y_~o}[x,Yڋ>+rWi{|tI,]%LMMQU~;/zыX4>Guk׮q嗳zjW\qʕ+nrJ~7}8ùŋR,`UUqW?-oy ;-YBxLqI'yX0'|2'|2 .UzvډE򗿜V-t?ꫯf}a]w#߄aHE#d^swPx[Vvi'&m Y9]t ,`;Ca;s/͜.2.>rU,K.!~˼V?p^U W\z)˖-K. ^o~N;tR>яh2᠝>eBi[0AL0hSM5`|MdRQZoG[gQ:)2 й0r4Q]iBZYڧʆ4*HCVDQ觰AuVVS5lؼ=S{>-gy& {I7Olظng>>k͐4cݺuY-k?ŦM9NSO[jX>HK.MXn_BpUW/ɝw^{E1>[n `ƍl<38u1r s<Y|gw}{yGYO~]u뮻~UW]Ž˺u8袋> '`0*~pgqFo؀"vl޲ Xz5Ii&~ss뭷299I'_ΆIFXwsgpYgqjlذ7M|Cbr8䢋.SNaƍ Ðs=>>M6zcl}G?QwQ\p+>(?~uGL ^d(.hP NWHRUW+ 鷬ZKkZvG2$hÚkxY[9f5Lη )%ex+^wm0裏#G) ^p0J֬c~pSO=uFQ.zvZ:o)6l9=+fڵkygo\q(97OѶxt֙gکg=#qt}q/}}هvmQzA4l2y4[T1pgh4W!'dQ<̟?F#:N-t <<.b^ n=^펃r)tA|n5K,cΞbV IDATea=dӦMeIc…cMs gmxQI%/aŊ<#,^Ǜ,1>ˎӜe?$cٲe&haɒ%Wdnf?p>p饗RUހ=ܓ;WFa\\K/?%K|qY6$ SSSdyN٤qx&EgMS?9Pn6?x6l؀u&)mV7Ȳݖl۰V]n4#NBH90>o>UNC:<(ÐFg-[>yvcsGc|W )9EQl(KY3 E]̅^`0Ooַ4g <6nHs4Mmё(cc;-[Q[;Kɖ-[h۾ia.vuW~"`0GC \M`FCKYR S3r /Gg4 *U9hpPeZ{gz!*E:ܦ-w>4:i:+SwXe5L(e+Uw2 c,kR4F' *U+)C)qk {yg^Ϧ`)*h3 AQS5-Pimb/;Ȃ./%4l!\ b@!^Afaxq# UUEj}d^/|nشioy[}U~a>=gq+W$cs=|k_'qnv$ok7V?q>pM7q 'կ~믿fɝwɪU/~`)}qd]{ 'p]w xb)*wu^4M9y^+^ /o~x衇|Q1 eeYv#sŊ+ꪫ;3|;gFۅ7ly.sz-[P%կg_䚝mW_g>|ؘewuEQpwկ~C=7 Y~TU=_c9Ɗ,e_ 6կs MSoO~WUY 6uޛG[rUgsN wxsR M$ @ ]ƽ 6.vZ ȋA^,X2]3ti/0Fva ,dԬԜt8CqčRXJe޺o޸qϷۋ|cc5cvP{衇g~gZ!w.Š vͽ[Uפ)7r%Zg?Y<8 ]R3YZJA$VVC_G BM4! .BN_UKfB h_2dp1ݝ*}g?GX%n/l4"[ FٍhbDJ/"\k6$"V |?QsͰ5 H Sގs:SV3 &wn=~◒}$ _W_evyox+^UK.ᡇqa߻Ene4I658)8g_o;˿QፙV}:S6+=3s'r26mw-wٰ0/xHϒY^^fT䌊(MIA&ot0bozӛXXXW^\CD G{L͢Wkhgٲe3s&'L4M9'?IN>$N=TfggyӛGq饗avw| _̃Y>O;;lټK/l߾jЅJtee\|wqDQUW]ŦMx+_?̎;8餓x{K^v1fv>яeO~l^X}{g۫&\?;oam|l"/pR*Zk}8g֍>)¦MP%e-,`1 ?\UNaDosw"ޏ1Su&.J0*rYr8# (k`Ť2κ`J!",fbu*iA!ѫ3Pv l `xJ$v/+A *0R_[6/g8W\qRJ~VLMi sзgl.\p_`zz`8q:T x 3eeuAEkyP &TpB^\ /s$ ~7eu3sVZBkmؠ v 8`0h+oՊ2du(ȍ+7T///aÆʯ#p|AU*o` 85:6%7ᤘ~-/|/$+{F&}4 `,FEx?`=ƆS`4ٲAtUhQ6| @!5[Wvf2;9(Bg}ƹ ,,lD"ZqJ(!&C,iE>QiEmCztat0_o)cQT(!χ/yǾ !}ūXT(Q*6+LUQD;mWntgIDJj B)i}1xys#ǷfgJ MpaKPQ7>m`/,ްڶ}fz 7tjIQX[z<ωT^6??J٬C= .R ELU , Cf4KF9T3lذbXҴ2n-BHFJޅ5d:pX%YfX"3=`္9\^bjj '2JTq+ek}c m)LAoj%vj!쯮277KyNo\5}b꽗w&ya R',Roquª2) 3x/b>j ٲ|RōXEW,Z@(#3 IVd$B3E7F)>!EՅ侲.}q3R~+Ae1à\&5̖ϯ]D3kr Į[8iF"apQVPǭJ]YCe} gӒO>N8a NSU?6XBQ^x'}Uj_%Z'@ϫ;u߂  w&,PJի(&ڭO$[lzsW\qEu졂nZ-z=5τp ހޓ*P O䠠l8j>^WN+̱jW鲏*p5Z AZjV6+n#@Anp1f;VUb nP75)%+vh@jIP,e)8>`y)U+-nĝ?m{ް!>^3J,N[ՇSEVzV :a]"0 5= ZYH+!1»l9h)O3D;C(٨NI$yQEEXDЙY1=3rOِ$c3#P%\i2 ՚ ]x/= CBL8ڧ5x  SSSU5leJP0e Hfo;9[$Byjզ7+{H&b`qe2$}^mI 'uȅ",^ZkD.ZN 0CxnX Ö?@&i{s+ݏgrCP|B! £>[8IkߐXLCp,3k b@bF|r`uuq!ͱU)NdE.lp0^9tTuQQ`HFF`V cd?,`Ư02bdB)Ff8)qv{Krږt#"=(J:ˊ՚5Ip l04uǭ2hQ+++U9X1ِ|FQez IZ[J`0*`#,kÜnU!k2$F/UI3x#BшB.`!i ;M/7P"0Ӱ Z&@@ zxz<է{D1|\uux0plU$ƕ ֭[5Ρ!rCH\ @"/$IaS ʠ$˧)J!!ҍ*ʥޒĊ$-=AJEU8uLJRaE 9M 2")R!U?# ז$Nq=B" 9JbD uDk QB~48>J&'ˆ Rx'To: vKI!V 5`8rem=eö9ITRNPhoŊ^okyx%%v^0[mlӦ* $*+ς4)\~t:-VDf)蒶TXΝj󜗿FCVujgtFy3̇)@As.\s +^ >߭˅~^ۉ)W+H$s-2=cKn%oBZև$ݳO "3IțȘ萋N=T)FX V#đb)-&'cފ@Dg5Epس- j0`skKz~G`RyAˠa-R-_TI|"9681:G֚h8AQDqƁR䣜 6ϻGNj>]nONU[cC-[k׮*9zPm ,2lgKhPu 8vU`PE wҬn?EVPU2\t&ik͂P }9unho$IxK_p8?!Q?ç>)^Vk~~تr#L @i (JvjSzW֎?Lrpf3 8/ *zL)͍߽8ٲi 3ǟJFM61iT  +ĖCf ?Vk|CqETu~ IեPx0;`u@ G, FD|*,` 3i-zwr}jL-[Fب5Q7>2+bӟIZz(NOX-ahp4dbWdyh ðׯN O,ud>t6b%7Mo Vk]yH(y[ ?@$$:锘pyOt4(9{!>EqYhc-[6s9qm6ba~mVǃw̭?.ýݷݻdt)嘎&7#vs;{ 3X!Cfb Ev?s'fJKʾx﹝E0-A!{Ys/{}vBraX9nQ9 wGyu=!p5 V{ 9$evJӌCb-ᅱ(JLՔ*.۷oOx͛78IظPI>~y~bz96`p֔f5lX"T0QZ#WdrZi<3C rPd38^"L {6-t>7Y|.VN:)ɬeǎS{8}wgꁇ}LwyE/3=RQsD0ܳ ?y3{܇ZX`8нm;8Ń,޿DyZCH.S3lvmeFk Z~3gN!o(f8XZN~($Ž mlm'{#0cs7ٲe ^+%\~XQcQWN; /~kټy3+뮫g(yCc _hZ|`/2Foy[RWJ/~|_grʄkffbI \,ХBgǎp tIw뵯}-g~^׿ܹp__lܸvה> }C Cz!ꫯx s݄6q˦Miw"CH網yyl8LrA#Kц)V~2hɐfoH!$LHatƩm@Z0Za_rQ0Q`5RQ,--q&Ϭێcߞ@z3zhmJ1?y]c+o =yo)h{|00$*w"Usepglgaaq`#Z:.y^$)Bxy_PP>5WU%50 P"sngooUYUѤa0Txg8κOBh4s9/y_//rV)x!1IqVz333VΠҪ3!ܫE<3*?]yh]caVBK#Yb, Ə6X#Әb0JhcqBaPX:0֑BrU +" 빭.w:us9L-gV%(.pȘJ s߷2Ygx&J(dPg$-aSs(F ez~mX&3}Ɉk̆-8SV\N(9[p*!I5tggN;EA.]٪]-$5zvD[j4B؃V%Y@IZo=*}9?<"G=kDz \p\s AnxG5Z?SI,-25;S9Ͻ}oeS]gwx?Q,*⃿nk:8镅lDFT#>Wyζ7#Hљ"8K%i#GixoE|87|T h<LM--.QqBa8k-1Te~+P*Fo|nuwV` C.b2aUm ˦;KVsȂ7mfn6F~/QwހبvxRZ s&y|wލmx錬%$Q?׸{YZgpr㟁[!) %Q+diaob57s|#ϩZ%غC.r)_5'/+ZO waa/}Ks.]tQEPVqS%.2߶viU5$ iV*7͜q .Ur3335a,{?]a< <W9??ke ʯJ%o8cx|/1p댦ze*c]?[[􆚥\>y'UUr8˦ـyKO[9pDy8GDQe%lJOWTe333,//333qwH'$0E YC$ wQ\j fl39]YEK]"$ \cJ=5$n[ѿn5.nsK.=_|q՜ $,Ȥn$ưzɵD}+!#Np 2ma)Hk|_ Sh(|켟TE>X%(0M^`iJ[,QR ,ENX ^9# tFK mQBLuZ MUE*У!Siʮ[ocD)/|ыIfX qJAg6+)ڃKQ!%','%. (pcA _?5Xy>7OZko]p8*/=[Le*UVkŐG>S-xY,:,Tl%f3&95,[!To!MMMX=19aeS5(łxnWVWWg>wv<-µ^[-AB=Sk+u_4^z>Z=bNsV;eh4gv2KÜ}mR"0+abiz!Ζ#fDKI(ۖ0H!e08Sڬz$QB3v(Zxӛ=u+1;t%+9)VȪ9\ݯu.h≩X#'\!sD-jd=قnض %-++KDqLo) t1KSH?,ʁm>\Ch(ZacXQ(rQ~̍pҫf@՜5'ZYX[plt:u֜0lGT*JNzްj-cJT6߿5IֲǤ;t$&8>ZA K+*D{p c)!ICQV1Qjl~b,spKTU>f9cL@Qvɷ|a1QYA M4$ܟ Pp4EYQE&h#Jو(n5q#nuiwWB[qIIq ~!Cx)40$spEQ#d11?+lM/jՆ,ln;x/`#V6~#6b>317~gTXtAia.50:pv=Rs7 ` HL`QB ԗtǑЉy #ۍS 1h+0oU`^}YN{]͠-?I.>aM"FzBc|'Hs~;drfR8+~ QNǥʄmJ/0~ƒ8E3rt0<^)[6C1@+s‹Κ7Ck&$hyEIYIm?@*{]Z)*I?O>JJ:g4NHlɁU*KrYPTXAZS%%d9f"nLҖ́*'[hG * O!"–>ID2E0J8(2pٖyK/SO#ڱ#eLP(FJJP[ }6=!v h#]@?0\ܑA %2XGQF kgq2&7ю1}ϗ t,pΒu6p2\١ RSνASL|K U0p|)<`CKYʎŸiFs>2ߘ 3yshd BջJ $(2:itXz~e^(Jݨ4W(;'9 Rc:+>_ `z ycVHdn>F>7x#ig:t +F}cVt",- \h ܄+lH)掮72F /noT68iVV(7aљf6IMKK %FݺZun$Xb(ȼ2,!$IT)0$4NšDM<I n*FH&C*II6EajUf}*oزܵ[WYY wD)9q&&hu͓p'+"/5 9]bNڶ`eLaCQ5vM4q|jDԆz6qxZb|诉wxcX1 -%̦n=>p!2  B?r2?-[u!XÕ`rQL!^/5mmM4q4֬O 1섯| 3&(>[?L6B-j  zہ6AM<-tb`}Ə&x"ˑ'&h'ѱ`x$SsQ51R'n ֦cm&h*4Bthp~ IM4D?b.1pe9FQL? ѐ]h?(9X?!EQEDREQ5&hpPJ4MZW&hJ DQDբ( >Vh$&h5  &&7ɷ&{$P֊։I!يIPu+fMdDM=I*k47S-(aš]%˦Z|]\h&>QRMLL&pXj`AWeBuz|zR*&hIGC,z !FM=d+\Ca! 08  !^8pVU@!ZI3bch!!^޾u70zuNxV[?H)U;PA#g MB#DiZ[ &9EfuMTpohyւ+p¿= IDATs8L q1"7D"A25g Zi)ه)f FKFX&I{D-71nhɒr+鄗 , =Gػ ~~XcRcuamY5Ijv"0 wiXllR-0(~r5qPBCJk☮^cnr'ܠr5ki#J)p bPҠDJՆ4IX鍘iI`q,W(LaAC$I~ {77xAL)7>Gz.VHRDN"Ty܅'D9HJ똛; !'7cJL@h3) 5 $R  BOiBN1Af}3&8BD8#v~f 3M`Wy3>",3Hpa2Ϳ8gF(KhXF n#+vb D}@\V9$( |CZ:w>:f9 -#Azwms@ho0`,Έ TxċM4q=L[;7A/+J QA\%X+4K'1iuȊO{CB&zDu8 B2do"wb؇t=F:VVL@RlNh\ª>#vG7MdnyLM? ]li)B!Œ_VNd8g<|"LJKG%CM<8n=\A [&ܧi巑 8bɊkSݔ<!B R Vw3(~@fvb]4N{EY)<%XS_J ˇ 43r hX>0V|;CmH&E!`i VȰZeDM<[ǧ~8 adVQ(i)o>뿃I[-/xlHu99q{.,mB}#"pV`$\i@#V8QUeD ZuB:G! C sp?al6<-PStUB$|pVJ9VU]¤eM;QY(jl>Qr,PrtC"Ǿu],7pnE/~LBZb,EEarޅRDZS6Jj1q8uiM[VN_WY$ו;ވXM%[+Zj61q`/a0O6lÃ1w{7ZEŪbU}KfFĽވ+(VQeFfFFd{h&p8|x$T>޼w_dk9Gɲ'5&k^q["ڂOzK~]ͣZ#X%&-jcSԠz79,h7Q]P}Śf!UN2Vhp(YL%$J@˺zD2n+.XB'`11dʌt(f`/G8)ܘ}S̄|#|XI Y U/a9#3W;x"ϝ`6do<۴=9ZWW-tF)f%0jWmj{#-i xS3Hil=Akޤy%.>l!fMΒ :9,׹?k+\D[MQD aH[†\I5qDwKn\o>H=A6bvmGV11JDYEL&" wzGbc j7K8$^JFK-qn?ʙ_f{QL8OhXd+ ɀ )cbF|0@b,D2">9GWUdM g̘IڈHԈGeusN7LJgh`vJjT!b0yjsCH 6f uM{(%p3Ui !"XиdrdMv_{s9n=B%~Yʔ61zXݓn!ڂ_9Iaw>)'%6B*hXV"kTx?d5vëx 1 I!bnq.% 89&)^FsC]G:%lLT3z+R~v7i4Y{{O>Am/R3z`pGc炂0|W]A%4KE&8LP*&Y|xl#Bb`[$R!i\ez`ˋMcL-ҁ?.5k{:.h3?/ &'_HdBv'8s׹lLF3F61Rſvz׵6q70TP $pEÚ+?^4KsӤ[o: ##G@F_ ?׈&U 4Md2mQW͚o BHjLo]JzoVT$M1Mqp<bL$mV`#b4-_c{ m?(]3S3$#YF-.Y:cVR{KE*Y%upoOhXvb̂Ipf\xXdE5>o BTڵT}C+ڤRZաl5 Vֈ~DiF'4jW2BG(ٔPX3MBt]"&ύ:;gN}SGv~')aPPP$T_ w>=X*1SUynՈ9j5d{A:, " `;7ϲ|xs (r8sɪ.uE9K#8e8ݱH?BAT;A4>uBs7{"!2'V!^.QH2O4:;zH E|XT02#-bMeϲQ?grD?A"L("GD%7gOA|_lvC"&%Хwc1aǹ%ȫ4X?i_B̍L mms UC:T {Pk-]T9jHmpmX;AKi^DsQe­KBX޶m1U>K:Idj4D7}̒E"d+Φs>K=Rڙu|`Ro"f>_bM1Ƞ'8x SE@8#t,gO>ƅQ=rom3l#%"&E}4B. `sTpHQkiZRp7QOY1Br:L %.x`29eX$c(ɶ_0EybАe~BIbQuJol^x%} -VwɄOt` ɛ׮sq/ ` )U9C)ɻWѼ2=nfK牂Op*PB&THA&&y4s*,@hZJkrt(DLKj!:n/76'Q5!v4#.OuUSo 'x# bIKJ|[KWtkkȅ!G5LΎ U5 ꉡZ(Tidf;ʜ>v^6˖lci 8W.Ns=s=O^BsI} #SbާVV X !v672**mApϋcj0b҂MLz1W/qNo?Ϣ`le!ГB !YQ%"K 6]Qً8٢iD3-1՜r#L%dφe[tN0:%E*1~i¥:2ؑ;jg7VXM0, SLS8gR5euT4QS"܂|?ӊ I} eg5maM F}m* fCHÃT]f+#lMw?ĝoSL4t~!L._ev/-Y&aqKUlb(e ƧTSӄ98/ O}(ץ&E&WHRML&/h*}&w qIFfdjPmh.}}8uaSĶBesG( XcĄ=-/aA}@b]C宁BLsTUfk@\bdd2 N6fk$J"AU?tQܹbؕSWyw?K=|M_;_gQg$D.9666y0_Dc_ *gym"TO;sPqRxYu?[/W8uֱ=^)}8^/6s/oUNpנחt:O|"!.1uY;~ttޣeS6W~fhhVϥ4 fd&k9sy ]?n123eg*cYl+oƻoožͫb1 V~`„S[b(|K|w{>7}?Wۈ]M3oǷF"8^{ ޸~/=y*{,%u=K41郯C/C=9!%BץZ$t My= >wt l}" ՐJ+ Au`ܠ]1~z͍%h@"FI9'EOck;T[Ң.f~(uh7[Ad/N'?;\}{ޒ6_g8},䌿X?㭟_?/^oKnad>Qf_=é{x"5Qo>b|nx=SOw>ڷ;7Wn| Ux_VTZ:ru6ICbQsX4::*W%Ik Q!cG l6PA֋Ob{"Uʚ @趔MA4zQ+{#\UE2=iOߵTVb$Dg#R7Vs{ qE04f^sDRCf=YY6,M*[,mp}7>C+ _׊S{j&tF'ʜО7 3Dte. r7ϳ3:Gd,0BW_c2{;Lm дs-abPO,`R V*Sf!rc*xE#boy z =Zjɑ;.]|njN.6[pGw1q/25'iyh<5`&h%zEe>KQil@LP? V,n MأkSz/WeZ}hѵĺX,p'jHfR k;]UI4?kAYik9#B^[Ux1$D½aBj3IDM`_p  ,|ַ>u4{QQ6LwoSm,8y({s p~/|w.r ܼz?b`ӭ9Llnu.F`w"P9_ /F~wY, WxQxs;?Om(K\]1ߟSՖ#1.(_'^p{D0mK U6Ӝ<0_z7٘8eӁA5Em;Z$ &e8*VPpwC^{g)]Vj#be {l_B;8e24\Ġ 2pa鄠Nx'X,z9s6]:ş?4lq9v_Y&X ~=ޮecѯ1a"³\}tƜ{.9N=&M jrO {?̹v:-~wʲƚ8Nj_a A9u4:6˅q~Av8Ցrq!ʨ=$ B4/H:RL~۵0#MS6&x/s؜\[5BtTc% "'n +C~g@Cd{kt +[!+]Cm5v_c+ H G)c!&߄e&[ݬUorL{//~Y~os鹠ND:"|E1GGU9{t rC|MDqn"}{O]o!t+11b#QrXZESgvw8`껎{ =":y;]'6c{EMYI}K Mm\l#!FEu9` L8ɣ/>,acXE AfiVE2;~t:E 6i LkOrg)w~ LUw`;Cӂ:Kr _/ hhCo nERu1[:w]̛OH&9,;P3FfLf@#~T#Eq 7tEB|Ldai= a%T2XBأg7M(>CAmOcBrB`*It /F["I$8Ec#x/lm\K5re/ٛ?rNB.\RjĘst}4SjcN=˭~t QؿvVq0w;r w^G)d1[,YUX鉈﨤@6,~-V7!ᡚc,ۗvn!^MCX\S͑!FhsdQ롞Lb$rJH-v]ZtPom&tLAԥ<} U|, #4 Ɵ ɣ6VO6fM@ѧ.2ֵ*,H w(h9`[ACCdR`fC*PSnοϢyes9i6, qAK*[l eCW'W-$osi<ѹYו26C+qq߃:mjdVd0e^,"2,sb8;Űtrd 7MUU,sj',dHw*WmA!܏mq`lcB;HreH 91bP_d'ܜY*#Ɩz^Y.Sd2ϩ6s-y0tWtc2J'&Uq[,-uԓ'DRkS{Bc 6 q''8yQ.8gO|-*3pF35ޯ_kmújt >pqu?z^4|$ץzɾY b R)ΝĹ l|}7ŗg(FL7fi12N !hrR|L8rXʨQhm0N;JmmbX<6y)%M7GDx*ȈA-ڶ˝rğ_OrdY& {ebWRUyr/j0-d9ER% WUe+`p>u h:!h:ئ(, vr`Ϳ+͢{l-zbtm >. i(}(u=[*YrQ,4]yizD 16 #ͼG^qʙŧ8yНeeHK]'KJHeA4Gj!)a[I }D>Q{`/LHjcLY }CbEm *sNe.}xP6b$Uz>b*r1țZ, u]Ĝt BsPivc )}4c>_z?mBWS-6`L͞1zBL 3eue@/!Ur 1=w}wY3*9Ly #1{*I$jUT1|ޯ cQo7k7c޼J=]b%mR׎6ź!KU\ڂ =T U Vr3u.kiApvdȣUٞ\{=f+[mTϷbLl͸Fc0 mA!܏*#q6xjFVs~n"c: YLyBԩyȥ 675 \EL}t &w`@!v\5]A(K"Ut;#8s,mΜz}3ۿF)4:660%*b)/uCrq%5%Fc1)(EFa= Qz$/(J7PY1:l? ًWh U% T8ܠWyh_Mɖt+;@v{^02u~> 1Q]}f>tF+#?K_nB_ "h14I:NIqĐ[;X[6sr`!1KG_W FlN.iIံ76Ƒm.vn^C\E^ = X"v[0/ލ7˧ Vw]jbj0!$VHHH(ӕ+q >z5@ qg!e\S*=Q'srJ[Ss޸3ɣ3kos^8X:|yJ w{v7DoЌ[bl[^tfRMsQ 7wG¹+n躆0ThZpB4UpYhBlU#! |[;9 ~^D)yf*+K{ 4H3A[4c7PͽyxoYԾnf);tg2\MRiH8t NW*])%-c]WSU5l{}^䑋DS}x-&n b KYPPNNVn G!A>R]cg0(mAAGlu[#1Ɓ@N%:™8uyoxkvvPU审>[:klM>KhtMz` |P]s~(9̤̽r9!KRS!F1ulǢyv`I}||׿rʴ:E s+6jn!Y* ~nNjrƧp$ 5Ya|!f Թ{3$EٓIM҅Lf?aZY3۴ʻÊ2LڸNMtqSPK'")|ގx3$NYM"T]fؤt IxҜ!+Զc6.[l)O?Űuײdwf`0)(( # )${ȢS~="]Vr{&…tJ1CDEkF$[>*֭AKG}?S >N-AD{X{t5n6 VR\<#U*~tVSY%H}buSBV(5'5Ib]D43W ͑I)5>݄;D3>LvGNYI 9T~hΥm1F66@mQ<ٴ*{Ź3.޷)W@hу}nnv >m~jpSf"vo}]n\>uM&`lz681j;AĐʈf5O|s1D*V>x0aKzXA/ 3)wKB")N>dvMHTF-{q!X%?wMNcq >VPPQv_AAACPPPPPnAAAAA!܂Bvq/;ܲ5Q$uGrtTVPP1_ʃWa[H%)׾DER((((([PPPPPnAAAA!r ~#5Gy|9vwO:]%n [U1 !v8[QQt_GG\x8R[վU@5.9mXelj8USJ{sboq9ׯG!U MO|n#W;)%㠠W> V-[PPPq]5:(oA' c0W˻d)Ӎ[mM >8ۉH >J` 3Щc%䂂Bp -(((T11 we󚂂v.WKioAAAA -(((([PPPPP'ʞ1uZ[ ٿ}3T##17ӵq3L[PPǬN%#o2~_{+̷f}":+!JH:fpzrFg!ѵC`c2GON;9̷f=Y$ǹF%iF%%xhw"* }@]M#LCU,CD M2C\1#FcsۃGۮU 8nԉbGX-M>m:AJD4 g)}d<&Ms@$zMF5gzo!ӟu^&q}xIJ3&~5 1ms۶@kasAA/!,k~wp?[B GT D!J&F"`UF ]wƔ:?7kamY "IMgO;BSrs>X"!AIb$M/󡎣2e`[< >'|sg0}SC"uS"ۂMZ~ҭD!#6ʠ/>~?ȟVbJ'aRWtˆdr _(g.=PI~,!vq11 Տ!~;k-r6*J:7-b'm>PBl*@Lbm^\J)(QT~^eo"?o5衏~T;ܾ 3tm˓O<&CT~ 4q5eAXU"FO\}ہ$((jHY9Fh̄(14 VA,hDU@#"6=/[!"f} ^ҿy]{> erdk_#|q_:D6DIu9M>Dd!-(h䄻pYL;}p#2Z̼ &d%ƏnC=u !͍p4&[kyJ"q2ub_WygL-rٯ]}#gr"lcL!BG:Nq'Bي0.|Vc^-O+)kjdh@pXLIt!½``o#-R^onՔI~ IDAT|+ )ϟi\Idlr_H8&e@8gQ@E$/O|:TnRx72!<=Ata{Xn]H0amVr"Zk}v:. $8)$!C ƑsB끎X¡QX[;x!yj2e>#@]9 L&t!-\i֭U zUՁxgǶT'1k:fn*ʥEL-gRQPp{2]Ӈ>۟ue/ t>v xWqD6BT'Ȑ38F ѣf6E~T&j~] dB#tߋzZ|6X1"jTQqpwTb4 sXqD=äf]^X$5C}fmf~>ʋ{E}"P ~~y>}4#RkkWG(k9Ac"YcQq4b0{bNLI\\mzZ:YiU.gm"ѐo"KDJ@)vO$@X$bX}V?ܺO bɂ1AA}kK䉣ݜQ[9 !vLj!|GUO]Xb8c\~v0m}1t]u4XqY}ntplkypk݁v!tJ^038WBbѰAuHj9pQ5m㩪0`JXokC3Dpndןy0osA/^sy3m*a~_FɃ#!@s8c6a}M)ng?VepC/DQem?97 Zb\yɪԵuqxt: c2`ĥ[ޗŢ|[:4 `L&Yv iN& M0NW D}^s?/]al;8H|ptl!eO{Uv;t I(HXFT`C`@,?3TT"I-!Ro9e>+0ys=g}~Jt=FJq,R ZEpHMG]όxg6u T#vZ}\eݺutwwgȒYҘRQ霂qL!y ]VF0J, v0Ɛ$ 4 JUO?p K:GmnGenָFsԚﳯ#F)q 贐!ZjI%4C8tk,Vo)nI)[.臂ZpߌGݜ< -QpH=8lbTP fȑm$Q#p$#HUL6l@hUJxWpO+n""u Ufq."Eoc _-GB&$IJo Kq V C_Pu׵Efls{|;iȨ:}ߖ.ۦnH ,r+L@Q|ހ>KlUFB7St|46vǦV9uvǶwkw[1lpmNk&aPmW+Zr;;iU(p\\B Rf->7<_Њ"cx4OY8\ pDgi6Ϙ߾q(J@x\.QoEjy?9S<-tvvEQY뺃&MS*RJ8.-M-Z"hYHZ୔" C-G\EYq؂rȽ?mPFFHb a#-X\ubHkbmN)\ P d@Br0U}7:Ls|AJy{pضh#ZtAQÆ0i>@*ЅOYJolz{wD8P1߼[=z7_"Cjޠ7tfgzh]^ouipג$ r C )8 %3TEQx}D͇ nDB3y̞¦Mo{ /ϦR1F#;a'qJR4͌b(d$Lk, -c̐r,dz FnE}#>MS/:fRd/- _W n} W2խD)$qh&).FHIҴќ5FMj3ƝA{BZ>Co0 B7q15A')eoޝD;{,N:*m Bڑ.Xi<˺Qq<z1<̛7Bc T(6ۯo*hw TTjh ̜/N;v\:ܫT*͵e<ϣ^.l3 Rl6Rى֚zy<i;H؉}¥1ȑ֡z /8f3SdСaH$L;6Q_'S?xko!%%88L JH)B7((dVY5؆meaZ ŠӠr+3L`"ꬰ`4RF)*z_֞D8-m1J$vbo+ ,ʕm۠&5TL\Fv- ((|dBpgZ̞=TIj܌ M .Elj$IBT""+0Y.j/h2M (CZ+JI84HDž6J~ ej@d 5Lxq4(*T sg Emk}֔fN\&V Z;Hai JSJ[pBXP. Q*\Y<พfU%zW?ι< n^J3Ͱ?'ʌJb͟FhV:C1ґ4ic$*A:daU4 NS*BMAv:;b޹kNp!I5Q(]Y)Bk.7Ǵ'5U[SʠL#GbOWl)Q\6$JjKW @٭;o168+-g5HId,Z WO8x̚5)SأE]dz>puSJQ&9_Ϙ1=L67qL|eLrEt]/ӧ^{q)'qsiJ(/2QqW0i$&̙3!C0`Ҙ1L;;?~:#p$rL2hW^́doiӦqTƍ+cr衇r/3`{0 $h)%7t/->9.ӧ8i6̛7C={3]W⻒ǎ[oc᪫""{spuס&"z!f͚~cpGrw:^zc=}nj{^_iӦ1}tΝ[(K^|E?p&GXp>>?f f{E>Z%L')Sهɓ'W^ɤ 4aw W1{.(U?2/,]_]w>JWWf4$I*T*#pwqлa==|Sagރ4.8W_^£seX:Ukz {Us~:,zv!s|;︝'+ ,XR-_UHZrxǑ$ y?4F}$4呹qe~JLج0tTˬY~S{Yb]to~Ĭ\3gK+WfBϵZ )=^{-^gOg?Âyg?6L3䗿/._c"Ht> C<־~fq? FƂqםsļJgG[xq2\:+/s\pk{9[ki<+yǹꪫ Bzj~+Z/7gxGu] WR)蕗x0,\E.R-3jJ)}n֬檫*miR$ t ITJ#lp!CGtccvV4HمjK펤Q4&{f޶Zēx)J _aݘ>,3f\\GՒ?ZIoQw>9>|̝;%K)_{8{;2dZk.2(,#'t\r {7B~p'RDWWw}73g,h# P vQLZfc1=¢83駟c:SO%":(N/gV|#dĶ)]wRJzzzXp!Gqf_3/V"MS͛ǑGc9!BpIG}nnG}?n8fԩEJG~ga~?Ɛ!CK/|.:g vh[PxoWfԴR 0JH%56 m2L Pg߭4nYhF@jl?G:m=aUIB7ȐN^Y?Y'#%@g 5);4hl$M|&AmEkSNe3Oq1=S{FJ#ˁBa@H~@ bŊU%VlXĶJT*YRz?x$&9gM֯Ck(dƌ9s8s׸.W.Qq1R HhR |\ρr@XT+9쯡Әu+3jİX1qSըT*tf(l0quBXnh֩T*1DQT,y}5V4Bܚur.5UʵyjO9mo]A:?46iD~жn]e:ӦNK7bx⯼KO=PYgb~m0B1UbŊ̜9t<\ps5jrVX.wṟI'|ߧç?iv}q#%=R@GDqJGJ<}puL8O}6lƌodь58 m FUaÆtҬꙫժm8Q *@+SuByD**@=w!6+({UW]Eww7wuxi&ZCGGҤhȃڰve] 7?)wtwwsOmauȑmǷܱ-{k<@iHUthepDS##_#J5/.yz :H!NLaC&I>4M{:+%O|w]QpIzaCuXt)Qqw0ѿpQGQ45B͛-@ avj IǪ*Z!k/֯_e˖1w\?e^V맳zNؙF 0FqGrƍx䑹|MJ6n\Oss ;裹 IDATxgÐ] W$z7n#;oѿqUWxbB%$ J~W˗-˔giq|)lh4bps!zٿVV+MȐJɍ0jXVh ÙVlu::[,h&rgRJ#-8qÛ7y4G|Lnҁ %V K^|O= E֠YkL/_9Q"s9JG3#:dhUz(_2ݗs?;=Ǝb~qݍ7 Jqcaʔ)$Ep*?#wC7zᤉB87I/~c/T*j4\h /~K.ƅLg<|Bvm4~vn&)ArMt@A?^)@:}q<>я!äsc̸}ϯPtO|w)7\#iHI?JA:|݋ሣfi|?`Ǔ+OW ?ie3yν8N ۚr#G_kڛ#:_N%KQߑd-jR3ٜ|A9Y,8(b$a 6aO3i<ǔ,קr VvZdJDjtФЊǎeҥ(DPPDY `iJx'7z)JEZNur&nB-ysNy֖w_.3w lQY)ohy6HCVhƑ=^3ׂ8edZ4 2\j* ڷ9ɂv;”[kz:ZJ' Rڎ߶vpIMhlQdߦmAVb0s ʼnR!Ea6!AhmW:FeJa]mBe،o1Ȭ1 M[]̟$s%L1|2)ˆ =Q|7F C=YUPk  }}%:3<10u'Gki$HR .Gq&@eŠYsGbtfS#?,bnAfm(~ЭF"M ` ˿eO[^; '[rU1ORHi+JYC\Ud+C8G5` tttdr;/+>9(JC"z#Tf牕J>sTdgI3qzb䭠Ij3h8N `j4Vl"%REH- 6OqLir cFhFtwwc)}Beoij~>0j5a"Ҁ:[]t ;Hš1$f} xt$(3g4M@j)5yI, 6PbzoA&5-[8ImYu`;l-͜tsY>)1vNuӅ/*FnZ*g%*QXk S:J.2iT\eX͢gq}޽xq}zJzP /^%|#VB@I!-IBj[F is0rmFGd6ֽum5M x )KUq<{Ŷ/zȑl4&t$I@+\_86l@gW'aYZ[x$I oJRtBN(DGGG$q$IrT !p3] ﷿.c&Q rjGd MX5 Cz^Ƚ`sy(*nڐe|y?N|0B5;CIL]}z #Եَ4.$A}6mR*%颕U9j0[*vN;|ۮR4KHfˢRj E:Ȕ%l̹BX|bIcٚhufP[p;2 y0 8F0g]*@ s/{{8}Q<"ůDG5zUB%HTW%.2ã #ʓYncGwli{Qڊc3T j1 8:StX**61 H˕ p\ն>ykؐ8_W CkxPk6(UZ&TB62d}}}_^,ʽ _BT*r{{VF^}< rq959Yг}rvD>=9"ƍq\Q%IBgGeɃ] ҂7䄰mypoc=\5bkr] 8w 4"0b݆>"M=^N]0\DeF- ˍJY F"E6ŗg:c7s=5xbȚ 챴1Vo6⦕yRZ.52[ۂ{"sWlܸE˗/gyc5GyXQ-bMxLO2|89ya)oXnCeѢEYC9p*Z%Kfjf͚EE]f͚[L?1ׯ+ܩx y:>99r$s+JYyg O qXl>6g}ZS!Yp!=PIJf͚e;2_z΂ 2u*_}x&/y@f^,fU.MͲcK/L99WP*>E ڞm f`ۂ5FۛRl)K1:EhT[2SɔZ22QL&\,s q*fFHI[B%Zz )FK')q -jHף$$!Pb3"3;ouie M2 TBm+m}:J;mq@ $2S`+kզn񚴰4`-5:brr¼)"Mc 1F>p4=Iqec^k&l |x#&K/cӦMv:@V]|ߺop<ܳR)YGjΏx≿6Bd>hk~8̛7zдEn~SOetoI/seúxQyJ:p!ԎkLJ>xF~x%7pU> u;XD6Pi눂H8Ag'Jp%[eMfIm]o>*[9#Ml0|+F}l GMAڶJo6/<r}i-ڢ$92͊UҴƒˬcVRɴ9k Ny jփ[[䔂) xK ]? N҄rrY;fCZ |\c/DØ6kB[J!Ij -"M^-vL[4m#UhWl-Wi pݓ-3u6>(ך ʞ:d(Bd9Z#Pwرc?O>l]c}cGٳ& /w̞=0 )Eq+IS gQZ}n*̙36mj|8A&aSJ1cDp]saCbq{|->=C׾F?A1oA 56޽XLpC{ g϶geLiVA3frB@8~\鐒m!J*.65jZιKŒڳSjŽ9DaJ%vuz [nGubÕf:n;s|h{UoYAcbHTTA: F*q!i1wLrveA(A]9nX4Kh1Gqc8C1J0{͜f넅 ˇ?>6cK ~?Ә>u ߑ$fɋ/b긱|cҗ)>s~cP).8N6~<_җe*Te߻ }]};)3cOcn;%mCA*mwLOGzz&p0B I@')#a4*$@CgLoMӘ@:mz쑂81E 1WFZۤOh+HA ip$bNqR+Y"<} Hq]$ i6qUEC6ptHwG@6&Q`٫KYҢ¬&xYuZDZMgTZ.F4z1!{n/vƙZ5Lk#:#5z3RTK+[~AJ%JҠTqSgԉRw>(gP]8EhCX5":::@j5nfN=BfG+tR_L,T*qm~FQ?3{ű7\ PVT*A)]wŤӋ>!?7vw}7FuveA\fяXʫ=z4~a0GΝW^}tkg} ?^[E|=kYmtf{JR\kym٫q3 7x >&Q3$# Ҩye|[Wy#2ax*ِSfSZ6J`;B8(nui9+k8Ь_D624+L>0υQ *%JDqD檔|T4WB\*ڞ(DHJeSHAz>4c{AzMbF 0*EM]UMyNmXD>KLjݑ =SH[cpqQb훫y}2gԨ]34Hm0c隬&QV.J|d#A[M헾%ӧgr===Dմډ*{巿ms;>cDm6Tш>97oӍ@+#1lذ[o{wz81FQ #f`kѳme~d_[{ t26RV>}:yVIm7WJb`܄Ʉ:?ƍYv ]RBYKTEu0ҡ:i6 *g:;V+A:07RnL\+ 8<4zzz8s&rM:::o^M7ѣ(mk9Ø9sX3ƍXgg'TՂ+΋fUV /oo͞z{{yx衇s`.BR_|/~r-9v f͢/d:,f̘ ۹;xG?>gdEO>n.LV8S9ԯ0ydElƚ3h@aÆh4}ƙ|3qD =fgR0P뷜rsÿxŋ_λ3$W HSR2|w_{9|);|+ρS)yZ.AZvT#EhiS9sYlK.e jڥStt&ZUk6-;1ttTW$*E&4VWבqD&7V|j_r$BJw$eO7La88$AEL״uW_׌"겊b@E0iWuUWV}E $aa ] Su̐qO:yW9a۵ܵn=7\#[G ݙX ΢PNr/~-7 eE%)[?w۸w䎛%ࠃ䰣[T*Rք^}o-1Z!Q4[Z]=` ʜv+woCHYi qN! C~a*- &na|lQpԱ'`ӛkY92J+t'59 04$f^+&mrWj"!I$իZA< Aׇ&̦. -^= Z/zsȲcp\v?pƟqFK ~~mH)>szj9b}ìGnV<(:.gE}x23pWK. P W7ݟC\_~|Vyd=x#J%,[wGE]ĢEkEq+^I$|p>d+^z^/}9,:.R.[)5ֆ>ISENS,^3, 8h.bvmQfU#UIiLSb޼ymoLG&ISheY$ ?`W |p|񖷿7:jsLe NҮ4ؠoh,Aj߿ݼਃWYxi(Z@ oPɝԊkE#p@ߒ:O:C @/X QS[ qDx`:Qx6PJ?3*_,B:ի[6aI*e⅌.Y^8SX6uֱ7""/wȾG_ɻ290vcyMl[{+N&vܵ8'lJlĄQǟL taْnY 63ǟL߆& ڐ0!gI׽Iɑ.b T" Tu36Bw./#n40**ZE_ʒ4DۜCMw PA!80DSYԑ۾5x n9MH-?ncyHd$ĐbPDy ҴMnb-_g7yl-jm).~=DGpER+ }D g~K{۱9cʊN7PoiWdv2m;>$AIkRԴ$ P.єFuFI.Ue) [VdJIU 6Ip"xI%^JP)Vhi 3snf/ rJ m5˗ŪUXdR.@1eI֮3FZmz._#O J8ƶMPTEeΝw_ǺSA AN&H"Qc$Y/YE{G*Ef#H0>6FjJG0Bd`Eg]z}&'kz!/ZhtKrVIDQ[*z,Z0fT8|lt+%:e%LL!>Ne|A[kBK 7 N0^dٲFGadNZXȐjwȫ[V6 Yw!њ8FG)"vCl۲$\K9/ jJ86>>Z[Y@hM<ʌ S%JH[sj^2GǛ$IQ-5T{~JA(Ԋx,Gܹnc$d r CM⃄uIdYee)%I8meUMɺ] !p34ԥ 9Dex[Q%þ$~e2ŔLEb86UU-+hyI3:AVn''*!iKCtAɕ0"p*-+(TA~HƳ|,X4#HHIC^aP(q#KGJadR-qCi8`2P+=LQm)wkJz㴲b{3%֔(A YF>E]1VV d7z$Z2ot884 Lm ~@7~VjLNN,VŻ(aංnLy j"ȑܚ4&:6O VBV&Y\y^24V*)IšV2 aMpàV%&#]5AY llv6}&zh( I@L<9uCꝝ "d +.P0E)7h*G\)+|eȔB{@%qE@t^ZT $3v0ad$"Q_i”>iw#PRZ%NF*4^MfYZD$['zDSYCdINZSk="ma`A Bb:Mey dҡc1Q+TQWRo):r>(E('U ARXKiKar0 r>+8eTe%Jv%ZR9 ~Շs˿%~y=O~8y޳ɯo):*%{5%}s8bա|Iۊ=<9;U{YsW$|+_>#ݛGv*?U$Z21/sWQs,mD>s#Wsq=,մ҄#ۇ2ig݇vgY$Z˿O?#؟'>tk_Û_tąUTI~|Ut!z(gyg. !|Uc@d#V)9padX!)o,km5\鴤|9i|:w6&57Eù7nJsJV7iӗhҴMoTE ak5TpN7C~:>`Qf,_3_jR6r)AH%BqS> WtB|ޠXj/֭Ƹ!5066 U"Ul$cݖ|sVؗ=HE% PDQpsaT&l frȱEAU!lw&* !c{.3[UsQ7`+tEB.<ͣ.,yLR ƙFf8z8 i[_a}C୷HCFAΐ茝sBP%V4!TXI0T$TH7B*),M=Li#I^*ecX>v%x~?H'We~pXw7w$m!yAn#u£|[X{z&'~|sX.zEIRR=}3 yY9/$Kn]H#(f*bP|˭X[&8'>iaG鄡F ɒV\6p?IP \ם ߢ]z)$yHҌ[oIG<^}_ټusF&zF*FUѣIlcE8sA0P[C+MI+ |U:pzQpZ(iDnfW"l%mk&%@BzHdJy!QW|Z#FXs %"ޢ|d\r>:Ehsq00eܡEVLCW }5=q 䓤²pC[ ԴR :mSژjكmyoV?7pRy|_EG|Oy f<&&^{StN9$.G85:v'L=?~^pia? !5k.HN׿G?ќrI_7<90KyرkxȚ5,go?8˹罅zxcO'mrO d2*y߼L9z f8x;~<]̛O|\pqJ'p ky㦛_eRW:V.?mPn̑:Ԍ`E |K˼"y'@L7sͷi8iڊ% PT+%ΫwW*0ee6t2 XBSE A{"jQpVz}]\sH GxIbqgQpAzdUA)p*::2pLxe%Nx#ywK1,Z06{ǜLQbIںT`ĥ@8]CcB*PXX̲ DP"KS!pޓem^ PXGƢD2f0葨HQ(5y.Ɣ6H3 8#TbP"tPgw"%/ ,XR ʐo&:%Iiglpw$^g+J}Rh$#Taxt_A ibbr2°`,ZMu,EJE7we&KuK޳t q NkO.DjW!Q҄:t1+/=6lu/)E!S+U|utlumeUOZcRIVR|]BN-fFDܔCXƜl~Hi'jK-X| Ly襟m?X)DZ( :ZR:(&d `qe JCn H("s(hu0/UAH) IS$6*&*tHv:CMd?G8C;DYLJq$%>\X:C]<84$S . MsHRJ$IdGx"$ ״ \in|۝mQ!y&itKZla&Ҟ&TAJGko2v9½&R*$?ɚ3)~ȑ>;6G,0MBnrDG~$D5|n#"th*M+76lI~>'f$7?L4itF#)p~٦ǧT צheǹ8U #/!RI%Tbwׯ+D:,;|3e%ylk~fL}{ޡ S:P;g{dx>YȑJ1%oBŹj#ٞ D}4.r)+H\_p\ߴ)?(*擭ݝNvk=n5c1!9 .c0C !~VdȜN8ȸpj9F$FzR*w1u3Ńs|LLL (F@Xbكh5济v.4M7o^)-kTcsx9n$il}S8\g êetW n#WW,1Ex C5Lv/,)˲ǭik;gv:jjEP:쮪bomƒ1dtLedzV<7pm2ӦQ<f?)ޝm%Y{?CrnL)wi.ZkI9KqB@mj\_LS\4]"Kit$vIdnN~A{psQ=B}ٳ:k\n`gLsy'2:J###va O&oMJUH?ڕs{G.0JRe^4=Ĕ ylR)J^y$k;}B O~cpaG?oh%8Wӟ4^GI tx}߻ >җs^:;gP\ ?ο#X_L8}_to7c<׾&m۶llfnwuY7P;(% rr k3.BDq?oqO7>Yz%/{oo+\Xu?o[~k&m;~q*Rxm+_2͜rIsyůyz_roWI((s /\W\߬s-OdZqnמōֱn:>p99pA$ς(cQBY}w1EIp)%y׾FK_x#8O9i?>[u ^kyW,38#+ԇ??"y_(U]211L5j婿os JS$Z)vycczꩌoZK |;b]{s6gsyȖ$R09VŶmXDy1F:C= ֳ>+Ga ,SPکWUEIOz_W/}<9Z-zeYArhhbV*cZ-$iT9lY{v0f|' APZhs6gs6e>JtETV׮]׿s(˒QdeeEÆ ꪫȫ1_SB3~AuVj6+Wd||ŋywa_#ny+Vpw`|&&&غu+CK4R*ȩ(=L5All"d^3AǬ95brr/s9 3L=lݺ$_qeSηOsC>aOiRqgԧ?(vMٸi#Ow>),b-ܵ.Oxo}(*x{S@jRT&ٜ>R 9hN>>Oלͳ\z|*G}WȢKCo:)@ȼ!/K<⑏.g O7P£bbVߠZ\񎣎>GU Α$ֺپ~:s^}Oxtm99y;!q汔W$Ij98cH ARK7i8ڭ" qTEEr# dVzD5ݪ @4hĆĎ>~֎s9p֢~044Ķ1v"meyY"]gįafpeY9{,ea"z QnKۥsvYمpmiDSr1tlrh&(.PTRYƂTx!]yHH ܐ&-EH2֜p/>X҂Pl6Fii\$PfR/f>31ZdF[Ō;OQ@HЮss6g;)[!)8 E*;<ZKANDk=XcBb'㪌#c=#amVq,?VZW-L_DGe &0FR78gV-xl3X=gs{uUbe"0@a*$$ɔLT#2nt^ZzbR\8JI+>J[άwXq{8V6>y(T] N]̯X*ئj)Rg M)gs(%N$V,K2|5NͼVJ)2zobz~z;8ٜ٬EFG{y;p!s^y w >: G>[pSNv#B؈}t>PLzZ{5DX;9ݬ&Zb!ޓ;7lFxK.e޼Jٻx(zWAYj7R9{ |k؁,. g[_'x8 ,/etr|v9h83nnMf#^]A0l_G \qp% eǝκ8L Ţ2Nܽ4a̟7v{ӄv͠ןxV[Ŝ㝳?~#ܝF)s:0QY"?' nl# 9M@*vLz-,zRmb-ͣه7}ZY;/-N֢,KE*Rx+Xݵ F8h>hp`mD@+ lMcȜٜZB]c4kց! TԹpQOSG;dkg,bH'Фh[t'^FOm{yrznm<ύ+RMKeyVlc>8`A'-JSe,Υlxr VPZGUBe;Ր %QiHyD[PŰ 9T B(/M/Ũ9|lL +·dR&!w B5ѻJrA9t3ȋUDw$V%+cs6gnjsPYb^DLXOsNa:CN qm8>_M> HH㽏MP ,M}Q x[!uc-i4!R?{Ԏ$C)A>!lؼꗿbb/ʢ@0 ͆pٞpW盎R/C2>L(OShL-XgzaeI$Iq^r,I…Hx:E-%^! kM|ƩN!%g;'{_n}!x)BX{V&&0:eUBJ6k՜hsvg; }?]U{~ZH){=i !lme.Oi9'pBN[+-zO*lU/-:Ip EsYRP -J"䃊$ r!_11-cm6Xw]=8c!ֹdž@ EQU z=~sm1 5 L@X{.F^","l=Gb/i}NܗH$r{`INET6'v:۽,Sweʽ%SSAz*}nJuAp}ݫ?kWWTHSDQi-s;s4;s8!T^>gg:MBc*(lLNӔqYVU=ð .20+J[T5.h[~&1 P$hԨQQ4 F6D9~.8@2S 4 P5' @>̀u`z8w̞G@!XFN6z: 3yJժ\cH\ Ȩ*teYԦ!2uT=̀*zC P(>`'FҌ{7α?d[40(64};uO3&XM>̀^+hK}`]_~mvʔ:N>bjȭj"؊dE46SCe=R(|pn?FJkT L5od/EPwމD 67'MS)vE !׵26 ƒ 6bAރ1N"Jkٍ?e{K5晷U{~ K+~ }opo0umV0 :x@ '׊1F$Z|QQ;Oe,3Xxk,1o~3+؋knm*D\49zӋ&>9$kE|;yʠH\d(h&{$-ш)FIB>)'/QZN#$( BeIǠ>VYfXTJa%#v},]-i #w{~0@EH)(wC)XbV͈DgZ XB@4dY JF֏YP%ySUU78 ) )V\2ױbQ&4Mbmy' h%'iDx$f\\!Bꦦ`kA9vDaUU)kޛn9MS*M&kSDhZTOߡE )[6 !7V5(]26s%^*tC ):TM3 YQ:ǍKaYWn?,YÎx$B(difrQs&ųOX%wB6{~4/M/&/v: k'_'qӌAS7s-U9dɒ%,IIt 4$`:ȋ~OMI;n|EQL@~ϢE0uTo JKAяrgb#!Biؠ706_g6XFvͻ:%qN[L s7,4Ո4QhaTy!2`?D}6қގ= :H2Lc"% ޶+O&8sz-mlI=>:zFhj}=ݱ4Yy^JA>AN2Dk,FCDAUUI`8p˲rgX4=R R[R{Y$ !,CTٍ(j/^ 0Q<Ǹ %%:ՔZ#YNh XFMʕ+ beȒꄪuijH4PoZgqΠtj,cXUt;S[,q#Xm!TLjq֜"fnn.{%iF%XEhDS 2sEtBG?RP-FE!I"TeI1k<ئ3ZO"!Pq#KRc!П{O琦4N!@fXόp0OqqXMXNfTk-SSS"2tvI03c,*&a Lcpǐ\*&Sd*ckVTCѷ!lhEQ52So8ke!,p6qCp:F喙GGp~ 60eMj(b6kז4M|gaQe]֖'׼5)=49| 9r%|Y3AN>d^?ի^yRqI'qڃ9xy ^/zYlo8|3mo?Gc"ZT[n=^s,/x9gzj^O*EQN?7w\C!ذ~=_v1t㍜wι<ꑏ][Nz W^yĠ[oSNa=e/{`+:(A(.xyӞ>}s_񭋾K$|;]~i87Ow㻙Y4ŕ_}lj'ʕ+{=9y߻êU+xŋ^LSU$ |_LOu?sw=>֭[˕W^'̗t?^ח cb$6Sh&G!Z2L/ϒvzq?ݎ4"%= X?aUJrA%ƺIC"0%R6{EoVۿwdh}*BLߴGcm|HqdE$i*4+mҗ#݅R /wؑ . ⮍冫.GEc_/$s> yū^ɳ .fiϢEȲA4 dd}~0e[Dl~p¸@7||X:IA"?$);4#Tr3;ׇ4XKe运Xk),/9wr8$\MwgT5{M S#,t;P|o%ORp2w(.GcXzrHl )]f衤4 ,gƍy\$:,oqGQY??mq`NpB/|ˑfU sAh I2+pLY5 Qg'vگbQAЂ@ qI,} 3g?q!$ @)̓${oZDJz2n;9:h 3A!3P7lMozS[D\tvF9$\1;;Yz_xӟv4SOm  wy'[̦ʒ)}qa糟7[oJ#jjE177JS"iAk-tgy&n^quU<6шn7fׯgŪXku{J)$h# D,VB&HY%!OA' :l"# MSJc}L#25>lկtR$=,^hwﭵ o0,7~WU`-Eۻa<@;Ne}.e4 `Yƻ RiS~8u#&X,$匋w&ޢP-rTKJ"8 4H&E !^h.Dqb=\Ĕ&Rf7n$O5Y`ZأaB>o3i>;Ŀ}K|f>>5IQVI|u˖|#xeXx܁ַG>1LM`];>'~UYi=fmt>O|j<&IsЛ!I)e;E(푻s_֮]ˍ7,6)*fi?[n9/ۏ=ߐZAm(,wW5~߸ bg'+:ԍ%RPU ?9XCW`ϽO=z쾜r)fvկ=w݅'> f5m=9xFAHVPuw<ʺC7,]-[8˗/g7<f5e9ٰi#kهOaff},{/>3x)%K~kt?lIk&Hp"FL$ ~IɄP8|lNx@H/ڤ*Gא8Ed8!4J#il;a-KspR BM@B?jCfb=Ȋr0d,Pei*CgS֊/`ꆩUY)LM[ԣjbfR""AӝzKȯ4i*:`Y[ElML~z:)w$$D6 >Pt#6XiEp>NOz@ mC'rO˲ۙ4?`aX(˒W8/Q55i#1M Qf7)EQ0$&vi+lHTlȥI15ݢ`=/v5$J#E`;˸kIN4MKzDH Q4ۊ_Z5 "8y֓)AvWUCס,+,X1[`0Qc:nDt:u fffMc&UHiɢ?!AnXkCߢm~A篵 kb|Gp LߌB')AIUX<)0o[kDABƸ1A 9i)2vNк=52ƐHeTTD5i&E)B 6[n\zG,BkAUW(ިn5*6⬶籭]ԥDV5e&Z5p"TKS_\QTM35bikRXYi Bd)1dE6L*[*n R BS5\Η5yo@e9 G'u*0(2ti~>t.f*55Jȳi0VeB"dM,n{e7M|B'UJk:O22a'HuBYE%]gzQY! K3oD<#;K .6+MIBY$IBeQ2.BgfTS-$jdXJq>*)iEJMU7^\PDKlAVtCeL-ގMYFX$cv~fҜf\;"Hx_fxsŦ~Zd*HClRzSp][Jƀ$IqQfUִj$uBc (:u4V%j"9?_Hڰxlش4QIu!{kI6>:7Ht$. 樤Bg)&RjʪY]luCi2=Ē5v"uUu|dY 6kiCNG' IT=(,Ӕ>Ey r4ղ{:NN2ޅ NOx4X8{-XÂl`FhrT Ǒ~L&- qv|5"Jb%b\`e>̠bII5ϕ#iy dJXOeR'q…3ƴ0)%@™)mPX:~r7S$7%ԍC oT &CDsd*0'ttG-'[kzy=?*ɉxKMKӔ,'A*L-*'↱Jvggg"@\"}*Ry=6@a8EkQ0??OXېgq|]"h=$I-uJXI,{{6nű7׀ؠj7u UUa[[13nII$c'ǽ8U`0Ru4IҶmH!# (-,{7Rֆ*ʵ&7^sx7VoYSg\{{.[AT |i: $"ykIZ-+A8pp$^aYM01&r"i'E44#Lil i $9$r3t5i{]F"I-7qo"(ɚ#Ϝרb1UD:V7P+tD| 5Yq2#W7 n[| <6+&Fͥ\9>c)eاٰqIh2%4Ȳ #Os[7bC'c`0 3\I٦Bl*X-/6NY"߼뵍͎iiS1)T0no=y Ti]M(mNR$ YhN4ӛa0JDiχi>hhIsv k1,_1dbaɒ%ئK~ߪ;^8Mƪ&@=^#pٓ iZ8plld L-6x꺦tq$|ZD"cL4O8:E~O(X^\p&(Jp.ֵ ˦*ȓ*=7pDS(_1ygpᇳ(N㨣dBz?@V-D' MYy$ -6ߏ*:9ZQ*"Q97ݛ?7D]UcC ɢE(tN xm|Ek}H:YJ5pHdŊ7R@1bAD.Uc*qQˀ[~v-6ѻAKfpb= 9ׄ ? QЦd]q7wpu?b^}]JJT3$%lЉEz?ۿ VnZa 6yP)'50pQY23Œf4D#xd7SW&ٖ1XXNƠ@xTET+-Ni޻^-EZG P;1>x ^T#p^ӟDy7y$ZSj'VZEw6g ӽJFF0UE!xi5T!<ÚZfٺy۠ Ւ pMᝡSd4) f46F(im*OhDaDgHMSWt<<|3G%d Dpq%$2.$X:i8 Js'>k"xlW\r7H{grM=B(WrjI"%p@E2E&[BhE)ty&2BMIN &MhF$‹-M$xR֩Tx=YAU(bh-R5ekMqE8 Q4b`PU$Ғ{=E/D sLv] E;P`08@E>t҄'?(HG8`(u.2TE-aSF~Z[~}OP hMkM뽹dEئ4Dn' 6bAJzE]1hF3L3CHrX$}|⋹o? Xz.EG8I#)z*N Kv`R53u\˹kIذQJQGKIa5"3CJ!SɂiUTUIAP'$RL2t#Dt$#`%6V1&5u z:[+ΤBQ'i1[4.O}9#/~%!MTa'9`vf7m@i{?hyw{<صZ Tq|iAwhJEwz1JQk=qHh$b#~.;Gr[?ܙZ3B?xSm~vq{357󖷼NyEMǸ-7 'w/Mo:4|h-y#9g57y?hzSO=o^]oDD**Ng{)[y;uj.>󶷽-q>Lկ~5/ykx_? 'tRo~>w$QӘres/䢋.⚫@kՑQU5F$[mHc9+~pQpLwy{tRbZP8Zvox:xp vlal'c!MEMd5J%j/T$$ ` _:%44{~[kó־\ϋ;m{=w}g=$ѣ8qz^r?h8?KN('O/G}p_UI oч8r{7o~3o3nOyc }c|󟧪*1d.Ε,oPq1a9Ȳf8^8Ic~b4c`_̧>OO.р?}iiR Ke4yE8xCcn݃o/~8qJ&{ES[4> K<^CʿK\YZOj-Ը4kZ'%}oMg etNR2hRszOrbđnkaRV|󯟡6Kp56X 2@φl*lՎVe ֯gߘ?7<{zï_{,٬ĉlf;x 7|LA4^s_}# Vyk\ϫo=p-a9IDAT|VИ4Դ. : )P/e/4-7|6%1 8ͻi3< Ѓ>Nz)2b9$X݌c1w2,L9+yo婧FЋXYYa~f:1H-Nh<Su'H`a4M!FK\6'MӰSW-=G4*`t 1WV4T)˚y%LaH6A:t*:^Jèf3eo{O ) ;yDeq 6xxm^t x--.Yy{Kc2/+mTe$ysl6LT՜[|=}/*Vze^:~ɺwL]3 8zuH-mhr%NnG^Z"$?Wτ: Z+tA8W_MUlmmIiTm,[r,e^70IEYóv]W]Gk=u2^]a29yY5kk8y47#ۣ(4m|V1ؚMJig.*@{O<Qà0PI8ZOb2woL}Rn)}<BZ_u7~7?d^rDZ;So¬?f2Z{ {9 sOEuau<)\äF|KͶ]Rb0idNz6"4& ηAU1Ka08~ iTIE ЪOrf4`=\rӐ99-1Up(en3hMN2t̖8(tU b8`Z5}Ber&6QjQ^,n6Izج*Hb@ f2$An{999p s둛fP$ mG9d۹Ve5C~w>JD!,s #PX(5k.A.ٜAPJ|=YV`҄m y1)NVA0DWIi* loZҼࡇƯ_OO~^{U%hl>tI$쀊ѐ`߾}|[%]bhtiUѼ|?Xoͦ]f `ƥ?~?O?cğ_rfk| miZ/_kO>Shuso0/f&gEiϞ YްF N ;| v4&pEI^][dU,--Q4 ʋ]&+n[qJӆx:Hmju2[5+r@r7ɡnaҴ2#mpk{:NފKT&ļIE'-HN3pѣ}NLXG/}_sxs]'~oQ&"+2UƽGn︃ak^L{yOG +'4wnq⚚Qc48uqU;ƅY'P=F۟E(qvTOIZhVZQwtnQfz%fc(GB͡UvyE̖^Nމ%[,49JpejcXO\>:ZQcbWޓm<"g)D9w;^w'k-I55i$\0%Yh'b!3[ϴF>TQߋq3FEXL&߿t<Ź!:mK:Z5{/k+ ޅU%m iC$pc}ekP?fi(Ÿ?t ZNd6mwaYpĘN4_,"*Q/a΁ hR$ cRuEdfeʾ2W@uK{x|A'A_Pbebᐺ`a:v5QbQW@kHH444`\jld߾}(9h,+PS&l6a<lƫg8^Y9TUEJI($GȺ(QOZt:.B"ZjmKmMb(S 㛐Q,= j)2Y;Њ Jwv;Xt^לh=ͩ0e&?9E#-UV] }Ȉ^"15d =,cuѐo^R%whd(,Y m(/жm笭O *lnn2:u&I'3`T ]H2ǒTE0`.V3(:)ٌiĪQxb}NY-#G`^w-@ ԩ3tYi\5tunI>N5c~Qg#NΆsJ=u;R&&Bq4k]dZS5^O/{/ZtY, cqcwj|>(ik/[_ة>V>RD!N.xL4.WVVԭ]ǚpԌ2"RJ-:?sF#8*NZ2In$4e8vᐪF8ptڭjZەPOiC)-L-\RJT$#RfӒ$)+:}y,yLPȖ\,\@f=:k[T7A)y6bus3U*Kâ) K~Z.> @7.e>@_8r.k%Fņ LXU"ow_ǝǎ2ʙom0,2\S(y"4ǜ$7,ym&"F&@tcZ[[[>oƢi1bjgEDn 2fzQ8|ߘElMǚfJ3n8vYnϻNXo[bdi$oJu{RmBV*ڻc˜V۪Qg6KڷN+ p.3ɺY',Og3Zi &Xi7~gɵZN0=` ;]Pq{k94p.d *nF^/uwhvk.K *Um%IR]}Ѩ8lZTR3J}kPJwTW .K 6]?"?WmY()*J-?[yACG3/Ip Μ:p#7A!Ej0AlC!SsǞLE2d'.Z)Rh}EpU)̷RͤrillQΝg(-b3)])U=:S@;;NwRspܢ 5-YpǑ[mGnFіSV2Y6;ۀFP  jAգ>3y({D,ƃe:f\Ѥ䚃at"#xMPR1w٨ku+@1@9j gIJUDPd h_3H VXup hmm_JۃvZ,]غ \KZsY6s }rpq;Az9ŋ[UʡPZo`umw&s00|UFw꙯G &o:W^.i-}[f5a G>%`âeK)64.4;ݥ۞\Ղ块;i*ဦZ&\QeTrh[׀e¼Vz!ad]1+ əYG/ ɦu4T=9e/te  JW|be 9Z>߱R eH`B̅{[KI}fė1^tgR{ΖnOU9E/D,FP;^)FL+M:+"zcY(Zf ǣ$q^w҉CK`,l3zRFitWdr7=.nˉ{ۗeY>xi+B}e ҄@>裏[Sc]裏>ثlG>裏E]G}2n}G/cG}n}G}G}G}qF>0Um裏>^>>裏WV z,bIENDB`l2tpns-2.3.3/docs/html/images/vpn-deployment.png000066400000000000000000003711371400724550600216100ustar00rootroot00000000000000PNG  IHDRq IDATxy]E>UgvwaB,aaI*8,@:$"aٗ!AB0!A6 9է;y>s:uz}}Q@,? (PB (0Qx l<^@(PF (0Qx l<^@(PF (0Qx l<^@(PF (0Qx l <O$sZ+dY4MUZ_#/1'fϾ,|_үyR ˗\-eY!k=,|&? o=߷"@e:q #ZD2%R9fZW(,˴aH$c D(IFs.)I""FADZkWE\ץ~y|` J sr43Bk-pi `f ևIRJ. @AdYf%4Me282 F7 1"o~4B{>K? |@E Բ*KR>-O9K &Is3'IR*${~m@GТ,dh4JRSB0 1B=5>1Ia(#Ͳ^Ðik?&g!?ϲ,SwwwGGLJTy:ڞz x"64+$%OFO_3c0.BWJ))DS2x򼟧Յ}R^? Z@\Zkyph4{VO,?Xߨ@ŀѫ wDVJc߽ѓZErI} HѢ21H4 4Ͳ,|ϫs"}C^Z"d$RN"K5X?^Va(( 9'{,"5^zJJ)DP |Hx^/I̞='֪8s]wUK.ۙ\. •DTׅya>Ϣ5/"= 38ë̜9SR*I(8㌼rϜ93oZ[*F;nB4^>/{Z_x=qkREOߟIFk]|ŷr[o5xqyG}ĉoa~|JNxUl.w1^{_$ 3JLzzzNo.쩔3fGZFyvuu1sѐ9tu!}9{7? @y9';|Ȑ![o>˗/꫇ !/ _a$6 9-r΍? /w/)N&Ƃ )K=2EokR?O8G履~TG}:{Atvvo[Hcv7|_(ʇӧK̚9YĝTZR1fѢEsoUO~\ Y'G7xcOO AK,9F!n!h܉jcSׅEkLDe˼4MuY\.{OcpKgbAmoo?~EY=t;|Zyn~d|>׫ta{-Zvr)"s(qB0{v*k߼G?ќ9s QJy83+g~m Çvmrm^uE͝;?A^0BI=?DRzqU^]?C?q>?hF>?t/3Wŀq{^c6̳e]&}{29E-{aX׷rK"*Jڻ y'1"c8"J4Mbz6X?6,$R·[gmRzݻrG1YF><:W2e4^leQm=c2bSN,&jw]ܐy/N*{^-6Ry8wU JdjڱOx$cf}~/%YI\y٢e ɻ6ȰXG<3c8(D&o}[Ƙ$I~! 70v#F"3D\.bDywq|n)ɖe˖yb3f,^Fe~_}K_>'\ N?tiͲJUҩ ]|ů~ÇSsR܅^xYg Y{'(8,#cu]&Mlͤkb\t… W\~oJE.yW{gy@{{<ܹsƏ?vؼV^E=]]];Ӟ{9x`=f!8_JB$|βLJ/~ _8I1c<sZ/?-]-ˁO:uƌY]s5r)3sLfvM2s1-Ng-gje.=0ٳ>lذN:)aÆ-^X:h"Q '壏>:|pJ)1ɊىW^׿/J/| ~z\^iC=Vt֬YOx_ u]ot- 2D.)J￿1c4E~;?8Z|]vA$gc>k֬ɓ'J%sРA>tz*J^2e Z+8z)2@SNy'fBtA~el֬Y[wuȧizeI# &]Eٶn;L>}뿘gܹ`0i~2._\qL0AԆSygƍ`6:G){Ǐ_t)3 p衇T, Àq%~B>e<رc_:,?-'O,oWUfKγ:Kj7`a8|f5k/lU0cƌOZ-/_zEdo_8J/ 3c&O5$+K/mRυ^(̘1-7>g>#g?w}{. Cxǎ+gY|yR;js_{u]o枞=CZ.ȅ2b~s>9ӤL^ӠA."k*Tzkt3φ>&Ƙo~l_/$ǦMDZ} 4 ϟvttțoeϲ7 w/9ӦMUM~o3&20|;5Jٳg3+رc9y1ޤk{\$iv8תCT`a8%J&$yyRSO|Wru1}ٞ}:Z"\9sDʞ0azK[xL7%-[&_e]3y~]|뭷o}K3ΐ2lAѐ_2'|(|e/s뀌l&o?\L"}X\դU_Z3Wh~ߊn6mZF}ј5k\>UW]qggt / Ǎ~7^{EԩSaƘnAJtI襪 ^}Uf|mңZ&*~i =/|w}Μ9~(Eu C` xQFQz뉬aѸ˔Ro… 'NLhв-_\k}ar-˖-[tQD iVVJgCx㍃>Xrp_%o3*IK>hq`V+[{c̺+:ٱcz{/$s?Od( "S]!z)82^|27pI'%ٶ61ZnFCv_sXnF):xҩjJG}TS+:՘1c8.rDM/wQj2#Ⱥkp ٫^{?IS!qWUo>cGZN>]T믿\- zѐecf 7 c5dYhmRtP?X7hc9Z~w^{^!gU`bHy9ʼnBP'nᆕ+W^r%7dS̭(њZpԩS'O\% ލww>%Kҋ3ftwwkxWne~KNP2qϟ?_G \zM4I,7pfϞ}衇zw:Jx_)$bmˇvҥM:Ux\tι'x-#i#rl)LZwyZ.cAj8 {9v}-RT+Wn4M71d:ɻ]r!roɊ+g9y4OO^{Ͷy[ˑ}?f2e3T&,Hb"F_g/-_\ԵZkQ3K.:(Q#2/yCߏB)%1fƌAnG{Jh^fGIC)u=쟾o, .&Պ_6BȗC .1={z饗nn[Ce@*R;pʔ)^y-駟=9^d 3jkG!âdMƏ//_~78k0<~ pꩧ4ɓ'뭷}j4EKML.]|cǎrZwww #Q; @ZMvMTaŋtA뭷rJ۶z[MƤkX~ձ֊I[[nřڼлW'"Q 5jvꫯk9]xHw(j­|-e!AjpV4?ªZB4rLjdʓTn*";X#,ZȫJ?[rVX!׊_6XWv޼yk%\r嗟p ^{# Z*헪鴷K^x/:BgϞ2eJ{Ӆnŋ˃{iO=TYڻ'MV[ 4(<9/</r!"7Ͳ;V*w}) r[o!c-nZoZk=ڶ6nuT*F"4Wl (jܹ+pb"qsð\.o&yRD`rӽ+˲AɗV\myEщ'(yw{zzvm7YeZk^.KAâ 78zhi}xQiJ!iޒ%KK/I /pw5n8r+]p 'eY^Z.yjEw}. ĉtuu벍xwd7|ѣG{ &1BO<ѯX"-\9nENW#畤9q}~kVn `…7|ޞI=mBkZ'ԕRB%瞟粄ȗ q'ϮKjo~ARE;vZkTO< yp@{{;3/_nV船sz2w}6lfM2eڴir^{%00s'O l-hi) ǀq?D;Se C-罎^3@Q{{{g^xu6Z>^guwNƠgΫE-xFV[ǏodMnO>ٯ1\ppO>}ʕo1sOҶ/\[o)*YUU}qǏZI]pB›"lNK<uV3hiV\)rJH9w'n_'t}GDwy\pGJ3eSYoF~|sR+X'ҟ4i҅^(H.|箽mVo?OW^y9s ˡA*{c+"L}wV뮻NnJD> Ce"Zl[ou'::70 /7cu]q3:;;gΜyu=~sx㍍1G}%ʻO0&윛0aȑ#O8,htvvJ0ykepĉrsQ4>};,C;N_˿y.,9?~r~ȥ7|ӽTe '\p+wԩh&M:SG-U-\pذa^!{Gʘ0o{};;;+ʠAm{XbȐ!_W8+̿oO&Mrva^\guϟ?aÆa|GǿҌC9+s vw,X _աn8q?V#J ԩSwi3<3|g̙_J1#>4>#,M0͛'y(GqTc^2q' 8q믿~AySx7 x2:tC=$U=#F>HJѣG>$o[[[^r:iixܿm׸@LJ:BW_|_e]5 ].>^V BrVpKx"/_?q]wTS2jZ.Cy[e>[s矟3gΫ&tAޭ@gg~#q`^xk=z1ct+; }qx^{mԨL/Mx IDATQСCRȧmĸqO>~x)厙E.My'^w}(u]we1Vն6؇g.nednW^5jhl%D.X` ~x]]]#GZk1sT]^ziر{] ']dvUWƍyſ+X|M7Tb{nΜ9Z &lƊoo/++˲O<+TPrG˼v}w 7y睷pB̊N\nʅ| cMqYX\/gqp/S{>;4M^|=m߬о~*͓@f*pڴiR;&VQF]~M?jE 99A5:_\.uS^ Igl'e~q{aDhXe4o? m+W>G\Jd;-%dՊS]~JBY.I9N:Æ ^xaܹoiv맞zq&N(FUG~ D xB 3g˭E;-3<.:Cos9gСַ#?_ࣆ뮻{wńdk6|k _^{UV_l~i4JC`=/4Mo7|;#e{&Mt>RA N1uΟ?TeG/[nyy_r%s'EwMY.Ѣq|!_ ,{C>y%FW\q׎Z5sZicMFW8eYF &7~}o-:th$o#<2lذ+bK.e04b-}R\P<>@ %K^݊0ga/4ky睽{Σ3BA͓P+W?箮(yw"̭~v„ J8zT.504r k* 4Ka:eHp:f:~(-H\;cLF fX,(6pEI*;GP<> rYf2V`%05Zq@pQlZ>js I=¢Bkѕ? _Q9 dfK^&P֌# l\!|\:vQgΊޑ 4K3A";R4)QXP kiodr$M⨕s=IvIDQ FNNS>1@fJjAR̿l5pN~˪{yk<Ԣ[[6gѽ8 ^` YW Фe2X@| bf>)+@7$0Y\κŤ-nѽt@ry SmSߥ@@_7hgsxGf",1=InʌKjK=/JNקI(H2 @(ﭭiriZ5S"9` `|ͬ2 8 ȁUo&Z bL9V\"+_ (C -L+l.$Hj `0ȭRד(Mp`}Vb.mA}<@DrJ0Ek,Qv&:pX7P@E⎑^rړ8#'լ(KW  <<^yPpr• 4C^VswX.AdpUe>޳X@5 ~{r19 oʹ]HVT^X$@F񼒻OoBh~͙Vѷ(0QVE_si/շ(0nFVIS4{/cR}]A>a/P`MAN Ԡ@L(Y 4Mշ3 gF)(RpRL#DWΘ RPYISN Ȳ% !FEE0Ӧsc#8*4-z}POr)ل:f&r,0 IFz5*G9Ti@GIKsY 4u\J8!L-J9Qd&`-!IP*VCC";v)ŎSBP"Iȑb0Scm岳FH0ˬ֚*(l#ij@12@za$A@HҺ"A)`m:P #F3nfAUնr4302QR8VԄ֊ÙpZ1H`[x5ȁ#ͬժVqRӈR#ѕUލDpa$I]f8ԲSQ8a,RS !X4O(I#BuE A%8 8ksJk]}}6i(Bfv& C @ƦDIT(a{ȁ(0ZXQSh?>S.6`VPnƋ_:FuVwqq(n6mS)TC4uIcM\5'Y2.6C*Ӻ?]mve>  h 8#29 p"\^``9WYFa ̽ϻ|Ga&P eZRFam G̫j=J ?x2,#ВPɑV28Be"&MC_8rQl90)*q0@\iHY֩<_v+8y4n(jf㰩܆#wc%fR:8*YoGrcw/u;u)+le/7˕=굍>SFqG҃G <]k2Q L(mA@ax"b(x' IH'ĊA%P *B-˂P_{N%=ٍMV0d eX;QXN)#JZt5slܘKednEs=_vm1Ng3VTBrPҶx=xμ(*q9I>;r Z|*42fDOeĭ Dž D1? fR Nxiu *CD0FL V-ν7WHClq #͐2!\؇ljA0O _A08՝w*ҘBݞ 83Nʚh,LFZ a9fy z7ׯ;ESfe2(9TY=K#9# *T O2. ض zs!pϑ UPyO _%v@8c*P*emnȆ:02ұbYV⒃Cb0|^7 q DT)iU7/#ڣ@;f=[)72mT.RD1Һ.٤ tNJ)R*1Y^JxQ-g٢EWq#Ŭ j>(x'E5, %&[~RiI9@qTZWPd3;Pq'fLWW@5m:$d5ց]#iVκdHW3BAU*(7 G8-r. 3]8?w\3D@$BJudtK@PEBXDܙ.zqjI4P.8j4P(%z:8 +qn86@dM&6T[5!U55:!'u0m?"$ @!B=Wwwz6لJWO æN3[ y' ݆%4\J!Pm`F[\:$ll̓,4*PoGJx<~AvGMLy8pL+r@N'6kU]ƽ2wE%TB:0&%x& Er@DvqEshՓ/FklcZ *`BMep1no-MBw ǀS)q@J]3cN_xx`MLrPFȖ +ѬӀ 89jj0 Z8Mۂf `PÑq^cO euW1  _>(^5QT1RpeAxP@# 5kk71s b[*QCvhUVYBq) HE k㠽^;o+厌rh-J'ū#ֺ͜ѰitD]wܿQ`L{$\Y "dF3131e2VI ܴyֿ|K٩ɡ 41z4LBpؒ{r _. !,+U3Re$ۀx λf0T mbpolz1_ |Qk8Xk].zZ픹c B0sjz!4AhWBoU?LMOZ/+Xqft[SbVKsϝ~O)uSu5d@PWYA.5&˜ZDeT8;~xC KfB8 gyG[Q{WGlrx={׫E|?-4/kKZ֗ ȑ̼F.ջndMF!8Ka6wnjY=je])1(3pjk{ Q *R܋#8 0anTڵٞ 2i?%3>%i{ҽ$_;U*`L_Ϙrgc՞LCA$qH1KZNծ^LjBk+E=mN Jjo4HFtzzRmI(RFH2@G PW` -n&2iR.UjU)Ut. /\vG~~0PN3.Nqi))Ԁk4%M֞7ǖ>CxXq'(ΉT*jm6դʙTJZc\LplV>^eA1ik ƥI[ cB,\\WTU@kXQwϻ%ҝ] E=X]*6ֲ׬z@י30jH 9 4qE0 V8![(U | ZPேJ Aka]BTfJp+4g燯Իo J-(qd*Z\2XaVJI8KVQ99pX`y%"5gM{iXwJ.g,Uk"z;}MzP&uAL(9{LhZ Pb@#דR9K~zj& C0.UtEt na>Q̦QH%@PiH*  mvpQ+Im׍ҁS0 y.U2JaM{%W0Xa.'cC*{2lT J} :M,lK+:@~8:,ی"ʕ"h^R?W'O uҰRŠwȌsfn `Rhf],B 0DQȲr%իmc̛>Tg[ ALN% @CVEPFW3(Vͼ?RH(b%" ADil#9QY7/PU3d$Nq+#nH |(^(VwkwJj7g7tÑdp$΂(4,(pp u5TrN% (bGv;̛&  ⨦ÁǁB2@b&W  uv Pdތ (,`p 8!dN5%רH7\f%~7;\O\!ha 7= ya KӣΙKps\6b"VIU*aeƴԀU3x H d"\Mr5aJPa {#>zKZ2(I%6˃:VG7z@NHfWIXW&žo)嘂~BPhxXވ6D+ʕ0odɹqrAE 1g5 ZZ’X)r.a-@Jk`T,S:r4%`,L, A&v6%R7J\= RdġI H!RH2BE0A\+kRC05sIF7JA3IqxI(ACiNbQ ǿFt=WwH%q3Z̬#AWKi>rdÒsZպXEPa@չ66PCYU "[ZJ%fvp8` M@EJ\F \,"]&IBf:ˌM(PJl fc_ښYDݯ>znV 5hJλ0w+{Q)q 0twt-&%D3!ȉB[j{LHzn`(n/u%Iϐbctr(űd L!QM 9-~Q-IX,3a;jb2 z\nXp@lE*J:JшAH+"k@3343JT&@X j kH4~T I&E(*m3k՝)^Rζ47YmWC_k`-5}J/`ם@nĜ9x" rDEANiQ103)H3PA1; CdlDg݀sPe]KYK GUr  @`ĵZ e۝?8amis&kӥ W;zVS[u&֎  2 I# fG{EMM ?rWr⚮Ϟ¿AZv pi8A Κ( Jg8˪9zU=HZP 4 )? "!߇EƨfhfIf衪ps8ェF04i~U{߫u]g{x"8Rt1pPq.IfNX{a Kbs{Q bRPuY>ŦcaފpETDF&i+9ȫ>ί*Q B8@-( # xշ/ІY9 0j=['jia) 7VZW eYqMNi\zD{FRLUʒR' Rx &iiȔ'}jR&Ro]tʴPyHskO} TT^AS*r-9^"/oG (!7[nmA]n|#$2m(L訌9-˧Tݷ<[F&3{>Q^\٫5iZcHcٵ]Np9G+\fE;%KMZ#aaJy6ɦZ B,zok?8an7}R(W#7;-\ GNZWBR@#$LkZ3*Tx|3@%REyȠgE!<'.  Y6>8=qE}r݇sWj}J|g42dr]vƅ?׾o?⛠ > Cqwҩf|_*F[aqх9wm1wӭ_;2z7[ĉ-RPҏ,0ZGE.@U*J1KVJU.ˡjZ!/Jz~`z_%RLQ(v% 36_=7W.${ߺjW.Lۯ'󁹳fk-ׯ暇6 f>zm Hbai*TX~1@YE}^Fiwz&az,ӷLp< )O/ީ/~bcaҢǯ\)8Nb{\ +_F!>w}X__Ꮋqק=»IcZqnSN~xHO^tp{ґL[ mU)/Rh=x9/# &{k\EM+څA5LDN]Tڂ=VD ?~$I chZrE&/VjMYգf=l08+S;'V`lk=39Pzkuh˨̲1ky^<}vj6w9@y؂ZsM*ϊZo+cڶĊH)oo>?xugFv,#h&\|圦"  zqBebw>pO.u5j.(I< m x[VQ ۟i$"A<+Eg]xT^XSNZP[7Ԃ$J%؁d8MH@n$~#ҡqYԑbVFQD[mӓO&9Vlyd{OԜ~vUY[m7yϞw)sQoyN~1w 4[[xSN k^?[+/=+no6PWݼ>:nLٸA0d_ @Sw_+f;MOo{NBv (k*<<-;'s.' ɩpYxYn8#1~@ 1WdooPZ&D+XP <| e׼4j9(^bPw ux1|bJBN߂EMB*L;g3A['[4 n+/__jDg c0JgFC7~|ss=2þ4_yOGlPX8Q|\!!^xӢձ.EeZfMPxS$[֫Bwy AT\Hb#%fKw?oX]b c?׫AH_mMj(g$̃OH| 4]XZҞV:++z2ck8PF)*x9-)Uld-sGh0ԦMz2r'`Y2 lxP^وp рfe?ڿ'eZ<՟X5_ ̬`?sGݛGH Lސi;9UlUۡ/c0۹Č4wZɣ(#Q3z܍nl^^rdZs'JeW436{HwB Hߏf-MA>%7&!S`4,IN/Sl^aT^ ˪zšG2+JؖecvFsIeI$2eD=R wLh@!&xk -$:8o&YUj𒢪)ʲL+h';lEF@K5kE)x({aD xweTh'_@1 pTbXCx"lP%]W>YgAQ ߏ"<:VšEU/S BYϞQ+?/%b-ekE/8yAL^l6,·XP _4b-H33,˫~vW#6Dh9NCxQMUкњ^,oǵUEtbRs{7܃ ke` h?-#I .T)͍B[&"x´}.^潲N3fAس맏@D; `]DT7ђŏc|WnEYd6D(2@Ć a͠>M= @rSO^ ([kHgJ2H9t}"Wכ7Bۓi {b $x /*_@.R͋H`tRxozmU" @<[=45289#ը#!ŝB;K)F"͔9_ Cc`p?R*TXCx|]'qbXKQr5"NTɶhGy9SjfCQ<+RJ $5() j=4;3GYWiQ6I$K;5U+,6dtǏs|<)i65fdY!9T<^%Dk FC2s`]ZkKІEZB5%xX%%QY/ƔI m>;o}zcvG/uDfE$0B ΠSjU?~]~bAWa!oVz|%]ʬpYO:&BN4#yHq4K&dYƺ&~ GpT<^a&z|]'$}!I%XľߜB3GlTIjFT/rFBPL e O Z4hg^CpG]NuW_yq k"JHRzFLM /֯XC*D`}D=n=x'{7(]I X"ս8{\eYyK "p ff`()e/|[o,K5YH҃ā%`""ͥq#Kg rWZ_zP ޣ(y!Ȉ x nwseTRҢWņM۽% BlQd*_Mxć9D09D /04C$/P)8 T*Yq~N$ "m;q,Õ钛nsΕDUkO?GT/odQg-Z7퀛w|^{wmWU"\ȑEQc3LB\}߅YaE[fEʔq5_awn)ELL ikfQ]$B(ctDd@ oŕ$`ZoF 4@'(@S̻w{&R(m怯{ <]rG]EhZsNQ8nѨBWJh6\F˲,@>I{nw8.|F1}#Q"!0o?-Vs-FO냙!}8"|*/{ VQP%) 0+Zj8skGZ)˲jX1jibblZk8Q^z493DRfv^y>2Rso}+CB_+‹2p{'"/S&d6?2_Tf:"tcPϮ8Wt>Z8/wCZAARPyABHK=& .H* IDAT~ݰt:Y3F͝+ưWNZ_G1Gg޲Vfβ^z ka`8ul>=J?o45m|#e^FHqCQS D$e+X$O.䚯3[3:mrҵk? 䍵h<{k.Y(tؗ1qY:(iwJvw_ekf!PU 8Exe(("k;C9윳j<ϵk ;cAsҭzm$fm6viٳ۽Hb|<8gs_1c>1+z_P*/s0>['xr}63_^ɯ%u{qmV] P0ha= VRX&fHȀv/Bwvk'rnKf""-nʴDw'+!Z.(I.13Ҁ1,'gR࿒=OW~.'>pƩAYZ/VXW/ZtƩfY\}Dh4ʲ%J)ft:VX8 Vx}d(XXe#3fl6+VDK.;wnV{ οšC|Qf҅|gqrul9Je43zw5s(z㎜/v^ݎ##-#V&έvynWr]/6͏҄*땊g&QV "7Y,{\٪LSꗅϬ{'c>DBCO 8Cӣ8:c:ٳ^RJDz͘1#$yR7tӻn++VDQt饗5@dZpg}*JW I(΋ƻ<{wSߥԼ5Zs^摎+Dgy(e2 <>)ÈLYgyg\xm5,7I<iAD"eDEBn08/|x[;MX"O ?h~P\̜yEqu=o}G= Shv\T렫yڱZ툣JWm9<<`9hc?h4,YLD=m\ǭtuWa%\y\zy#Gv[/gM{ڂh8 kediW)+>_CunӷM9ÑA{"cìHy<3e4y"E7v_l4kkj(/'{pw>u'o 0i`%`@ JeYZ͉vO_hg}WimfsM[kzDT4 V;M˲ LYFG\hdYVX=VLNnϢ(CfXjZsQy*0 ^םn&⒚)F׭Ec}-~@* &r->djQ(D,WyaC"D(2ql6;nYϲX`TVħ_o_, hn0>2 Vٿ{^swg%, k.br@{b@(:N$,˚fe`N8aY5aN휘x&_^tΙSO54MO<1wx`ttιHQylԈ2D3H2yĪ̑彤I +^,P`L`y-ʢ(FGG&';r;t??-oY=P?BX1qE+Wp+Vwy Tʼn' O?z9昙3GYgZ{q)<nc&<v*JS`*= ap`A= 1S-CC*/A^ݾ`Vpl쒅 ͛r!4b qy(~Hkrýι(5,ˠ3sns.4MR!0( Wֺ( ᠡfo&q\z7\BKPM)<u%\U5wXa Baǣ*7$C^}>;(9DEV[8dm4FW̚j6;R*+X~+_Ji>v׿upň7n522ǿ/~뗳³ +k H5l qL% 0)9z@֫AS{eJ Mz`D ` 37+`$g GF?s99P[ǣ(J;UW]u7_wu\pA(4=cV>dl6O?Wk^~C9Â.a Dgw}݇J6˲N;mSO:֏=S+VOcAcFt^P0BI~gCKMt 7z}C3f|<<( Ѳ^/M$I6pC=T)~vhg ut6凱&Y 8N:V贳4xpy6o$9c3F4|sOX©`}Cqozo~\eGu!rg >ĠS5+f&UxUbڐ X8Y;j JD`L`-уD0@wJ$J\>A 惪|011Q˲t:q_zg~ze###aS1A'fIDs HRAjt:$8xM7 o};4hkm "x2lx7[tFGGCC1o;NoRlI'&&{WL/=c .tjIe##Myի^N#˲}'Ls;?ꠃHӴhaz5+p .Dne B,r e,tIxC4$B1w;:J %OȂ 7Tzi@.(-DQWQxM`y` H{^e!ctt(,f͚$cN:x$@vǡv.m6ù J)ea0˲`(L>O5͡8JytW#ַu'InwZVfai>,D4o޼ >cW\qEVXsx³XO 9u (1훴PvO9л }K`?xTgHH4O+Tji?<7 ]M}< >`?#EWU׫kJ9~_=ӂ7@a83q|ȼy0Cx~HhI{t@p<PY?Z뭷zbb2X7a'3!7;.21^ӟ>XeTkzu+1J~{H;GHԔ66iJKa'6ÇّEp ^U1ÔȋiշD?3?u;O(2^N<~eTZ_ ?z׻5::rD@GZuXx՟5Z5{/ZE?A)6eX'f^xwWbn$Ip+ZH{[x[8N3::ڙk" ρg >1&McՇVʋb<;`Շ(UnS`*VY!/=cn^olvnWNDufsbbRkk?~/}K'xb8n~Ŋ;vi',Y~QPz=cL ~p e˖͚5+vL<-[f={d(}cO=ԹOOvs{':η:*˲F#{nv]vE]SO=\wu^ziQK,={[o Z;kh8 kcOXPŸ1tF<"a؄bZ$UsxǛ?wFǽ^52gE:…WFF97~鬳{_u;Z_,}\G>`,Yd7,Zhҥ s- y睷y7pC y^<Ϸb A^%q|?as j=ܳrʠw7eYfYvO}{gϞ̓~97gΜ R󶷽mM7_wܱۇ6װzY\?͗;j B?<Ccg/9眲,;]Z%_eDZ ô."QA8skhZȧq3_Q?9l':&''ͦ8H=de1&tQ` 0%d m0a5,Bqt/mB*< #> J=Ns!9$dNzn%\뮻|٬Y.5k*N3sh )8pgT a)ӽ؁ndEbٲemѰq?8CON'MhdYڗ1z6`v^ott4,sph4B fy"vF#a 'q*PYֆ p,E"^l6C/$InJh>L5j~ytN.YRU I۲&'&7K7pVd4[k;(MF(r=Ds.lc!;as] {y.AgInUJ(bu ޗuӴhyIJYX0nZ/  AE&Izl6&'IVnVyn C{h3t:CxC7h, iPk+ %pPˇ+Nh4BVaq3964 }ʭ]xz bɲ,I8紊Bv5, \)zsr}w\{C7i?ceb;0ۊg氫$IǃסhSmVQxG;H(fE*"MEZ(EoawڽvRi]veם,bffu)xO:Db.?Z$2Is$l!גPqcZg ϊ+dlI-h^wdu)_Ցv+m`=1O.u:Ve<כq\ g:LOc={8 *?zb݁+G?+DTYk[ojD\QVNzof0s|8, '&&fΜdEnsf͋{HQccNW8[z^׃\ᠵ D5 6iM磭O>y޼yo4MEHeYodC!`Z;Y2RԷ IDATD{j{w}עE1{|im0E966CY|Q8bٳff?-)J鏗l+tUJ6EQT*ֲ7[qyW$裏מy֬M|%1/)twWXk:41ywZkPdE*X*nZyǕJS\vJ;^R`hwYx cLӧOo6l9Jڇ,ү_OӴ! R٧dvdCґ$^xa}jYE2N`OfwWhtuw7M_sU.u8хss7ޘ2e B8I,˸r0gZeQ4M,8ܲ~KW))Ptqj "Z-4T*0:㜶؉t2V08[M|l%76. *l[3w2 ]qe윫V\Ͻ{i=X,SJq8RR<˥Rnm`%#2>^RX80MS=gXk՞Rc}jU+IϨQ;k)>^vJ;^R nY}F#M8yIWk]T>Q++{>ZRpyŋMJNiKJw|yk`` I£>* !vuW>DD@)jwigʼÒSͪuɹ>ח;fYTZVj5[?;o?VfsRGiKJ6"\hѢI&YBp,.\x[kogy3 HyO<_lg2dD9V͟?ĉrt+jYko:5({=y*j5Q^%?>A·a! sCeg;+z!ɓ'O : @YՖ/[RV-<5iI *DZK[fVuG)>lǷa ' DA Mo\V:9'zjeq+_(zNj-R*iZB4E!X }dKG(ZuuuuHxb#Rjl6k1&rOYI)_z%n7jԨ4M-ZB6Tz^u/Y8%\,Y;,js̙{AJpFbC<45snYglݩj;fDѣGgY/Y{`5Qd@``!(U8>͌VERuR$I0ι1cL49gIy#"lV6h} ت!WeS":זO=(>twwy駫z^Q:}QuSQޚ {Y]NѨT*YUV)"fYvwjۯ1RBvT7Bd;,6̂oQ8O>(ƏGvwwO4'nm-Yd'ů.5;W_}cv|JF.la{ѕ+9i])U ם4kǼxwN>ߟ4iy^Ts^ .ePIS@Su}ٜ={vj.W_}uB^- ;zv-c J??ZG:?>r$]fsJ%sD ϴֿo3fLzɓyIժn=z'_g>E; Iupd,aŽ{"JzzzcNC=f)vXIQ[[vJ;>rD)94E6f7XC-\.3\t+O8㸬q|3gN0W|<:곟@N*!B|sPh-c-[vwvwwرcs(Xq(뮻._|矏h̘cǎ˦NZVQc dMv|±vW`c[9`I4M7Xzm'|7. J?ϟgN9;{}ݹ瞳Bs.cOr$"@@`a===OZB;w{i6===&MB &]wm]vi6 W%[h8V5j8gkܖ7-L![wm$I8M?5kV'˗.SB&q"vqmøxrV&Ie%:JJ 3 ݚ~y8ODW_}>w#jO=!ѣnjv.jVc{/mk*ߒtPoCĜ%pNaQ,pz<$IsEQ!4k6g "v[\eιqc(UQi=:à&9@R|8{_!;-y?{/A[>l5+bF>s+gUI'B@D"na0ֱs(,k8xߟ(V(5 @wO;8kFjfyD(@Ph%&oq0JVCѼVRJuQy}1c>jFq޵iӔ(LՏ{g-R"竬 "h~B @@Pt֒׏kh6QiJڔ[9uW~M'>P($h68N%jFiR Q$y5aXUJYVaV˖-֮tu]AY )F"v(p|?3|0a`W7 lAl)\Jl6q{;iRJ-] xZk\Ei.EQTeډi Hub)V:泴awV*r(*%o+\Egh%W곽YE!Dqhsn٨T+Y#bʓ$)9ݒS@ @" 3FY^p77?ەj(eiDTT1: y,Z8 tmV+"c~?BT*RA 1d",hC$Nw:Ӓ 0S@/{'͐??C>"RJ_;_y{$V ~?#\>2_z…Z /Z{V*"׿:SRJ{SO|͈O}j \z饼>)"8)ڜzN$P腄t\XTjb ؠuhWAkm DZh,`clFǰebk? g衇}2R`\՚[h}wEqOGѸ>bNxrO%M}=k]@ p;Wz/͚uG T*%)x[{{{$I}/2nܸ($9o/~Ǐ>}sSN⋻/_rn8nZX՜wu端,:śF;lui:f{&oeIS,X`=&Nxwr!J)!D___RJp( tBa\@z!' }xrPa1N=l^$@*n /zg1Z1^ 8sBWmv| LqQۏci6GUQwmsd< J6YU*Too/(%t-Y$Eq4ac;&~Vq'6M<=IǏK>'|RqG|8#ӟ^p&M>!8|vc7nC=$ kGQ$!4"RD#GM=3}on;}s.d:1)qgYfThZZ!@%VVs+Z !ql67KiK̨2TUlFQT׏> >K,ygva( Pʼu嗳97Qy+#la8wi 3={s9}hB7kTvt3x|["*am;j J9'Aeq1HӾ>DdJl47w'qz뭿/v}_裏p"zq=n (sP*`IӘSSͦ?^J?Omƌ>nz(7Id/wi]@>xbAu~߸oӕy)cBozO3KJZ=X|y !AHf-\>%" !Bq=N@YP%o kSմl.ƍ;s,Y2sm&5[˖vIIlY`޺XG`]p^K7y;/DJ'^-oo}]'xɴ)SO9_%B3g|{OuQ\jl6T*rbq~I)8B[tYM16=Dևa뽰! ] VDTЊZ΅$T*5QqȦ%o !EBX|9,[Rph_7eifQJ9{ɒ%s _ƒ?{駞|V@chRJO7/.|yY~o,]ꫯ>'xbՌ_=O?=a„cǧzj``,)_~e)e2 Svu,8`=RUZ1vQm^ w<){JӔK^=/gQO#d'"Y{'IIP@Dax)@_ןyW5{l?裟;wޙI>x ;S.QBJqwsy*Ҷ0>DMzT*wLpu7v#StMpsX 7@:N gO~zYgYk . E[rD9EeB{>U"gި@@O3&F&WIB`q1D^ XnCVmp͐C BH <"E*l>C9(%+ +qYfqjR8mڴ'8ؙ"{7pРɜLj;OjLry9 "kDQeq\cD V:<:Mfk<˧ι\7˗F\K.^>k~u- L\[YXc[pM'"nBa-[y;Ms#1y vw y;S!P )%;_sK:k C*l3|ƆMgoo/!~jX)s\Bh6S7!4{uk(ؑgD}竅|tJiKڬߥ.}j.Z w:<{Yq")T__~,,?҇?yHZx9ݸ( Nrଏ89=.o6rtWVkEa jsRJ!e4(Wcw#qy+gAt]H㤧ZGc\oZk<>|܎9s!!I?=k6jԨiU]aͣVuGm %LVghsJX㕌(`w(C1j{6T]1+%$qPugY8ZX;1" !PPيאRj;焐ޑs!M98,NDJzU!HJJ>'?ӵJUovEa%==7~Џ~`4DUt4fVzbMs/cszk6z}„ R% xpO[N*pB2_dey{,Ti/!O6@\QޥKǍw_=Ǒ1f6MV.!҈2_e+ }|s" B[{|`Nxa7Λb+ sܜO8rKmCS9m}xS΋w`7"A %q f̘qA}kU!iS~{c}#4.$BJeqveT9nAB4\aTRʹW̹ z?8I;AHl?XorI Ж~/Y:)PX+8ⴲL9 ҫ"âXXg&#(Сd.u6MֺjZ,ME5G}~޼y?shQxg>_|%V2z2Ċ֘^)dM^HM#^B@nDB$M\ $F j){]I~{s̖4,a$TV $B8N9/2B\qP/'8%!}w{G.!lY\ P GK Δvd5Y%J|DA@\o>`FFL]dLqДPJ fy IC;M4ZcP(qkG5(ĭ,g|(+U{v|Zۑ*Kpt6e @:v?/߻i|᫮=tP@JkUΨyww6ӧMFDg&sq|Ew}V8P87VkK:/)Y [(!R$*QdytI(,nS2eH;%]!9s~w"36_EixX`P%fsWR HH~p" /(xI0(B"q]{-xUqL" VR %  "I@ JV8/_CQ E㸻9TJ8gR((R$JoD;3VףɈ%e:ST)s/iej,8cu(ѶpQ()LJ-*Ķp`)cXHRҥKY vnO8]h6)/T#|(rSgA`RIA  @B( S@+ͯp<]cw˯^ħ>>gΕ/e=w-w7oRj;(f͚/^|뭷z뭼i]tiz]w-X`Ї>4gΜ1;6Glͧ>8%KL6駟n6B:K/3f̄ z衉'Μ9?,8s֗^z;|[c4u~9oD$j7!(Py2>^R`mT*y(,]?>'__SN9{W%Sk}T*z#ZzoG+o <<Wzwϛ7_*(u)lƘ뮻_bR 19?kۿ,˸Gҝwyvm?ϟ?vX)e5jԴisf6xDiG.͡Z}"Y6'|xfơ_^|^Z 0nvmo~pAn{d.Rr~c.1ۜ|҉VIHeKyn!I^{׳,~yJO|Fozk_oDQT&LZZ=G<]BW)YAYyVcnY1yVރRl-2h44meEcǞ~??p1}J=n6X̙~zj:~x!uN}q<}W^yo>~,YSONWJy?K/e;j$Rr)uF*mE7vb,4M_s9 .p.s˗/fm^}U(v0ᅿXIE=hѢ38c̙g~|gO~YfK3gΌ_JQ!ŋ/_gy睟}Y>lQ'>vaJe```ӧO0uԢ(N:GyoO?=~Fs=k]wUJaqһլCCzFSU9-s3M% ,x`³= p{}Ν(UWoGy;3fcUs_0mA7_9,/vm{.wu( '-twL:u=s!̘1h+!;BWF:-Ztw}W\~yZ .?(T`mzAE !p "JsPzB@ (\%zB@RXDBBQjJM $`9sBYu)PYa<~< 3Eɹ:|&cof6L{RտGyq2{܆B)W5o4,B`:&8:ʷojAEq2ʛ蒒X)š,˸^{ﻻy5Uk]mxII 8笵iQdsjOOlq\Ʃ7+J]]]EQղ&I%V*c w(uN67J;^RREQf+5uNa\)%d?Wl-YҎ@OOHe')u2j5WW)h4qsb;;F=wWN0[e]V ybzh f3@q],~a[^ q#@Yx}I$Zq`1P8}Z=e\d˦%%%%[6/)))ٲ)xIIIɖMiKJJJlJ;^RRReS?-lxG|-1| o@;>HH (*J 1yI`$T %SsA)  ڢV 6;}!a#sKW(ʲ ~AEQ` .iQJRw R"FKfvS[^y }'q#q[Ht2@Z*@C ]VЕ&$L#QgH-Q牂8ڨRGڢ(*J$oAX !": BBk+iW(qc9n \%@XZPZrՒeYZRynui4p c c8Fflf]94nRje! k1\c<}Ϯ>%jN@CN`D,r)8g8R(ujXWSsnu,0B )5(bw;/- vED{{*{ڸ0 ؾȅ~_EZ%)dAi55alncmeYʲ<$@R+wGQBjoJz{{(dP#푒:x"HDR*)%p#jZt\@+9"Bʼ$S Wm,b 㐸t$U}#0\擒u q8(xG+gu Rje@Ľrc6Y`{ {ӹ(UEEQt+Xs=M$IXvyVDoys"IDyܔZ?G<@J)ȷ5!Bx&m5dQe8FT'߱)6B@)|p޹Xi !K訚T{E+ B*]0YVIjeY&sFzRE+sEQK=6+o:q),˒${Hwq$I}\TJjz@0xb_F{5(UvwB+,. leH[$#QdGQTIcW8 $WJ L.% >hVđo;!q? }"$IJ#KC쉇ZQܷ3*B+J$QccTRckRaA Z&s&ĕD |W;?m[P2ʲV5I{n(IlSVŽ2fY"mFQU)[WH)BsӷV%JV" I-LV-YD0lGGgլի!"RBv;԰r@!(,zT8A8* )`AD^ [_7#9jZ20(@H, #ǩx1I8 cꕪJSB"nr3aП}H D3EBz~FQTdMkj6==Ea(w6S- D@y+Q*}dyHUSn:J)~⏏ɯ༎抴U<#G@y+4'UNZVۉ<" rCDZ(79|xvV57=:DP@,H='#\8Z(k5+#R@@H#5VER #/!$6h䃧\ DHA* 9"xBHK!І[\_؜ *;~a-ior:c2Q IDΪH*jZIpƪHPlwZi%w BZ<@ ?dR&ow+lߢ%t^XY"GRQIPXIc!X[qjL.RJHD猒Fc8:BP Ezu Q+[jř&|mJI8Ii*s+R?" (5SEIIRൖV?{ot?Ɯs{"QWz GP܊rU/!JHrM[[VBY3H)ȧPQiSnUyb ЮX58:WZ3Nh>6r!|g2qY{.c"[H"BQ #pA#ȑ4M}I!m/ @d9+crU`1"gL7%FkxwHO rƁ!c Rv:sޯg`?YKBs9d,2e ooz &F%!1w# rMg !p h c'BJPM$?^DzDF4&$61f1@Hk=:m_9^S' rZэ>ctyaX}|me>ӈ؈r Bh|ttYsHi>(1ɻ6 YVږ4;fޮo];QyDv9Z!je5Pݬ1%]bH`yWatgz '1d,@DBc_pzKy*n@ p~c1$`A e-9R @&AMAaJ6p>NF`5\""g+eqZr ٪@ t8Zsak%IJy@kcfsɹu!2f,#ϞM?GZ BK1.`RQDQVK^wv+Q !!3 86\$^N]kqNq+E+zZQ c3g7c; jx@cyQ$*U|'~ل0rDXjoX9rn#16s0gΜ?z$ >A%I$λ|mdkmmE4MśKO>. GuT$A)q}>.q'J˭ya|J8謳+*+q,"2d 'Ntsa4h㰱CwΜ9ᄏ#Ξ={Μ9G[o!C6ؠ7=ӵZm};ws.. 4aܷo}kwg}چ)EwAyNb#笵ZkC7D6^?SO=շoߣ:*sɌ3ϟn9u@:;mڴՃ:σiӟjC9T*(e͛7{l^{>{yF qHsEs|m=,'Y;,&o \[tCc g3uC6u&.AҞ$E"Z dK&4.8?[cUƨ3Nju]zhmm}w~M72dȌ3$)ַ͛7fܔ)SfϞ=rgyo{~z:\4y:a! !  9df:/^ܧO4M=3a~ac!os6|#^-ZԻwocr]veOZgb-?qSSE]~.]vG{ɒ%=z?W\q"1C=#~_|ɞ6NF'\Hol%CiNnQ,q Qc6ucEiG=}zW^s?JαcO{"1+c+qUR!Vy0.yy?h#b>}492eM7tu]0uԦ&,Ðj={ZkweaÆz[Ƙ'=_r &Q26˴r\cM=ԳgOB(&L[|%FtēJzyr. nBw}cǎ}b{!;)~tI^qBfG{wq!(F}p@u_N]*A׈d,7x>sJq뭷8p7 >_rMvaQGuG] '0udY[p {gݻw$A{1oemnM7q t'^z|eYkX :_#ؐ#$)7^Stc}55OLd# 0O>JAA'Eю;9SΙ3C=餓MvG?13L0?~,~1$A [tgt͘1cv7n܆nOnA|G]t~#G+]xᅣFFr̙3p@RDəK,9o6_yhCD}6l]w5TFՓSzT'=wuιoݯ>l~[/<s7ƘW_}ummmZm]wx㍟x c=6x~'?% o!Cxa, [oۈK"i_$eK#]7k+\ 2z"G眳@Zk' PW4Jۏe?^>|';$I<j}R^xk=4hGz뭭Zo;v"lΫ4WǏ8qi&M?6̝;Z{5׌7sիӝs/<_16f̘SN9妛n["z'g 6`wVT|m&Pb_Y쾣 ?ǫ߿gq/B i j?g͚5]l 9jP(@{®Z%;R7RW)p9DJ`:({Ԩ.?kqBEZB(Zǻz)oq7ƴe{gΜ;/^+9:6"!DeQSV<3}.B8|r_ɴ֓'O79x7_ҥ{#l;S&AeY=1"z7FYo !>޽{1& 7w駟>qľ}Ξ=c?$I8_;euz>޿*ADOۭ_o|GaHJYռ%q'o'|rPOO{={c9/2dȘ1c|1 ,2e^:a60\!C]U7Uwn;xf̓AV9b~p\_ȹ17Ut?AC{_ѹ7xСC+ʣ>߈x]w}{s1w4(7;n{G6O U1[~a,XN;=/qW(AK ,8ꨣ]wuQGGQM7{'_s5\rw+ܵ^ۻw ̡:w܃>w-rA׿sq M6GX?Kvٳ>z{Χ\c,_W|6㸥_~o}6 8f0`@/ q~S *k!qHX.#爤`HH}`H+9AkhUBxSO-Μ9sCÇ?cGy$ÇSO=U)5zɓ'#wyg$!C 8x&o߾&M*JZm 7o~[owyg6⋥O`U秊ok7>ei)R3h!XG/E(C0 ğX>5:n;#V'_Eb7F}r&CWBy_NKJ\u~i6|Y#i4h\͟٨f@L@^ayNO,XeU WSse}TW ;_q'ŏqZ ?߮#E`itzpOmQyUH}P_S^:;ǻy$::8˿g7|ItI4*Im]zYK)IQ :%ygJ$});__f,ЯqTm_VqM{-J|+}Ѥ=E0t up7 *UgcH`G{\M[wkky!_o@wUV';$բW:4(}jhU#Kό(seDevm78g1{x֠t]lg#T[Y Naqsn֡Z|x! "U1N3E "g]c6xB7LQr!rkTtU820W&ϻ/-룮*㕴KȐ `a B0t`eDs@X3b eY]a/]lo]yvgQpA uW۱,;:R՚ EcLqq\U{ބ (|t@d`r鴲n3reԬm_g%fZ?ggU\ N.7yÖO\ ᔟ Q)Z`w|+C kn→p]O\[ccYK)Jüs.,aa:e9@ `~3HNlJ5Sƒ)xoyVdj%aLAe;ᓌ{tus!YsښHjhoFVS?%=/) ¨ū'Dɀ15DdҞnY kx1cLZi!)94M(2B.|3Dw[{uBOJr0\V%mm6y@UQ!#ī86% ;C+4r4τiT$Tl2ZNy7kϤ4xOp$Rʏ"_^eb%B+a2dBm^F텮,hKZbk)DMץnk?ŭ  ᫓ Ys&kmfqD%Q\[G5/kH\]XGZO#ȸuA`-Y2]Le sAZԣk9hmå.]LU ޒ՝L$9""KѽUI E@2ʤY!2| qa IDAT[(nD@uLr R}7Gt0,dX*Ҭ5ʑ(εQ(R"~Kaut-O :mkuV+Ŝ'0(Vw12R*IguέVtá5]|RkM*Z͓z7*okk+Dĸ`*BfzNUv}vǮKK)E߲6 ըn|_un2lD)pbbZkq2E +J|#X!C.Ga :Q 5 q@3@p#9ÐgCFR8Q 3G΀9DaV9sF3$pVpg,wF c X1$"gl gU&v  1pDH@\ q ^n-3!u`˷3`H Cqd,udp@5X'882p 0"j#Es?54Nv 8 йu8T@bdhQ_L)&=qMSQYW DQ#C#|C] 9[p$E3pJL1D@pdA(8"$ HbppO)BVpnt1Yg(1 Z8cLEiq!`^B}[R+04JZkι#CDRJMEN1ƿY9ȑ.$MVj+D2ΊK䜵61& MS!(>Z)9Y c D(Q"j.ڐ(cAnt +3" +#`I.@rӆ1R\!y-i&WđieشZ DŤ1 Ztrg36<@ʲHF0 1R)Vc HHkmNT SDVRI)DFkr!KH!jMeYs 6dmFF),CV t֢PJM 8Б?Q!0+9Z$* :ωHxs)6OS3I<+&GFFa$QJ.`iUnkݳRnseYDZ2ws |Apv*wF.Ӧb .ͧr Z{Zc;チru^%Q\E*Jj,SY~:wU"!GelE*SFBx^zU y&L{zK  BU9Cȳ3THə53ft^kO0dT([pdpgj,Ȯ&bYB<"ycM7LD#GVqVknnZWBAVT-W$LSPMk( e$ ښ,y3x8ÑeJ!NrVq#BPY* 3Rpd xZ{TjUlJ#qδkYZL"VY.93nk {qV*B B{o^xа^xqߡz-裾?"#pTBضdqTl\U>/]tCvēN 4JgJ%BVQϖ\),*VrےRkEYQyifTV%K q֐@Y V /-4uv( QZe]pY%Ib!@&-xv:}a繐A{ғN:i签/&Ij޻~n{Ν;wҤI#O6Go(~7/.\~?SO=UJaT.VkB@pc ASSS  ^{ijի}(jnA0v؋.G1kmϖzNf籋(tKݱqWwZ$ 9k 3f<\u]{ό'Y=ccY5559j_g~EKm򚱜fiUpGi !@HteYqe(*brN#ASiFZ $T*s(KFmh+kBX6RNWuSS1 wwyҥKqg=>h 3q$5JJ岱JJY*> nv2Z675U*bwc,Q^N$ s;3[[[=yV ,˪jR PrѢEկ8㌁n \PjDeb:r[TT$I="Ozj9seu}ϟˡU+m<`ƨbP*WuZS_6j=[-TvW(t@ P/tb>҉i,K"d8 jhr3TyLp,gt8 с @ҥK5s&O1Z9gIHbe5*LJQ'WW>1] VX-HW^A1,KBplY)o7sѢKbKNK82QQ@*T۫JRҴfUJ QJmrvD0VXV8 \8 b)&/Wd(@ZZb@[,%䫚A#:to2bkLcqh>ji1$Y3.R9+cuX,dYja 5J$eP jjYFQ蜍HT&Nk?$W:S:(Xڶ8e _YpuhAr챧q{,cX,|,ܾ*έhN-q[S|z%l$#kS)e{キY,Rc>}mQyJ)mmm:ϝsW^y/˫>6mڵ^tM?{;9kqyimm>}/1˷z~8%hzGk̙o_c=vw S~7ܯ_niŏ~Nt4dW3p"/y[1o޼+r뭷5j;hooGzwm&wxW_}_n}ӧO:uo=cƌI&vm>YJ\p]wM2_衇⊾}q_^z-»|؃s~/\P)G]|<ԩS7`C ŋIKS~7}LdaG}>{կ_N:q6;묟͟?uȐ!'pĉM'6mڨQc3g۷q.nm…Q> Ck1fL60`pԇ!zYǀ/|e߿R*qX/Puf!?kqcB!ĬY^{c=;({'IRVcQM8ѫ毭G]w~C=4l0c771{G}ٳ+ʶ[l—/l{wׯc=o} ;BOv>b<* a[\TI'HIy¿K/4QF]qCtMzC9Rp΍5j/;x%K==zx^z5ιѣG_?6H)ϽJSSӅ^"\tgvas W|ׯɓ';n馹svm\pzܸqyo;`|=> _ۮ#F=\OG!D"D$b#01ܰ@d<$c=:vX)%0k 9QJq&Y; 2dSJA@jPvc9vm3fL45$s2pVno|gw;v5Hs>/^`$.raY67Ȳ4ÇtఝwO9f4M #WV86Zi0 Rad\hka;Н|j=ZRi7gΜE:HQ.1hRVIR$8kiyEdH)?>6_~y?C&MT!4ƔM`mi{Lpm|fywkuO˹sZ.hn|~_[nix7:dH<Ε1`LSS(RιHJV5yqVyӻ;}љ3g RQy$Ta$mݶц}Ƙ#8bDoʲ3 RVKdƌr1lnnniE1jմX,Zk([)pDGsβz6%BrkFQS6H SƘ9 d9Ge _xe]vTj}13i`4Y˂kOr3K) Cq4f-_>#~y8(W GJ2@9f}O=5ڴ;=;AWe$o]quVY@օ"PpM6}g;{wo/vasFG|*? cHC-yw 2hþ?~w]nGA2&twV*GDܑC5V i90 rVkCH>Gsq/Ïʓ.X,i/hBg0 1wri@ɂV8G !H_q]@ U^ RZ+Q#~0pG—:p(Q1H0fyCdZRJV;x1g+9BSƍ9,9Y-#AV47 dMnuPZ[k!,C,s΂Y67739g1LRFCK巃tO8t2=#1wg-+d0Fۂ!ZX rVT^}[oeo{vs֠u8JZn!"By@P1X,zɓ'/^ '0bĈOU {R?K/Ԑb-~~;L0??\`G8lذ;z%~zh'zRj{%ͭ{ww}7:벯}_tgl@Yg^K/GzWɏϿA0jO&_~ c ]>FucK.Z9:iJ?뮿ᩧxh&pi]~F??οp֣Uoܼg󠃧(weߑ{3ln9cO7P(;켓'?8~^{СCr˟ȑ#o/moɩ2c2d6^yh 70 kr g3Fy/͛7'NtqҞ={j uM?^#K V5% >=|k^ۯom,ViyK.mjj B .l"!"`r"bFyXֲEY"LqHV. aQ;JEq(J'R##FFdX"Bmj= Y- 8Sp+ZVK$hQ{sS/!dvO##p*SbRDdj5)#K"Z1V.7p4ay-YxTN'=ql,M$Ig䮵6" i% |`\[VkRi :cX 0.$HPBTj(2 \sgvaO~‰H)ETc" km1R)Va\()#Dbaœ}!rO={8:"1$* CrTtCXkq\@$ "9DVrA0A+ι 2X֚3! VqqKdQXlkk$I|ufoe{jjYbùui\}ҭon{ﴫUz{qpA!RqZA:Qr]TZ\:*wJeYV]_jiiY>&MBTer㜳 P\(rUnx MlACVMLjiʘBBDeSyo{\9PpDžJ*0p?x< CXSSxhuXR6{ιFނ _; ȍ#̯_1Jһk0 %D10Ƹ0 \B KZ* P"6Y3KjMq1 TDPƚ$QI+KP+eEQU8sRY II)CD5V759gi$ikkKhYpeT+?!৺sNpNDWϻȓjϾEsZkT>{p)lm|X " 8sDDxFdat 3Θ|% $iDĀpRwWο?9G oS駻wZ{Ih1F)Ql25<!TX SBbΩ6 rIfDc5MLLAx%BRŴ\1JSsc BTJ8x.\!_.mVȲ,K))!cR ;{<"_UDR1"6Oߪk:kmiF6·/lԼD%y!X\18scRM)r9`5ha!T*5i+E ")YkM%!o"QQLF06J2%Z˚RIAE{[G !ӺAHq!%dzqO4j%}zyWA+ ^(, Z^dc)!a.Œhg0AJB"sQ8K”sPdR9̈E_}كV,r\$ X u[5a544xn*+8[}ڧ}aJ♇[^g4 ȃ}X>AEyik_"~Ƙwq=ԃ#G`:W*JYщ6+8g#G9ˆi-`gI"C6h6A&9B"|hLMiUX#H`ՇSFa|R䑻k]]B+ѣGm)˞Z8d/Bk۳:n;3 Kg R) FNx=wQvR0GQl\{UOv]10 91Զ.|#;MEcC㺡 V*DdrECH2e BPׯF Vз6n0cj-x d]@'Z@` c%FQP.7r: R"˒|>vΈ$ƭ1.d\I.Y%gbS* EJEmĀ4K U_Xo?P|D)mӦʄUνJdiZU,I[!֨|.pFJ%EQ (Jf.p.T6o+A7;6_Cv!q{ѣG?C Ÿo.}w:u~Lkk4Kϫ;]:qBvq!Ȍ6S5v݈뱬rܑcUS}*ZLȹ-:nryo$ڶ.} aG@mkx>pǼMM 3fN뮻F/nhhcvaҥp'6ưs5XZB;LwLy&[nnMcc_pw^v%(6h!1h=s'#Xz5x˓n+bc)_nu੧"O<4uǽ:|.̲va2y!\s-a_LޞH8sݳN7qg'g=0W+MmޜzsRҍ,˔RB˗/tM}uuu̙3}xޜvRvO>V)'Xcc=c 3J(|,eB?K뮻IznNZ'/O~N>+WJ)G.L6={ot9餓0o{bŊqs=C yw{cܹwcWWlmlc/y1pdQB(!AZIr7d|_n˒E _}Mnm]z>}>=udsj񷹈oU9eK͆0wᨫcnirc=缡R.B筶@ҷǐr9u.Ik?u{UP*%K_6 ()$: @yٿryė ;{_K>\}UkGuu 6v<B!m69GcqÀ'r7i2xi?p|W_z]pW7^K^ء\to:w3iRxu޿޿ė^,^4+ n׍2RLxa{6B]pP0Koarptn5 zQcߵo;s\c6+m.}P*Cvo_{f.]{,v)kϨRdž:sƌnuؐw#;>Gn]yeګBI¨W;J* q>9_fMКRN@Ɓ7 1!JpGq!2rr]^駟u׾#aC9.[EN;nj!_uu]vꔩ}z|Ɛz a퇜1ضvؐnOW_u%F.S;uc7ӂJM0o_Bs΃:}lk h}xm 8ܮ];cH:(CRBeV\aK_8 g\f5 vm}{/I eF(NB0L0?a_cH.`ϽH_(f(Ikrˎ kc}1aR_e"SI)}>#;u*Yq^x~/N`]NY0MӡC:o ,PBB0 !;80PfO=[l)2}tv'6{M R-,gQ+a>\%0gJC9%͆AdҌֿׁÏ_VJd}"Am=ϫNo`=8{SO=a&rӦM9cǎk-80`zС"l̙gμ̘z_~8!rs=G?~AlٲiӦqcƌ9CQ εi& ô\?ǭ P E2T*!xM:RCb\, ckHVJTԺ|.ƁgU Xja=0 *D# (OP>K3 pi]v_ )Ч?Xcj-"AViFHȵ_ՇAl@ n ֋@ۚږC=B(!c-$ÄӴn3VS]Iv*k;*[ߨ-Xx3to0J#XdZZ2 b+M%c4$(;f:otQRD61u&  ߄FYhh8&kDk=9[qATrtڢ;bqea76u]8?'*6@hCSL(-Z~q^j RʔAM;- 62iJ)j~X,w}to޹x;qȏ:agu~pkt\%Xdi&gL&E&Ӥr]rE\rɈ#0rr#_{ag-6 Ef,K⾡?{1MDrQ裘0u̱Q0tΕMBf)e%0,B@0ߡ[~}o(Ҁp{D)z4iR$ڀ/kڙu_,z'c(;L?f/Ǝy٧ا"|g!Nֳmڷ qcGE @ B8+x&MիW2`Bo:tAg̛FIpP$ 20c :}co{vA)јdrd:dA T¤T_!BN,Le^C?>4m۶g p˟}n" vmVwgN1%9+ܿ~ p aޘOa@==j!gϛow%-%A-t/[H8@nu|~rÄ8΁K~>o+ۥKQ=rzmK.ں6o 4_xs̝k}vՋ&|w->}ZGCdϛ7._b9{OOtc=~my+rwKoW0ҥc=6bĈ(7le˖ :ǟ8s6l]bJ俺-ltݾnjU~AH# al-ro;tj X< P˽}~~|J RJX1p$U0F BYI׎ig0"0f()}B<P 3eq\P^gdX@  IpZJe%F ՜seQJ3)B1)fRh%uBb2pY`anҮFp-vQ !kc\K [f,p; pCA&L%$ت*@B˲,9)",8T\N a))!4JڶMh 39gOY1 1fua[N{D*VB. )PF@AjZB nľ#PAm ű·M4lx#G1Ҁ+# !A"iV5p-1RR9bɄQl!g9'3Z0&J) 9_ 8Za`mjhF+J9&$KSLH6Viqƒ4mS[SJsTc.p>i_Gq9418&o6qNzݜ0@u1 IDAT_f9\#E]=m۶ cc)$6 ՔJ,I#JvӸng@80M i9g c\I˔@2#2\n#U:068#C P ʀA*cUJhFyNFa"ń Ԅj@pЬp$2 hsʵ]>`S:%!BθRZ``I!T*ps4M&@0N#R)T KI%daq)1Auں0JSƘBV U-AS2U  9S (#h;R'JL(`- Lmv Pf74T(!,`B1AH)8n!6TTb^ZQBOH)e3(B 9gh=sV!B4e30%RJ5Rfu`uQ7KspʽTdrYd J1qiM%I|ȣT. "QO8$A!-_>s["7_S0?c̳yPJ " bX׸X"J坥,?~ΡW٭ÜW|>4Υ"ca*aP0|Zd2aYWi30B,JyTT%Il9Yfqs:g] Uum=huSJ5H!n׮%H$r644x=sx1P&èXUUT9Jiy6MMMQ.VJi}qWZq\.9 (*i <0B+LIFu q!: {AV0 Cz~N輊C7p[?-jDk^qv_U?o^?n;@W[`a*s-<|8"RT,3aF1B1Z0"@dAP-0ʡ$,)D9ʙ$m"(&8r+ B%!e8!4M59@Z1Q"1qD)"kRGi8e#i5sJ)m '~`A7kpع`dbTs#ځ a )I)aVFKqNic5ɲ,-& EϘɜ%\YiRGY 8Kd.SYm8r6K|!4IB< !(^F\N8eBcd`PL*4/2eRh,82s!_dCEߓd=NOwYy˴4B0[ C=e\B+RX`sZk@p5B!^ɓ3& i RJJRA0X B#}M@D LΤ #6AfI)T$kG4*cwK?9 HP6v duAȲ @` ۜ!1g%ӌ,C\ \+]T0(8!O2cպf&6RHq^IEQ|Ӹ$8caDyj!Z&RJa1QÐW*i(JGqRJRe1 b TfYFi BNQF,1$Ik0d-q 8_FG1:3PPHiAuQH5|QW7IѪJi)+,0dbdl @ Uq8)ÔR2%-6BϦRIJ)سUUUix@֊ ҬQXTJP&M 9,dFc+%XœŞePC2 s`O UWWW]S%e]k Is5?o~A@Fdܓ{FJi]]]6kTOϛee_h{.*!D%K}؋)2BJxCVϫ[k}냿kOyJEQ$D!Bt A8B,BhCCC.  Jsa1~aFfC`f`pp_=kL-qY hc?cέ&6s:=b `W矟]3F0JI!ZFoPR"|gq c0Op*" Y"&4 C0) k!})ӸMM{ɐV;\:5Qє%v, Yea(vԪTaB #Uճ1)ž\+Dè:!c"!* B56 C1Em+p HBhTJ5 +JeU2E@PIX!d(Ϻο~'jJ),#&QLsʌX~9`,pQAi)dWDVFRbxZuVkF*cfYZ&J\R8M q%4e+W.|͚!Dk7OZlmda )F1`g! XuU\j$8Y>onmllcjjj$DTkU ez{e?0Ie# )8B$Is֭88iUB._KUV\ZgyF)xh`{4M7D+o\n>컝3Yf[/h8C!0 qAg,8qg0wf Agʛ/,yUi(X ڬ\E)QJiR1&%u:KRL:rs9k%& YZ0,K ,˖.]J(h8P>7$9\.y3Res@XRC sr)A\wZaiyatK'8;*eXOu=+/IX-:.QhV1V_7]7 ߪ3A!"\\nM7ko׾ H6Z{^u>Z756aBxxkJwA) driZ!h.|B r[EZk28Ǣh-91 P:IӤNSq}R!+/W׮> 4$SnA (sOrW9rر]wݝw]'|r5u]w}#>c H)5jԨ[m5|LIL!LܓعfJ=4+˲JGǕJ5Εe)D1G$ 87o s*ki+uZzZ}iY0Rb;vAJ߫Cw[+x/i,{mk .ǧa+qd[+ol=`@J2 H&нSw?9Y A% *`v6Lh`ƳJFy4Au)Guĉ/fYRb9RAȝSIJak3~ /Zk e*9Sx[qRB *BSbh:G`1 ,\9os]O^zVrAgv搎 &N~rk'4cR{Cϼhqr_^J?XѰtNaOΡ 9jwoSJ)ũ0(d<"d\hC4VieM@40cJe!cL)N,c%-!QwGLa1Vb%8`9k5BFB9k-"!d 3Fb1_jhݞ{l7>p>} w}ϭ._JI8wjch+J1;v5kF0aš5k.OWnjcJre]{mkj(کӲ?" fBI/!B)MqI) *m(ol pT.&I´V 6Y@Y Z#s)ek-#^è9g9^8jEm4^!5ơ8 0~uH=eTw?rK7# s"7(?Y͛7ٳw%`NV}Wwg@Z7lN>i7G(:ӧ8P*ŧ_̚5yN8U7o'nZkG+wm>XlԨ ݶO8q…Gu[ozm\B?>^էO.l5|sou=,Y2۵o3nܸ}wSO=X#R _^{ѣGc̤I BX=uO>]v́F-p۲ôgHCiЧN=٩_7w uPF&调: 5\scrH0h'w۵_9O98A(T_}vsGJ>_>]vͲl|ӧON|ߟ;w53^^{M:裏fi:g4=#۶mM'O/?7/WVx7˲bٳ__6a}g45gLڦvnݺ}3f<^xa}wi>\]shצzsnܹ,d駜roze>CIŋWPL~qmJp9%` X VLJ ޅ~|,NV5ùSANGQ?}o6ay9/>•] Ͽ曜ߘN_e.z/DZ/L/>|D~zʀak]޻}]^fHٳ^kIp%oNe .g:ut#8xw>O=ҋ.ʌ5>s_zevA'wܧ|2x-|>Z#WBMvdY|yӧފ׍v!!nЌHJ\Nzg|O2vswٶ\.GQa~?;SL `]vaᄏWe}kޜ"9/J^ ]~}'OiJN:tЯzګSÐ+0Jʕ}gY}z0`=tASJ1rU/pC͝·~s}c;{:(sn_}R˗;枻?\6?裇yfySۦ欳6쬳d"u>짞z*ş9x0 ۵kwUWs= iܹ3n;ڡC*ֲZU7yV֮ul0hݶVK7ιb`>}i`LJxw})!BӶnW_555 !ڵo[oI!raۜr)ҺufN޽{~}va\.B`HN.}{ѣz;oo>{eRymvcu;OȻ?;a6*9L F LmOweI@ oo%6< i@֡\K#k,5#$^zLK/a͝oΉ#y4X5(kAc!?{OUr߽s9J`42AC޿}q4|dN @ bي1 S>XJ(j*5iV ;0`duMR (a@^#Gˍc G|BXܫ|aȃ4)1@6E!%8%,8 1JTW)!CF?iL_nٲ{|ֲPȕ+MicBTM۶wq'N~AI!Mgǯk(٣ǔ)S /dm6Z)BfiڡK06B@$EVϢ83gNofϜANsFHE 8q<=yKt˓&]oֽ{W^SOnݶ\L(%7b* p5usm4AElK X@ B[-ibCH,1j,DcK""R/\圳s ~yó9e}fϬY 655F* Xȇ'8/NZ-\ZKkuilwE>yB8{_X37YLWY0Z ѺoޚA<󍍍\rIPBHe$䁒0 \uU8ʲT)€1nnnf08w$I(x'?fBHFk]Ӧ! H3VX84J|"~$kcRpf!)b <&DXݸ IDATnW\e[߾DGsg,.6Jo\ᩧzᇯ4[qBÆL2޽{ڧ~cy饗J҅^t 1-Z|ru +3q'n\a؇qr_X'v>* sRD0kӦ;;_y`*;k-tlZxʎ?zFS=Il߾=˲+W2>K.?~6oEs!#0ͥ ~(S+AƆ,6n[q0)55 JM%N_ܸzBc WغqƴyG~^s9e6IJYdY&d=V*776}Z@jӦ QܬMdrJ=qM :c__4ۜR ~[1h` X P, %ib1 x;p}X:͝6opebZVBI[Ok?^o8 <`pZ 'Mr/yaIR~W1O]r{CC(IBEEcS Ȕ 2-"Z_Omj_B^㎎//1~c-IMAƢ1aPm(r`0Q ܰm/4u8ׯ_?~ BHeVNbr'nZwg3i Vokx{gztiWGJ?^r c//^fk篾Gi 㦦￿o>9ۺu+Boߺrn*++TΝ/7{f͚;JV^kKKqd10j/ׯ }嗞m߾a$ M nj*McBѤIMf O꺟!Tk0 DEwӉwk‹;F_{ݎ8rjӝ;n1dnٺmذ֭pQG ПܴiU5i* ޺msݪ?dimA?9䓏 '+}?`>c>٧{oظd[~|eԪO߾K-zԑTVUmݶ ֽEo'AXS]=C֯_|a[k t ra'\jʴ~p{>߶X[;`ҥKuOWh|_ f[jO@F[(cş|W w\ż^}t >vE[KN-0Ø۰~}uPl}Gsj2=;%O4xj`938q͝ŗ\fw7_xŏ{Q?ꊫ|ϗJ>Gkph~VVV2et{lA:wae|/~0f3`̣ <^zJtٻsڶk;pЀ 7_q'[!Ʀ&c{{uܫwOV4xO>U{ȡ+VB4K-^Q=h\u73< bv`EZD[c @>^-x L-3fo(/_~wϡԵ ;@q'qG.}{[.{Nz @K"_:AkC ߈oj#k#A襉` 0"R km2!R)5=dPrNJvOAQ(~TJI(G1yVaR d*Y !@acƘk 8Z)E yH,&e0fF#Wh0kK牔eDj%a@9d"jX9X-VK cc@k Ę8Y& V*6@8R#R P1Nbfnx:n"8q1m ƘZ9G#cpԑo+m۶ >10=ϓ"]bTܣ"SL,Yzŋxک|>ITȔq kdF= JЯ^nݵ?|?aMJ,(Y0M2BR$%I ik[j3.J-'@ ΦL uaW|D lvX os~w0t1ιRJ~b194e ,sW1ݚb' hc^%(BBS)βDk)QJ>ϲCp ֧ ;p|!R8eB1!(1QR].u:Ԍ1B;vwJhv( +NbuR+'ǚWIPkmEEŖu\8H c'AqwȘTJ(nF8RB)F9[! V\hnn/~jժVWWpb!Ҋ(0F~ !bB?8ƍcDZa8V"-(ZBRhҤIߛҮMk{Zk@*MդYu˕,Sc!)1@0TVJ""Bki4Էb 4Mڴܶ}382E:cX)1f#ʕ+iۮ].] B)j#8`k8|>"7yfϞr!sIO>dcc\s555}={luO}u >_~JiӦݻ{wYVo}go5;mUH!DEEET}֜s!DQNEB͎bWH^gY!]uBX#J1'I9u~D)2)v˅8X&2u)eyBG9``Kvg]0 ĘSGr\bخ];'ʩB*cpZnSv0J).BĘ s}wXk9Jcc,"ιj8^" .kE`1YS\Ή}ceBFRF!$6mڀnE6pγ,ASsa)4mQJan]gB<4%a 4PiAb1*%ee0B+vw*rcRxZ63%-LH)YUU]*&Pj7>)[V (Qf1ѩhp'%!D4QTEh Y'8Ҿ|Ɖ1Ppy@J0̲ "}Dt68_WM6';^z)SXkO8O?8sqM7{챣F}]7+F5tk{饗,Ygرȑ#؈rMo3AyX!'Qxee.<պp&YdrX.y9;VUO!Z1u2MV)WZSJ1v"XcS!_ǝqX(!d3⎹ht]nɲ78cLEE+cUɕ]#!Spj|΀un7]/um ^JɨnuER)-E3W[]ce/tuN?ܪyPq󦊊 ) pZlB(cpleBDC{'2Qyr)URLT1*c1Z[ >g|!2a2c(qd0iiA%A>hPP%X۔'mfZ1m1#OقTf W* <Rr9:2RPʱy>{.A`o~]q7ts07lP__?o޼_~O>m۶=SF970]t) sOS؛oپ}.]TTT;[n߿!Ҿ}XjUuu1&2pM{geܴtL Q0u b,Rb”J[k9kh‚5H+k <%\ M Yk9eiXm0 {PNQL„RTJjk\1PO@*k1"YqF(%hiLHLkidU֚[TR}/4fLe hje0#665UVUeBY>&$reB")cN*#k(ʥfS_x`3(N#kq@$ɂ %:򕕉<1Ɣa-'{]+^WYwm>`VuVZƘfgB)mTmסX6 Ya9c֘82SN1bR C!t(RGd RFahBIhPLPQC(deP!JE,B֊T,-y2 @cFr"O#>cR)Ǩ".C{2d Fʉ" 0L,&$]F&IFB9a\H]QYJI­{Z|>#۷ ^{nݺ !/_^QQ tхzc,Y6R޽{]O*]i?iN0 `6# 3J BJ)#l !J Ƙ]*(|hi0ikNH$c 9\j.2(&Ƙ5 Ͻ7i!1fj%|(&,M4rޱ{pخ18&9d4M\}'mʅ0ILP8OhcȷΜ9X,C$%w1n6Rk"*i# UUUZ,d& zK%tƍuA.BBhQЍSt^Dn6RJcIp֎Q9\-a}ۢPQFIFQ#aDfDqR,U1`A2T֒2 `'( 6M"d1%2)R-Enճ"sYC11ZGGhYG>0W*5sN]NE)Ϳ˝grISB BրZ Hʊ<#Xd VJdYsJR$ň y"0ij-=7J Bp>ZҡN{|i9Y0Rq bdvߣAfN^!Y-hc@1* 9ciUnkuxYk,ArJ'Q,*)D`p?$-;dgd)3Δ$ |m0MD+,-n`UE(QXp;n϶c,Jչ co3r!457ء_̚ZM '̝펻oi&%Z,K a>|Y FKRl18]\ @j~&[N׿u~<ϻ+X,paÚ.N: #ƍϞ{O=7߬ y{?~]w֭ԩc\.BŢT@CvƍoFP1bرc17p/[|Cn9X0wɧhJK/=3֎sҏfϜӡc^3fNu{eCr0K3g,]q\NSZtHD@>m}駟ZUUq'9rK/p38i;4|'qz\eh+477zx׮]1su9{w5ƀ2fW/_6cnwqXc6o{۷03$۲iSO=wߞh:s7nNqk^=sI?o~IAbmַ}\.1|qƝtIɓ'_pΙ/'O\SSsYg2|8wslwym۶ݲiҿ!!Cib{GyСԩSq/RO>:ujMuSN9% sg SN>;ka!gDQ)IřH8ƷysÆ '|~{g^`KjpO{ &MOOz=2lŧù3kb K?x5t}L67d=!Ǣ4uo@4J HYhtËM5 !oS{>s^}\es,` Y_7ڝr)ay^h1;w'bBahߘλڰkMٝwbxoSJUWWK͏?ѣ 0qks]v5kV^jkk_clĉiw}GyQG|> MsСCg̘ѱcG[?M7O80,|W7-m;}QJ'LpmZΙ3gرu֙xtAk7oܰ`'M%q.Wq?y#=笆mΚ?}9#ϴZ`~ΝO9GƎ_syW]qnauȚiS&_ye[6md4 Za_p#N?i <&=x˖,>s?2Jn@G IDATe;̛5R 0. }Ϳ:38[o:e>zgu3f6qnke`|7L~GG?طx?wIGUϝ9bDGMw,^3F|8{o>m˖9~%~.ymZ 0zQwȳ,\qe:wv44o·ӦLU+*GzҵW=ԓ>󇎻?'_t`/M}8knZw޹r>0gϜwvW~};vhwag~]rESW{LeR%QjZTk4A/:[~~˟xbz}ÆRΌJ>iӿ_ք}=6v7샷O}_@+z|?aѻo֝5&UG=J:nquСsf  7fоX +WmX8/צͿo^x50D1hRJg͙~rO?]O=ܳqc>jԕk%0ʲB45V{?߆*C'[1wyg+G)շoZ݇wmݮ:wn( sB.5z~?pQGuQ;U dz`^xŗ_Jdi:a›E_veN Dt}̙ƛ=H? xD(@MMM#:M63W\dsۻW.mڵ}ct=և2ZSS]$)X;q;mjjjjj<ϻoᆙ3gуyl=@Vv4![RV4\w#)czwR{Jz;^?͞3cƌ:T)~]M7_:x ^{v޸qûo}xR+]/<j۵msյD0ð'?w[nwB >^{wާw/BH{J)7Z x\.9N QT (F8X1?яƍ׵k=z!xgyN8 &g9wLm;wCJ $Ilڼ(i6‚ RJ)ȓ1'f)M֦MuΝ-[o+1cF`usscݚ5&p﮳: (6ᄍsjL{3msd4w  2P L0iᲫ_~s?0W9() Ͼa{}`buEʔfǁѰ93}ګ ^Y/bR%O&ia|{ 'K`=V} e w$j췖w-J3ܮK9x3el.gK9 J%c~? 1\:1:tHlV˸\DV{(VKSiyN[lO+<ƛzu]riF]qu\K1R/қn^KRO>;g6NTDz"1=+8.wQmfN/y"ܶn]B!S%u"2Tl.}X+867 UkRJ)&8_{-r4J6Х4769KNADQ a 6m Gӟ^OA[l6F9T](ahBS"30شv Ō׭WtUIT{Q=4iS'}ѳfZ$Ie=mA(QcsbF5UDĦ&sN]rcLr`uۄ@( `W^  [k0ŨH=#%4`A iЕ Y#kmR345{CP<(L E1&@hN;6լ]U4 N~ 'tmՕGsBJ!{OU Ha(W/_]SQ(/PLUURBd)0b.A`W'Q-Z,] a]ZO턏]jpV!j$q!}iߥ(*,?vWr[>h!DeY\^rvM?kEBauըQ8b(h-r'W3/:ֺc~.{Ϙ9ZPe9$pQ))eEEXYY)]Buuتnӥĉ5k֬y/)voO>$yQ4(W!;:%˜J%L֬"2!ŋ2;v6maTYs֝3{Rb~-]v*1L)Bb7n?>wwя,DAJ缲vP0lbkYƴ2jI´ 0eBT W}?ȅn2y~N>| Ljg$ 2 ĸ/ ]BI @ :t\q@!HF~@o<֚+6PP My]PUM4Z;m/?q}XfVXnC?Aֶ?Zrf-u/Ǫ-ǒ]xkta+Z,/:Ts7w7s><}ĶW.D>ꫯrJ~g{yK+?_e˧N% rT(/Zg;_|o{C_klj8h9x2/ {ٙ0>p/G3·z{SS{աnyl8IJeЖ-b P T붌|^o<ܵT)],($'R(TІu HO+hU5kz,}oW|MPnå?' 8ȃXܰv [&B,jllXLC۾K_F_O}/YVֲ`{V7'q9piǖv{k2mo[@Fa{w/.?o={D[7qK.ÜQ9!|T 8^tS'/Y#GGOW7y{s {7^G6ZQ}kz!Gg6UmNq*ETn҄Wtrǻ}… ?.{q:FB\r555~zܹs)+jy-@:ƆM6}m۷!w=?| _G}4w۟viڹs.Z@k=h vтښk~rGE{ymk#%4`K`L,A(%8Vß_}~c~p0ss2N~z\`QCs[LZ?|'g Z ˖|vbӬ4{VZ0F-`Kk. d]Ti-1hԧ3`};u; D]ݸ Iںm{tЁ{`Z- ~r۴:ٹsΙ3ۣ|Ԙ˗.`t/$r^;L -j.}WnvraYaGzvQAq^Jg[O,;nMoڝ$}nڵ츁Y T_fQ)Y*0,I!,NR4J}-%J#N88IerTFPK>2VUTid BZ*'Br*0,B\ +%aϛ)'iU!),"DiHaGz`WV3T` PJ!Bz|hTK;4ja;*pii @'Y10l5iUTEkq} MF ` 171pr(ne*++=|ES*57y^qZe1bP?'bF1̍d絴nL H xGr!DJemM!WQ,ca>Jdk- ?bTQ/5##Bm%}JR3k@#| Y4~ddR** n"2Ezci)5AIZ!2 M[ض= rJj2J&Bo,q 9m&X%E#%4#fUH)#݊1-6 Q0 #>5_iWi# {Txة8η5_vI!$<h"1(cZ[e4Nuw97; ĴB [)fN+'د0v \QKowY(;02B_u/`z-zAhJgRʪ8Q,SNfޙ7ɬ95IFTp/#s&"rΓ,c@ik ƳL !lIZEI}01`1eZr0EJidHjyZR&ȅ$Ǽ 9R+9U𖱪EvvD&/"mC 5m5Rj{Jet.WqJ2'ӤM)*@UI9R+p&Td^Fڀp(Xu@k,ڢ$IF 欩؜d"3Ɠ$gJ*my#1!BJ@P J<S$~)a<+˥|>o(B,F(vHU'q9 |DSF4211Oh2FN9U,gK>|C&6rBYCqP(kSR-4r)J6{aifr?,G4ބqഺ*]x{/sSXI/x+LrRz4&J4M}/8 D(ƸT]SEs.M}xvnDzؘtOfvff".Vp̿rіr<;hZd͍ _Z" Z,6ˀ !UFEeqpcĜcX &4lχtJ2\'b8T(Mo$W1BҤ< Df}&FkR U8M82*Ôr_3`,HcԕpEcl)VBA(awHeQZ$4% Tnrʴ52wyIN0 ;?1-OZpjΠ1Ac"d)Zp;cݮ#ѹ[ȁYhDYqBikbA;pXpkyZe 88@i(u#Ɖ̄TH h ,|ƅP`1 ` J)-{aiab1 ZYƘ3MM ҭ3XD~@ajpBZ[ BH<OiK,4 |^lj> @LWj}*-m'l  !wrl|fx [ld3%*E!kюKkpZ8"RcDPi VI9#SRQUuLĘ)X+HbrRd4H[QhJ0N"3V~$r(0B[ (6577*rIqKQ4uX*kZ 1X3M$q\(2 "QF;I?ga6H:~W픙,-j5^7\c KDKTQcǒ(jDEE, #3ǚI+^{}w8 szy^ItvJ[Q)%ڣ/8띝o6(.{G@K j60p!0zj0+Sf,ƘPH3B /DWޭSDVkii):].};R䭦z\>ï}*lJ9Wo68[E+Mi0Zc~n'c$Q9݅ç?pVO'Z#D|s ʙz|,Cu@ysߣsď6ii R7Q㿇:qrEQ縻Y+}}Iq,l4 `"/v#?U mozoKZzp՟⍫=Atr)QJ{ )e\Zc`:`&6f$d9BF(*r* %$c,^g0 6,wb⠯$a YEE(9`: M^)J})u{)GkoFD*XL/YpN\eTQ*Ea{ ^}u֌g{%^1qQ(,JHr IDATb@A+&NA:J);;;}_J7|s̙%a.S~: 9p[mCZL?<.(bY4#4 Ë/xo̝0J/JYt7IO&]ac,R92u\.g8E6lyE$e%ܦIhzR6C7~d5!4_{Cq晍ZZ}}8g{d㣏> jѣGEg(rdERzH_3f?<}t؄NgF$`iQ }9uĿ^648 A+ۗ#IF!7Jb0rLgʑ8r҄zrYִVEQ;zb%\ߗKYɠA>%sGۧ?v駞r:i7|c5#"ݰ_('Rduyqio:"uTJsZ)6"p."oe=wŧ`V^٤<(U.yD`:bt;Y]u`劖(9EaUsJ3f{e@*<,U+V^}U2 w[K֢dd5uk?z֖ %Y(f{#F% Z)"VJ`][Kkŀ0 Y3NUX!Xwg+Wa!u=Mȯl{޸з9KW5>>k/3R cF#aubD7<;BE0Pƃ`Y DYPDrz&<ս !_uqL[q+C%f*q&H'F$T*2%8 צQ/4F:2PE y%Q9u颗A  {ǽӞ?ow:p ?'zN(jv …z-$vcdV jQ]Q4KI|)ZBFR*SB +p@wr 6 %ٳoQFq1fv>|xн<ꨣg30^.pEwO3.~ ïy-8S,E0pN8VEA)Z֕JsnR,#u<͜sF(Sl6E^DQMr $d\ ( G0$R䴑EF10oW׵ZUc^p퇎n{egw̪._Z^ސrM70a灙QR}7^Rs63`1, .{iANyqB?%ͬwX?>3_T;p8*_?T~zm/|)%K7߿Wұob`G.l=wZKi͓mRo]|a-4h B٬(R}{%KzefRtM5;'}m7{󪕝~W7dJ8 4{'/3f{œH0`Cz뭶zZ[oԩƍׯ履~2uf]veZ%\J%Ks礗^|qF9<ˮjs<'rs?vlar;vLؚ_]"y4ƴ/{WqؿΎ'l6XQg::r{{{W\q9 ZHa13gδiӌ1ڐ5NO?zG).w゚}~wakk(& oSu6~?^s:;ߘ;+o}̙̙zutPyt>u\]kxp;g!TlWo0`]eO]w1\/v^-Œ9kJZӀ[k 8$ciIk Vpy&{f Vw(hzh4]׽کӞ䱇w '_ni2'[ Dun%!/ar0 ,@ ,fO}o#,h-)W$ ;W(,"BF45[oc xKO}?^un͸cǞuYmX˗J%Al6_zc9CÆҫW޾8(˚{k}ݻmM7g0o;n;t3'{=|ƛ^tws? 'Op ιK/tʔ){キBbGˣ<;⨑l8`ȑ[lq&w_6hq!R,#k>@o=%/8l0gFA֕J}r-N?mԝwyӃ L[k)jT?dw߸q1wy1cy͞={-1b '?iҤAlˣxuO>9#-5!k= >_Ԕ'=`c.N{ҕ]>=ȥ_9s Fȉ=cԨB(Ƹ},p.r'T< H0JTZjr ~}5G =[ f<`z5V+iaD)ag6(Fi?b6R$I5clnٛq|G:r]B1̜ǧ}kupJNsG!ϊ FOӧO6 <͋m^}տm矏23:;=[oR9!.78m%#im0ƄOι0 }9P/;s{nkkX3<~W+1cƆnhYgWt&N=av1FB!3~ ]*U yN}u0>vK1r Qgȟ-(VIJf4+q#l0B(ьX Filmhm*/OӔF0 ;;;$R2ƌsNK QYͦA(AKy+3B"˵UQ8c{!eYVBa6fTR7^ k9".RqKJ)TV;Dȃ_bNi^okk;}D5sOt  ` N9#H!U2 URBT*y3B! `+ Фi M0 1`LpШUZq`B @n )Yk nVBV )$Bq@6q(V$e{J!%eQz5N뙓/(O>ag-!q^K,`~K.[ȭ!P" C%hŊ)Qze!r`sɡꫯ~ǏRFADM}{;\GUeA4Ͳ,B 8}6K FqLFTj x!BrE['a$JTdM 6`e)#gU+{+m,.HKX{'Ke2<0e ab! t#SH0NfZiiEb!E28#Z8D g(&J.s;`x]w:>w}+ʃ=С_\೒v6.#T*AXX@8/&B_w7ߜy?ˏ|bʼw~Qgmma^Y!aDͬ`ArP߿8`Μ9>S͟?cuΝuY뮻^6-6˲,0IաKAN8on`°EpgwyՕl_ŸqyᎎcN:rvJaƥRi<BXJZ˯8Q:O6ݬO><?e; 7:S{>hk|M-\8tǝ@h))I!8R =<@wqJVK(k4Sƃ01RiasTP!`9,,˜|cAUբ܇~ u@ \(L)Qr_>o~V Y6|ŋV{5cOo]_h櫳>O>R|pk_ʒKNxMm2o<̪Z}w?cO8~A=ܱmSO: !E{]`:_ݯ.z罽;o~Y_qΟ}c6b-\5| 9HEY[nOj=2m9r̘1{4="J0^zI&-X`-8p`~Əx?w߿[>g|SݷwKo&.]?xG%+WǞ{;U';yo͐-n\|G0Vut<vmn{y}y~^(g>O*h'k[#FRLna?זre„ O9iժUS~#t4B(?^ݮ죔p #%[nvOW^y_~믿ߵyy睧&M:Q$LjFG9d]0)嶛oƻ #$)W<9YNMxL1 A DQh40l"ZFu// aГЛy1z.! `; (&y9Ą wMn~[쨶j/Ygb%4Đ +5X `mr!sP/!L3L;@I (R JՖ[Cޫ:I3UĖ(LJU -ɋ45X7Ʋ2`!`yyƮ/n8 4k!ļf5sEyޫwkKuǹs?,V b!G)f)N,8cЭtRZcipl=$tu$ %)@pf4`,ic1t:=|16kXUBf3C5FHiR8=bQc~=ꔴsE±YV[ɑȀ.Ld( b\]gz@h A]k;L y%_Cw}<`qIv٣N$!rbm7} rHgL j#~W^xn̅ z¯fx٧<ڪ?o2eZ}t!{1lfǶR1F$."Euӑ-@VATDj֤q3%Er%[%R*JUJ˴O2J!7J<2QS{q.9~1kE\juDJ1F i,!!5ʕ^R$IRD ԺVC*4"J)mPhfP֡Z1 "ñ0v2A@1Rr?),L40-TZ*0Qhc)B*8 aB(sp-iY%T6 DPLs(/$eXHBi˃HIYø:Z#KrťB(BT: QLX*M(7΂VW>(eX6akRJ[50rU*C(oyR%RZ[R)mu )1:ܟ,֡8*U+ܿ:Ls LC8/dښf0I")0J0fViڥ0 {Ԫ7ہH)RVcpTrEQd-XprLYk-} '{Wxp=JE9t3Fg޷Zkh3p 7x#)QZFq݄ pCYÏ{}SbOj_q-|駏>QG ~[nYd㍖|^+/?c1;sKK۫:hܿF?0`!F eAFBk ڛ.g! GESJQ* 8㜽=q?,#9˲5q'D# LVcZ-%ʎJ)e/~KXkljV.N*ZBk8l 5DEqFyZ 8鷢K1_"QK#bBc-9aRAL542]\ 8c `du ds)(cfD#[,]@ZFHwڀvp* eu #iTבC[?C;X6ȿ /7(O $X;1WDE)F#J?\=@jYUBK GA!s&pNV70Fc 1`Y` PB9 8A5A @ߐ[~(t7-`c4 QhW.Yqc,D92/W"2cUq"Kk4UR1 @Q^iE#9!dʕr$lBoMoo9÷IQpy@)z{c=y-?Eጩjj( 02F}{e@O*3FV'9nc,PJXk;::<.5JɃ:s&R0ƬR9JI$R c^#y{8}xs}}O侜k2֘A.z#JaBj&fㅟIc5R)pQonvF馛8(vZY 7< >cnḁG{< JyY9<;ga<03:1_ѱY-صZƛCXzn0kƏLfi.^(+-%_,Ĕc^1+-ZEnYUDR54_Vܸ,I"Yo_FŁ:NcHUHUY#8a~zqW[BP$!R)f1n40x{}2ZZ(AY ,%HY3Zb8#71KQ9#=dVJ(Z yfZ*!yN0"0JaqVU&7q%^tkbe󗷷#k~լ^bEuי5{3jim]#:r 6H⒔Ri z0klJhFm8;}5{6 {{ƌ{w}P<QlMR_xM7ldg'fv[> `}]~~(O7Na3x6o1\m4Ҭڀ[My"8ٳfڨg2l6x7qd6FcԳv]GW/E5 h҄ ˳JqD#Fp)ꌳ5 5a|YG}4撋NfgKq7E~<|^{ 7'NoIlVw`+g`qg]w6mg/Z{T*/W^9p뮻R;<{ & 4hvxGse˖^{~iƋVK/dg/~j\z_uU}z6ܠzQ#jZE<珯W( y9'R9) eR2)4zgb ߢˍ! 6qyfiJɲeKK_Bs0!u슎Um-ZH%5IcT\< hZk@(wP l !rE;qQZN]^QeYʼn"x^#E0nt+9oP !Y3 >7d|ǎ>kǍ9rZk> xȐ! 1:`~מCvYOwmv)'?u:c3GwscǞz\|ԩSs$Y~N{Z*^wӰppɒϧO*c=^L{S4=sW\pxg]Z( i-=* $)F}RjelRZ@i.X9ԔF*kZ[„PBh3l.2!q,Xy2C\qW C&Z5"ڲg2-TrlJf2u5wqVUW3 E+ؕ`;nDDc(Qv4E "( J3]Wћ{㽗\>ChEiX!iQe:JSi{TÔ2e>%1$Ҡ"PikE}pB_X߇X_߿ZAPJUJqR{kʤ-\3N3V=M7ۤ\ ?GoNz0FQyOӘsJ"QJ寽ʝwzգG x>}zi|ן|(cTR9ᜦi/nQatʉ'S*KJsԪg #uG4hC sG<Y*B<&m0)J|cN4cRL0 k/XYQ'`` /.;7]v%gv*hc6I])9;pM&|lgqqg)5WQ$Xkkn ]oz5@1h!Tn֭mJ}]T $pf-[\=$Ie:'Zk/ <ԩC@E.31V."˒ dRkj*D`H) %(Xx qPlň ʥ(0 L =?ժר2ƒ&(hGnooCJPL%E 8/D=x77ݻe…)L ƸC 0qOE(#1 Lkl3NooF5e,W^/o;Y7՜0'68Z# 6{[neYw}?J_ΔQwNvJJB!D\)Zq:QnlGVcK.ٳ'!d֬YW^n=xe ,(`[*N>s=o6 9oyc~tBX)a2ixтcG{lWK"06Yl;~cd E"q]઼ pQwp&/Rca BV`hV.Z0iLE*5D&Xg\ia,#x#SZXf5rY?My.&  Ĕ2m2{b7&)tаaÔR>lڃ8s/XtQ2 `0„ J`ђ(N>7s>?6Y-Z;oӧOK,r-{8ϲ\p_4G{]bi6uqO?ꫯo|ny>`_`qZj=cȑ,9kO>Y޾>\uU~mf >޼4Ϟ|;o޻{fo:ݼq4}(/`7-( 榵ɧI);݇;LӔ11u1G?RW-ImBa)bb5F!D!Fik+jVm\fQ0 br=m$6_A$,vmޟiCC믽SOV_C=~0PZ\Yn.\Ξ9 J'LJl6 8A|teMw^rQ0gҏޟUW5pR)=;kG1^=Ⰳy{kY#ڨVbK^qm}{mͶxdV yM8{:Yu{'==÷ƌii `X*䇁¹EI4uԟBNx yG?cj)JVm |O;v^~qyLj'%umnMQRaTs)~yG5D[//`//x/8|`4Mbf?pW_oM}Ù}>pFnŚ}. -,TQP$D4`v]L,,Ÿb:q<3 kS8 -A` g(3E|mݷ['JA^XyXd]1j;Ap6Qx90h!_`!dT8rEJm20eaRTL\o5IR)Lc{yzXk$_*|\bBKiL6-m (έ qZ#LB?mRREY.,#MSW欵IQƛ[ZR Oń 3`5,63D) ÕkZ4&SJpߗh#*De֩Ҡ\Ʊ_*YS)R0̔ʵnՂ 4JTƄ"B- m!:qI \YgiRqR.oTZ;W,B&SR0VFk 6@ʕ+[n/U\mxVKZwߞκs-GadgSMl+ 1`=%mX5]TO9ъy52J,o؃S_o::TíB#7(q'םe S`\Kei9G*%1 cd6F#adfPJRXMal4R2Ak2ΙJp5Qh5R2J1 )sp`\lk' kA,Z):tM>ZW^y-ÆiĩҡoMRW9'ǖJFs>pwh}ᇇv[o=mڴ7ߚ̘2dɓ?3'Mmhj|\cJ8}^=k~?g-1cڬYI]xE3fL6m rސ_eңCaG!3%8=fN]`ֽ6; x3yGm;`+}]˅[~qݷ/AS\^Iiyld+O=i]'܏;EKF7nc'9c2@d* 'yjЁrSDSR'>S"EHŁ L˵{()JjݺWvJxs!|%2~ mV#mF j 7};:cxxhk@@Li0s0S$d1=&31` l} 9gk{4<`굫x}࣏SN+ƽOO=c@=?֬Z¸}ӓd֑OVjL1K.:co1䟞+uԑ%gΜTxᔩoyz1 jnm0`!DEQ+-Z~ڣgpgK_/8㓹Ϟ9 HcKlۡvZCEBݺuEUhgQyF0cherJT悲66b*V7g+-(  o*Eݕ42$VZ!h-ݶX%U~JB5ARc g^W*uYb(#Y# 8OZomDZ7V4U `0}W^Y]v؟#bF9;GU߷?[0y\ێ;#_?\{/Ė}Θ6[c##;G>%K %'8\ˌg[;4ӹqӓ3 >w1Ϛ9%XBXmd +kPJ x~fVZ&(c #X2St xRe 8!$s`QRē#qJo E@AT.ZRJFsy$IT*9.$ikXKA92RfYq=ܝFq [CB/Y˵uZ{]}5wxۀA/?\jp"\kkcYnՀi55˖-?駧|?\wxwrRC.馛0`LhkTYdVY 6X~UkV|e[[A?ĔXZ)%M /_-|Y[7P%䬡9N"jƄsꒋ7lSFp޽5J)R @0Xce. akT|bZ1J6` 'hI AXmE`c k,mZz} ;FLj`AC=(pi`AQfRs!KV2N)3 C0`,ƨiݪQנ^={,l#ZKƐ5Ve ЀjmSPqnk^tI^=(zmfq/Xg <Ƕifo9w|1{J23iji̳"!8V1k-RF^go?sY3 a6fm_?~]SE]O2%ǞfΚЫ'~z~l邅.rB9"_3w0 c|aJ9q-",'j0%`,ORz^VsC_尴.Sc Z@(rLX+8,1c|ZLZ ^'Sm4!i۫-oNdPQJwc nAK[+!$,I)3z?toh׹SLc%rB̒0('iq?Jj_ضU0Ms_+Y;m{gR .e#YK~ѱGC{n{G=:@xZL1bf}60`ߴ&]/R֖믿Z=I&əXf?j=#lܹ<̋Lm[#74??\}øqk#?O>'{u] u0w{⡿5g}ym]}~4Y0oE |A߭oT^ƍM6+~lѣjmmkE?Z6|?2iLRbp5ĺ_k!`hK8?,RZ1LJi;"hix =K2<\ 1!F1 c̙Ϙ\RaB& jIB)˥b¯1x]}7%a9ZzYG1>sܜ穧ڧO$I9[ouߟ8s#{=\+AۤI?_tũo6|v<IoNRpӍ|]ru໋UpgrIDATZ6'u ΖeJsX4qӈ,"pZ9=DNFKR癒-뚜@$rAIBKRJN~=)ZNJP)@%?gӦˎ; 8Q*tK$ϳ,O`(HG5&I)`BQ ˔illlnn>W^y%Dk}O8sUYn!h ITk䢡,mwU1HCI\W)Z*I\+u1Ik '&mu[>jm(aY0bS X nVqL)z&cMcC mJwdA;-EN!qxv „ e&%$f1]Ce/E}v9a[Z0AjF chimm<r`l7};|f.7EBJwVeS`d&gl2KV#d;]]2:oPV;&aV2j&Dɜ3l8ZUw񑃏ƄOXl+x}]6?im+ax,ֶڟzp+j ϛ֝u9\{ݍCI'\W@1_]`i";ø]vS)6w Zͯ3N 7LAk|at.S42m BI giJ97Z#)J*c]s;vۋ|`#J%)@ljqV*&n6--cCuOs]o\I=`6}pom|.+(,X>k"OkQ(IskeR@r}ޒK3gr\4)`+!k ` )aNYZK-B@6^%(-FL$˟ⴙ>xkuklȕKaUE S~ q p_LghnL#_1%1*A8ѿ455B6ٴ<a;*Fp"g(qcJ<\&<,1cƴY}A{566FI)SF#%uH6x7ںF[y!ʥy.ZCuY1*9]n[!iSJ 9!DVgu;jUp֩cj+0%H'Д70EQ0quZe9W:i94RaXŞ)k]ߜ8^L;8j4.JZ4M24%yKUWWT*8$83J2|kt!/((6@(<d,'&LQx1`x9$\Jkm爒X+V7MSߝy=>7|gϞcʥ(4ͳwlͶ1QK織c0K{( yAA[ 7EQ) *}{ṚT{c^T*_|ԩS=اy缭sN0т <8uߝvuu]ZC1hcz,[2ͭ- `("q.<< X__u rs^rܽ{,˒8h+_:AS=s\IBȅCJ8Z 6Kbt삂|=L,%suy'Gq1f„ř??ص/>t9gwR })gooSLqӽR)M޽{;^G-R/w ;2Z֕Z<ː>_vl((((vI !J))%'s8.[~}a64sF(:?c=V+5sƌfV_ o/rM+jucccK-G}g?[hѢI&]jղ>i(v ?)m͔ 4A麎 o' $IdYfr.Naa=4M=3J+ y'Nq_tY*˽VZؽ'"bj577sݧ~zCC[wSgznC`^?S;5\w?gΘ=?RJ9PIIW)((*I8 J6cT(iJ}J8D.Ћ/0p{o," s}|/ 6hc:v\v'<0#WUBȦoְFMQ&:_f]󐡗|nȷ_ y"0N֚cE{gN0AK5`@_Ǎ+J~nkm|lԨQ-:th$`W\1>osםuY;sN8$1 eBV=RʕW >|ժUC?6ڸg->cLE_"evZfqL_J)sU.hhAAA߀8}.cTe.ȣ#Rň`2X)e5!#E!UeSňiLk1&syаc\T0ƾQ ZR)̲!䂝@imEHczo5./Ssn}m fQEsgIUgaDw㮸T`@ &*I%&hLrͪ n\5h$.IOA lȮl20YS,wB CO:UO+13$H A oP$H A $"A $H)  $H AO H A | H(E $HS@B)$H AJ A $P $H R$H A >$"A $H)  $H AO H A | H(E $HS@B)$H Au ߩ6eb K A}~(~]Xa[.b-G@@rH1Mo?R$Hp݁vQu CR1#cLrq5ajޫ9jҨ5o.J xo*|J{P7!v*[P{ ,0 E0 C8ADB`@s ?z PQT2R nADEy 9P*Hk-63kSTD$E,QAHs(J<}?ciWQ*٬01M9=ÙKQEt- D Y-pc ~L(b(A"%Ha d paRm5x'$NcRD$D$ȅZ$L&#}hiiR>5!LHx; ˁ)~ɔ V +@skak˪ g+"EPy!U@HV>3@LN3RiaHY$H =(n=&|>/o Ęc 3;^gKEY).!"J?Miy#FRr#q?b.[@iS.lظۼn"Eܾч?7.5 WhO#΄I$H`p˭[0*?h*aU<͊!ϋANg3Bh+h6/:iǓi:[fP63 C1VH&D nCCC*jjj6lX: W_}w:DE?Ry:u1bȑ#?Q6Atҥ'Nܵk0cƌ|OӾ_~?'?I|?CٳgYt}cٳoc9FL7"PJr!}}7nhd2b10)'gK\RsB*{nc}?۠T`MQ 5GQd*p:եF,= 'Z4 $87_@lw>(RT>qHs=nŝ:uh۶mGydG}t-7 '#|wuf͚կ_7xs T=丮#|ojjjhh~_*͛w`#RuUb2 sT*uСX,Ɣ)S.ҸLtguÇ H:PHEB>ŋk۵knݺg @O6,FZu s#33w}ѢE:uafkS^B駳<٢Jb!j>gv'q"*2Q%0&=mlٲtjE P m |n>̎i:>j}#k!b4YA%Vm6ަWYM;_"|L[# * 2G"%o""gE#A G0 C=<Z/YT* 2DBHa*!cL>}1 =]vL&VTe>C(Xk"#(~\.TH +n)\YROģ_J8=6G>RJIӎ=(y,IHsE!m,^8`(R b"VH0 0"R@X|dMCnLV+؂ +e 0,gaքA1uvֆgqoK= h0 e`-Oy骄Uq KB3J7,V[O>uG/W&?ՐJ1[V[XԖ Pl kfͬ^hқp~n+!Cm`3HEZrtn\oR vE]Jd ?/~ Aٳg+dk^]]"f;v.fI!{qTR2 38y;w]k@D/BXH"oV&wc'ĨB K2.]477c~mTƍ{'EƍQM2*J͛7//":_|1TWWϝ;wΜ9Rɍ7(MA0c ,Yz< NaFQd=7p#G=ֺƘ7x㠃r s`E( fJLyB6y>Ӿ6,[B-oOUWul_{+[_C7Y}-DZH d7M7n|Ptzݶm[1\T^ΈRDܹ/ܳg#F\ *AGyA_|5EOi6m:u+q{+>A"`fc̋/ꫯw}ozꩯ|+x۷8sDq'塇ڹs>۶m|M=kllR,B"̸ ?p_WcL ,H"Q. ?:4 3fOFαcJgyڴiBf͚H䭷:3w.#SWWw9<^{w!Cty-XE=/bd$c?@kO ia*?]I.0a|r J,B%%)7h6o*=~ڏ@=?ml1,Lk`mRMFI,kf &6`K`&UĊml=p g(&0YK DAD3 [Z( ﮻gK V@362t kQ1 c=\Fo-ʏ+cQJ90<@٬H;*7g˻xs!k\\Y1;)i3)}F.w2bp5;Ztgāy\:-~2`"iPJe2+Bx=ZT ά%/J8NI&O\=zL8Q gy#|b/Ey|#a{s}uͥ'OXP(=kʕ+wyGH;+G)tRg*c2|qƍ3O$A}90a={瞻h"d5~K)ao|WBP NHBO bX@AWsmԢ| 6.,xǂ]Kx W)b$u i)bI/V1`X`ʵYL"zY|vh 귭x?~ХYRZ)-AIp )2xK2ĥ{[Y}ߗP&#&N, q`DR*920iҤRI !ӍN { ðX,&q0lP(h7m且;Dpg~\bw`y s'O>/^verk7o4hЊ+:v~>{,UdIJ' =G@:`#Y)߰aM;%܏|o}P/jin]d H~ʄْق-/`f6 @9SV)o\v(ݣ0,0Y%,1A*`VS5RPmwzъu^g=ƢD~zlUX cBcBk#V#h"A &$>ߊ| >|*K6,U^Jꜯ[)Q:@.rRҥˏ㦦&T_7onݺ-Z( C w`Ȑ!O? ޫW_~YZꪫ;jԨq]iNT7,(7DT($_{iӦ.ެY,X u3ѣG;}K ̒9)ɔJ%((͛2:|_F-Fr 2СC.غu˿I1\x ĄY:ܾ;Q2'G H;D q~ FD n'pB&Z SjڵCVf͚vPųw}WJ fMMSO=C Xti޽eرcԩS_gM), Y3 ~ G{ZB)XĿKRR&2ZA;tjCK0g߫G fy*Жbds{ ݟ1x S.Wێ;d2W{ <-Zk׮aÆPJ˂ ^z%80w\1Ԓ>Ri̘1]w˵ .\le27u@NIRmJ/d?w/N!]Yu֬Ysu{L&# ]C?̆f-y(0!)ND!;Cj~ 3KbnAd~URya{a]tKT4h9{c\.'ēµ%.%qIbUe#*$ 0^zrWlْfEاOio߾2444c(J( .`0 ͛W__f:"3f˙oѮ]($(/< OAI\@^S+k<6>H1,`M |b]?[y,"5\JiR֚ (+Vb&$$epLV*.{ Ʉ%ьpEPIڭ|J62-kM>03mKW?1;] KTdP,e$-0q9*Y( Pd_ĿV<F3fׯߐ!Cdwcx_fr{5e;n-;w/Mn-Wzı^)5'u]+WIbs=wH⻫G}>0{w_ssz޼y͛:uϟ/FB̙`km:ѣG?7oFeV [o}'7my^:>`**Jwqzȑ#F> "`4Tjݔ[_?ޒ ruY9Ri/JƘl*--;I7]j_ޙh3vBh92ny$GL_rwc@dsQSL9:M6 4i___?k֬ˮ IDATkYjeub"tr>jk5kR#r֭eW_0`l\.HB)N4P(OBL<Į_zիO;4|;ٴiS>?s/_`gy-[֯_m裏]NZ|yԴvN;^nX,B9sgԗUUUg~X$ C^ :m6m^})Sxw uUVzMMMBa̘1۹s3 "l%1bJ*d9DW,oo^W'5}tMFW^kkk x׌1w4WB/|_~/u8W¨a0]a@FIkK`"dlj;wK73vEF85 s1>Փ  qZ%ʶ.ȭLܤTh=+d-iưP*"s%*M6Ӷ}q'wI܈ӁH2Ԩ ڇRDҥIӲ9#׬Yn;ku"ʝ;wo~ǎv8 QJD[WW}G~w8aFQSSs-Gxۏ9R)5vؖ)Sz .ܹ33/Yꫯ^t1c|߿ۜ#ؾ}1&]zݙ33z.?q~+N;qcwO /|ꩧ9}]wOӶmvmgnƑ#Gp/4-("u*d^{vuذaT꣏>=]tijjr^'8j1ss;ꨣt`f={ {:477ֿN̞e˖M>]t"uӧO߾}AϘ1C1*J .>}zjnn}G]te )c>?ׯ_=db;&Lxכ>]yÇH8ƧLΙ3޽{x_ơ&Mzjkk(:_Ǐoll>}]s-0 vzE͙3?aQ:u~׊ɱ߭"  %`7mPT2TTTE 3[@ȰTB>8sKIDP^Pp&rr%|$*Kb)?%Ily1 keƔCiMc;۱!sF'tl|+uLr;zV!t$JCǂ F1m4fϞ5Xc'@ѭ_?ooпǟy晲4~W\qŋ?|x6mll#И1c.]onݺU&AEJo}ԨQwu1fԨQ\1wI 9nxW=z 0@) ==mڴ}k=z򗿜JDm*G9j(gHܞ/O@^c&Ͽsr[I YwK66)c/?&/3=^] rWPobRlᒛ [Bl ^>|D1g.w{wt%1Tc owZGbl u%I!'ܒZTbikpj !XPWV1ڜ42UdeŖI f 5 e˄ ]9Ÿړ.T>ebG+~W-xl]Qs*=Q-0UچZVjk#@A~t|ұG;MPF{6`-uӵ'".:6 :f8eʔYfɒ/]vΝ̹ŷHJy+'аcǎiӦ VVk ˬD%˚"kطSTƴ&3|W^-u]7c GE`LyO6NeCK.Djv׿ĵZ[]]-"?PwWz(JGniw4LҫWŋ_s5}Сömۄstm2JO>[|hs;ŮMՍ ‡~ح[o^z7Pן~>k֬^{ @d]kٲen% 'DY+A NK)*KLВjύWh?jꀵ;v/*jS*J (mEM)f@L#Ȃ|>JN{K/q={3;wlCTgxgs=_W̙SSS3uԯ~]vݻٳO>%2 \.f=?^pW^)_٥y׵k/w˯:f~衇 0x;N2eС~J+3x`gWt $B33nBdrڠ'DElzh+yVGSfaΓ`AV85e.%!*3%*{sDl5cIf_MѪĎݧ(F\+qh=YrP+.a&ط (0 Ƞ`R7~`TЮDA bH@lhf8Hkkk{)yd#~nٲsaQĊUYY0\pa^l^D-ҷo_),*oqĉ\*!R;v ꫯ>CO?\gE-ds9>QѶx7f̘_6l׾v-J>[fͥ^z{wQG5dȐ6m{|8tҥCzZbŀ_a׮]7p7 qwYg_[n;wv cLΝ:+RąP*n|>nf"ܹUW]cǎ0 Zx/Bcʀ0%N3I&H nALX&F l>j߱{Qs*ۈv1J"ť|Z{Yb!x?rkq2 %bĜ5b8.']A"Yٽ˷^@qv,J冬 5 z}e fJk͖laU_)=sogԖVi045G#Bg+ eELF3-\,!B\qmcѢE7xo-ZDsHla4H`T;3\>(.xpt {YΠbv*<9݇\,K\@q U,UI#ws}}_q3AI A(HSd @ 4hm}Ê֑_BC*ɐR`jଧUN/E\)@=,n_8>/`* 5 nC,Xo߾qz[ԝy Bw\+X%( J;Vh$7NT%wDD$sK8")*s-:jTr|JIa$ٷ{[%H 5 gX/ fhmƮ,c7Dv7TrQ&(QhAV@6DBQBqn*兜+vT6lmje7{jgfz+[}PP\6hMBVq_!C.".)Ș0\[}J?9éjBhOT+aنUTy!w+Wrdĉ*nBlJxQD9%Qb"(+fmpK @4LĀ,""S^ AC:; F;6NQ(srbN𺒖Sqиsz._(*\q\.+^a-,=Xz-tN9⸂lr%buS|[$ 4\:m%g#LN!sDz5G"kqgZF>gK 1pt@$(8$@)9?*@}_7~Bnv'!m,ˡa03) J +k.YʉWVtScM(l[h:4a&luuZS(eU ETچ5nEFQ*]tP䑢BOa6 ¢7I JYG*fc)c jRʷAӑJXq cg&ÒjT)HlT-YX&"Z̈́ ^ؐᆅ-:*`,yc> (y׬~\f>}, 5Ji&+Q$JI=JQdiيR5UTS)JaNgrmJ.QJRRʲUJG&|ψ^C+#bZ5x BVkmD` DJ\,ayUQSbG6 / *D֞eˀcIQdmWCA1[B +I M >b{VED! XX2)B "Oga)۞|hӻWO9bk grjR(=߲Uj7vmذaڱcǦ GY{&-UaR)ɶ[rU1F΋8d„ zǏgqcɵ~e a6dRʈJz^,Ǎ7a2IZ{(J,LG a sȑ# ebgDb}qF&bfyTrٳ /P J:ujhDEh/23JI,4NA0 ](7hҩ4y,R)?%$t)(>HWW{lϏ=Joy*1]佖0֠,8WҽZ-\nv B9L|*W`'S"_DL*OX2KRQA&qs B.s߯VY62 I.JlM;EȟdesTc᳦r#à觊V`73>5JBIk-XF0+@Y ]Ї݃r *>esgNG=]>" 5mZ AVe; ڦ^ι[FާہRۘZRlAQkwJdl.&QM۵iLT*ZZ+{wC.ӁmVhG1}E@|ݰ/ԦLUA*l@X* &lV?ʈEdtJAjRD -AT5G5]O{_Jbͦ:(X4\$/^To~bŊI?|$3LI_D) LuԽ6VQXSSSdsÇ/ ]BPR^yS2Mn/!6v{ݬK%ѣm#n 0iWz%b8vXiW+eu͖=jDa,ѣG[ks$̔5󼑷-W˳',+Eb1J3T*eҙ0 ֺ'SYIUĂ\s5Z5vܸt ל#G;kq&E b)E}kY宒rʸI$o"5C\N^:z;y4RD5{V}x$cBVl {sownOJsr u( 6|/ >Srۯ?gt·cp[m;TowВv'y SZeKjfe)[zv鬿{G^ٶoRU939NvnZӏZCMf:,:}hCm\n͛*Yjms[Z2UML "o(mARMhe0O<3U:HW-v`{,>ؾg;@6grPZKo7Z>~ЩS?#oÔ?,[k׮_~9G'TJ};߱555^{muuƲ?~|X;n,QFEf̘qvib /Z[nUTʭF&O2dHǎ1Oƌfot:k׮'xo~u<wK0 IDATҥΜ9.ڵT}Y"kkjj&L7n\:Tg2/̙3^a/B/I_ `f6W^٪gϞ@{ݻ555Ioyu/]y]d,tMfN,B^QYRfΜҗ$nYgcT+e̤3G ORcnj-;ǧQwXȤӅfEŃ:i.2YB!GWڙ0`e03*4rL %1̈́xeҡJ"c y~ZcUCW-\5sO8g㾨pQs%Tv:F!Ο??SO; UV 4hɿ?iӦ]xᅋ-zW//^%WndR *+Vs1[hČ"N:'?ҥKoVk[JA4wqK/4x7xcO0 8\޻ꪫX(<%Kmvܸq;s;찷~{ҤI{>/Ν;O^dɰaz-Daɒ%--eɪ Py4S,Zs]ykŻkLP}ٞ޲1\4/{Wpf#\tUSN9ڶq]a/]7EǘZj7l@AEPDb{!&vFٛ&&bу(bã%޻V3qKs;y>'Z5Zs9Jz0N/}e뤋GG}\Dz*uoAm%̈x SI4!@ELťLQ@tOuUoÏtjc-466"b}}=665nݺ߾kֽ+ v]w<-[kN)h=سwO=Z[[Yr# #!:DZ3gܹs] <nf=}/Rkm~KXYZ]]$ H!V---A޽w---e##>u8pE]EQssR*8;wEϞ=o nj~衇:uUW_5hРK.DxcK[} Bdo 7oWw9sF7ӮƙR)S[ |ծݻwzꩧw}]t tpŕW 8fN0/9:;;|3iR\ P+ae@dmjjҥgӟ1ׯ?>A=c}o}wMo9AQ.:`mb)Ʌ| HzkcWB LyD-yS($4eD @;RZ:$/4uZՄAu71*9Ck:[A.n.R-m$YeKuU_7}!As_9HR`x{ 騏?蓏UTMػj߾}Z_zo߾{4̜YɔC[ŷo耓|eg/})I8njdɒ=O4?Q}ϨԆ6˲7^.|7xo~'7nذa$Eڽ{E6|`ɫ{TZL6UT1*Y:sΖBjժ}ˍݻw_tiCX?񮝻-`3ϼǍ8vq>3|l劕]vŋ  \'lڴ J7|xC HX8'/޽{G=f̘$IZZZ֖aW\yETj-K.>}yQkiӦW^y+lgYSwرe\0sysٳsοfpsnÆ |ѣG?~矿dɒ~=#^z?U{}֥}θW;n-lE!3 yơ{_ٲmP==@ϛ3D̒DE^'I*YE֣kEۢ !<[=Q&QXeMikkZW;z(_(e,0p UPvJHD>1[e[ gE"M|߾թ A;wޱugJQN{ 5 I ">""H9~#L$T Hk/T ɓRVJ3C ;>.d*=c?Ai?9!*Zkd*5=%q?TE0(G#{?曛,fP9Uظ+c|8I8;r\[[[:{LrTH$+V3:70L$ CAw52ι"";C! k(ڻwoxK,rMFZ# 9"RP* 3k;Xc q#/^}Oy);:{!)g]h+{:yۗ#ה/6]XIK'HY/IGyE4yIrр<(Gʑ <8Vj @㢗ڤBb-.V;ٌg&6:2 kjNw:~vI)ݭBG{H~_yh, L3)9DPҭWG񞺎G&LPd'dÏ{?t}/SjH{eEQ i 5#?"W<8!sd t$RH, A"X&ByQijrQt>t@ `ǟ(Q $ g9BTΑAprÇxgTI TYT)H!-S0'|*Ё^u*k} ރsēQǍ~v2%o!rQޤ$rQLL+MA=bg( `çow~_xiV=h$IUhX@۷ܩ+O45}z&+oux !*kZ`/,&sƞ8lAJ! AZ BJȃ]'F/ 2_f',cPZ@RKq[ウBUuʫW篼dcMMD qV" :j~_kWgpѥcn^o @{(,JYJX%9ApgPmh"!( ?[1p_?Xb`11I#ޥy{{>W}Cy:{[kj,$舣5yl[UPZA`lRʺ%ZT*aPU=>O sA@e-Tij\eSSS5jT^=vԘ1c֬YSSS3v Xq*%$ qZKk}DP*%ƏqSNeJ D, \裏>:j'_/\0MS!P)曧@SSz{)J)%`ǟ}_`qV\ٹs租~zĉSLT-{ vu]'|K/ԹsŋO8c nOBQGu !N8QFue/خ]N:1I( #G?Mt„ C PރB̛7o„ +Wl׮O?c[ J߾+ (@D\OLѳg_RR͛ EqEay#b P&˔={:s;[ FaRs|pSS1& ˵8B @=@T-MMjAJ/fj1cFP,X0eʯ̘1O?ٳ{dsf{4u>#)̙e'1?λ֭[7~;wv?@Ԉ2ێuADIܹsĈU D`. Э{]vu{زeKMM(OI)$Bc*%:v[v:tŻx(A2GDRjT$|$z (:uR*7|Ckrt (A,5S7Gv̟pCD%PH*ŀ*6ߘbX0R =H"( Ҿ9j̄A't:tnӺ UݹzG_yO*d8;iPvχje=N9jqAu#)޹' 9ꈣ2J2j~ӡW >:/RP>j"s-'Y6ddCu= gt?lw95/ER+ʁ!sWkOX4QAV%g)dONx!7[k *  !PJF>cƏ/(ƍ7bk^J\h/ ._\=rL> 8*}zYf1bDEijF}q9GƘ1cƌ=ZksGu_Xc\(T*qj8X___(`Ńڵ+"2߿oR74p1z̘1cǎRXkO:餑#Gazjy7o^ccxC=6 1XO>yC=ԩ~ٲg !č7޸tRbO8G't=EAp[ĉ,Y_/<6@MMX68Vx|w<3ZϞ PR k}MM͎;8{R:!P)Y*%ZOI<jkʮ"sk cs3 IDAT5+D`,'MDHSnAeɱ(_XtBGJo+>R)Q(() ,jV|H$RJЕJI>YoQxމ9*3$p;yoߡ)ιn),0Xk棏>:#n1c]vM8qڵjX޿9eo\passϟqO]"Frtꫯ_^mmg}w:rQ{;ϯ;竫׬Y3u5WWUWw߽W+L4iϞ=3f%k^3g677tMfjnnC;뮺7׮mWWw/\_3ܱcQGs=C- ?ϟ{88ET_?Z8sR#齏SNy>E 89cMnA0 0/]v۷o1{?kVc[nEqu׭_ꫯ^fM]{ΞRPj|ڷlR=D(2đ=w5VdQIJ M $ɷT@R۞/8L.މ\,crQd`]FD9'ub6My"'JPv@JRQ,KY#R d d yBO-0b IIjޙ )(-;BiG]Vj$)#G~"HHU"h]aZy'!BPRF Qs\G΢U98Y_}/ZeRX)rNXJ9ʛy7[>WXd_ Tks+]^ۥR" MT蕠. 9/jm2`"H4:Z5OB@%cxnB:ϑ [A*6D$ɔR?i3s)x Vle24 k}e|TlDdale̪uPb8NׯsTƜ|rѾ2'.oT?0 *L$KDA+R@)eeIe0qAxj <vBÍlqo,ƘGLx{8q2d֒;w,WܤolߺI!,y  gH^K`2WHhڵmإ g@n7 H1J7 ryXC|QAdYs KykJ5(bSTy 2s.uL朏PΧA 7` AsĻRyT>b)I 4MuwHՀ*%MesP@y$V,,twQuеT=䔁&h=w;PRJ^^ƕ+%˾Qؼ&о¾jn}١6"*/.((I>㚚*5s^Je8

+W^1`tɾcO:R 6x&+8_UܭB]lrF}÷?:ه"HJ-ty8QP5G<[ZKޓV8ʼxb d# p'gѻL)d~@GB EZ*Z#F|B!uTeJ(qF* ^Gz#( H* R& !uV5RKO(pr+pAy\ЅΘ0N5CWP!CjQ(xoxO`=CD䔁Rdk_"^+!''1V)ϢS4Ʋ.TJTl!IedޓR{&I*ZqJS˅U$ɂ@aP,AjR)ZDz1T%@,3Z+fOe wk,YHaI%K,㋇R?缔m}(n 2c֖[6F2c,wJERTr=  B*3x0 [>bm;51_0&DH @f"6szPZ(5B Ƒq" "5$,oO"Ȭ/ Ig.3ZxBH8ORI 'В9'cl BFuDBJSf]BޡӐȴW]S&ee G{ ) M3f aFD@ޓ< PX#vƦR)%獔m+[B`.{ :P^vuVH=IJZAaD RIPJDA:@ial־C)7OZzR &-|vO'Eh֯k;THԓsk\*X5Qkˀ͜EAa3Ig&$n P d H)ɢ ̮JU|CM$MPSPI]q, r|ԗ̬!J8MPƒ#Ѓ@Yq 8/'g58DLƣYy &i:˃C'$hͤ!4IƦ^'O$@ϏEJef i"vcS"SWзn}^ЕwG0uhmh]^!^5f5tn]*eY5` rY9A dW ;0=I"AȠ-ƿeOdOAK}8HD+/CXV&A%փТ2(o <x[UK|UUrUidZ(s.M-R>*?%=zuuȋ_5t' a3+ߍ7]$'̧c%ƙSLa`f!Dl).UX6B+jLT*1֓OT+u֡Ckӛoy눘lҥ7pòe˔TR{*1{*IIbk٘icSSS9D?~+Jq ~m!kfz_~O>9R)˯G}TIU,yP,AN:t(2d5 X cI*X,yhyן'I#&P03HTTR H8%I3Ƽk׮c޽{ dTc"J4N4KsQegP߶8sG7^'&](f6kn)7O5k+)1fS} ?|饗(I@1h%;L)ǀ'SP,0?:BKEf a[ _A). ,1qk"P"Q2&'iR}OZ Lq!+|$e&)Crč5,=RR\J+)$M@yMs.Fh%^E~g5\{mll7o^ P2M(L i* RHu<%V $ 3443t.g̘! I҈֓!w--B3gcNy0iEH#G>\.'40!yfd|>MS6DEew믿wk֬K?cYlذ!wIJ/5jĉɚgϞ=gk=Νy0Rr-i.\/g&۲e߾`-[a(Pr'|rMSn(hΜ9,,9yiӦYMov# .5k?sիW^^1cƫ5Z)7O) w߃>Ȣ$ҿ|;{gWyeTd[g\c "O6 TDT,{ 6(OVUU%|=Х.Zqs/B[ok,7քAcBOXRJVDT5˶H'cmZhe'1&˳D(;UM544̙3G)0<xITDLTIU:ʼޜD&x.o-jh?3#Og{Şc N+W~ǭwB0ə1&1lWKKKMuG/ C]4MBDDAYIelQ.k:""|>Ϣ]m;q> XN,c{O@I:`τf͚uÍ7Xgkkjmyf FJr $R\sKs.ʱK=5@eV9g :iloYఞ?:sw<~{JA6)@W!BVl5P $I "= i;w *6!yAAKYR% W{M8p#r ѓ$/$ś{EM.x4ᘋzv{Zi@F*@"KD꿷'yWKy睷z /_|ܹZN8u*fϚfϞa>}.RW\qE߾}bE{kc=vec4Ws4K۶o۸qN:e˖7_6rR\ @W^yaٷ~/ 'zjv?O8qȐ!k׮B曣Gff&L8ꨣ>ïO>sΜ9sf̘qUWًcn`fCYg׿{nݺSO=X,~sW_}E@\pobx=ཟ5kgm3<>4hPXSN=kiiYj?LD{Aj)[o7n֭[{/N`]Af(U14Σ(8 !8q>8qO=}OYN;mŊG}k744< ^B, %K̟7{c=eʕ+{w}W^yƞx{lCCI˱s`ҥӧO_t?Ѵi~7557`J2}6iSv:m4!yם֭g:y

|8bĈM6qWreѢE{9蠃=~gy)cڵܝw>۷{9j *֭;V\y`z駟ķWͯ;7{F@ *ZY}w8o~k¨*q%u-R9"pm@#dfp؆c!Q1-)Oɋ}>ٷlo -AreAdIK-w@(>U:xܰ>:P CG9k Q_۫W??Kq]`ݢMCƘ.$iRS]cItŊ-x,G8 .}l\yqK38K.iKZ2qĊ'qU*3ٲeƜp{]:w//v:3ƯXboSO]p)'/>8pUW]x:W\q /pyZw[nݳgvҤIK.=ꨣ6n b!@ӧO`"d֬Y3iҤ/˖-;/¤Im8Cf9וּY1u{SSSmmmssmN;2|>Wd][w;l\V{ϼޤYiv&" AE4FbbG<ǠAD1D=v9hDDE@H@F5T;3{U?1gԣk.y)}o;3Ν{ꩧ.YdÆ pۻoӋ3<B`„ b>Z_}7tӭފp)SiPiU0Ic6oifذa<g1f̘w;vʕ+h:tz:hժU?6mDqiӦ{G&Mk~a^O>ԩΜ0 RQ)5t xۗ4hzȑ> 0)uQ ~ǎ{?xkמovMM x/{K/tM\p3RyN(0ꪮ&`Z'Щ!)B)BHBUUg!Tjub#G\E31bg/Z"@)=(1YkQ= |>ȶy'/_|Ϟ=C=^ӫ?ڬiƍj?V/^9rd׮] yvi :8d"?ju(Cu5k֤iyŗ[hݾ]ǃ;bcT;]֮mCƸ| 5à#+?Jlۺ9wGn޼7>h#~.²5x/77mϞЫq_7[gE!CKdJJQOS=A۷}r1RfoQʳW^|N+.iXo!ITHkw_˅CMZ\[[0V?BPj><4w۷mb}n]tIӔq>q`wt.GtJw.?g}v9#GDǥO>?[oeݳgN(X d.͛wꩧ2ywǏ177ǜOۺe{\Tokۡa])c _'liQ~9I[ҹ[xU?˖a>m9'bX D{z0@%`;!*~şh'mb[<XuxG3 8=%GeI*PM\i' }ѐco=C[h 4O{gPV6ð%Vq㶊SBi*sk.֭Yt?Sf< {)ſ^wu7oE W?wvۿ_pBdV4 nښ$)SI٨ޟr)VzN;4>}} (/^by{Xy/B`Yc#8}N<ħ~01o6sP*%e0R([lqΏ<ȥK19_G}_|Q)Yd4Iu( .]k<ڻw/7 y )@yDqN;3F.\8fQ@2hР?ݺu3Z)S  (a-T@&7G41XS__ߺukU.cZjo߾?QF sƘ-[`t_}FK,?!r1,Y1pi椓NZd !HcebE!9Dž0ݻ… #I)Ehιs-ٱcG\8g۱hHe˖͛#[eJ!Dιb1CI)rx;vj ʛ7oefB@J mFIBy,FQEa(L}ᇈәyPd@e%my-Y奝S;D3cUUƆZQFC Ƹ{xgĄPT*=}Wn!X=x;"xDcTHg9c@9s&mWvgÃ@{΀1/;r2ByXGoY Wh֭6mf݇ &EhaTU0>kG˖-Ôڵk7mt뭷J)N?}2ֺ>^޽_J3SÆ ι1C־v{IP:U9&> !78-Mtf-4noos{Ƹw}Z{!KD >|g}v "T<)naÆZjɒ%gu 7pu]r%-Zxꩧ;B9;>={._lvWZUSSx|;ӧO;9Ν;wc g9S뇉1k!'p׏=z̘1ߞy/1_2 *uBkaæO~ӧBN`ʔ)E=1syquuu=z#+5^<t={ܰaC_BZ6n>䓍1/%b9Zvcꔩ"X?{,5k? :jE:k۶mRJ!sZPBJB8.g^kѢr֌Н%O)X[f^Vƌs21 3׿i3k,BH˖-_0,Hx|ֺ|P۪U۷lْsymڠ(oRJ19I1XhҤINN)kfTZso~wڷok}Jw;b#![li{P{88@hml֯_?jgoҸoyw`SnJٳ$ƥ4J93Ƶk^`Tu?Z A@zdb!C ZkE*UoTr^l{r齻~Тdt M⒋|ZAkgR%9*vgBt:l:>w< p =32I\X$9}f 8ZvAG Ux9Os(*QZ۷o߾}LިQ#km޽hӧI'}Ey͛7Z)+r]wqΝw߻ 8a֬/b˖-(޽իǎ{QG1=̙S(Pci7ι ιc9RA#͵^;k֬ yy7a„N;M)9s\H))Æ ݻ7F*l1h #Qȸ’KDMF)9>_>jjjn6 _vmΝ[jO4E?z͛?SLc=_رcӧO:uj6mٳ'\z{1_~|gt+VJɗ-[9G}ǃ@lذ/֞ww7l؀M6+;555a(i#7|'{sR)p8ZkJI& f2~7n˖mG~;t>߹sgX{ȑ#͛n:Νq"-T+EcsI /7n޽{?y}{`ci_W 0B\TRJ=^ti.E{䶷z\nϞ=Rr7H)nݚǏW^UVXbȐ!RrW@٨Q{u /^z=8ׯ?>Wkkk||>+ ÇW\]bEpQqNEv1cƼλs)emmN*k_}hkkqZD$u*&.GLSvkE6kK>s߽>wumAW[WkZ깃~\;|ϝ+3\G޹pغ,w^K2:oK.i;:jq5KRN%Τ֧zm]${|)NJTc6t})Nz:oOʼnr;Se|?6P{|uXJIH-!Ufzrm(c} ߀?j߻_Y`SITb)7ĉϴPoë @R-iv`)x ~-'ExcMpZ O<2k~̛n>oz+Vu>+AG-^ 'zKKϭ^'̞{p"_[iI(=`a-(?:_W_ܳwQGK(՝wJ8Kq~2FMh^4m>slB9.<{Dn@]-YXOio=RWW_,)^Bʫ2m|Wٻy 'OmH;?U5hn8'ݙ>cִgʼʟ8 V43;+Ek5m<릛0\M?0.ݚ6k.By/h੧Q&=?uPJB1& ]bUn=ƭX\]}qszt|ϋd={^b)1֯z{|*$4U͋ T\[#_x_J!|ĉxX\J@5N)FJ}L]  wo}"JtwZ[!j;%`Ң̕Y9ss%wbw8$ L*cG TĄVz7kZWI-(PR3( ZJUi١ߥ+O>K7D=b:S8Dό[%QP z +gRΨ;Q? JvL&<;8i"uPe Un4&(A)l-,Γ4mxvI~9J<(BLLS))J^(("7R"jG2cXl( 6>hqFh2!L0)Iʢh}B2)6lzP;B3V*Y8DZ_a ( egCDifb4&%gYoZ~'^]Kf:"2l4-^(|,s&!$[4k ;G<8N2SJi1u`J)Ո\:-˦#{&A?ra<~n!XPד( IDATeM[(8\nHޕᔳSx& POR*!̧N_7r0:(O0QJ9cƍh(aJk4Sfŵh\qJ)6dUvlxE˓9=sNJ[^?li_O6@l!䓤EMIrk[`佭1:ZӮy:t@-8RMw??* 9e,'xꁂ9ْ.itPӣO8f]uh; 0N1J xO'@,')Wx kB4ǐ9S #~"'RX㴺:X=B**8BPJQfP(r!{HS+DZ0`PekmQPJQ('X* # %UJvhJV)y*ux)@ptTp KQPT #:kR B(!P)ʦODEi4g[vLZO/c_3CBRHDQK޻I Bg^a 8NP(1ѓ`m-s eR&8IvcZ uM1TWy0FTIYFSR\qf:wܫ4$)+Q9p!|Zy%EZ%P(GqTJ) #b/dcp^'G3ZEZM0a(8Ō24 31ԩS^+Jлwo!fP JvQZ0c-c8NB-jPCّbIȡi]v:&GS cQpR98jPe{O<!@) pPU ;J<PY dpC% */$eaL+6Vcrc,$ M5! `(3֊^#ņ鄆N7`d`17ekUJu>AȤ1@cgGNbAe 2hc`0߃uiDQH |>ޅ_;;7Fؔ" `,J4XaW M5\ne{jgcTQΙ |X7D'2 Fg5J 1ź$I0oXץo(Pa0g(Lc7.L`HYs$Tڍ( ` Gw(O $^._\YYmf0  TZ}X#3ޢ}|] Wq)}\g\ (AC)c1rZ0K41Xю_ `uƞ$( / md "6x #/ִn֩{;TEFCȠp@< w8ƈ(94p)>OK@ni8qBgs~B,ǘeGqgLz@<DemUF+h˱ޗ  SUhМaeDDI)'Mt7a g̘~T)qe-x𙗐mgua*3 Ӵ?U884L5px8Kq˰28pJ 6Yq8RLM֠N֔ap+~'si,&_ NxZK)3 0xxпmۚ6m{ ` s#|_|{#FÐ1`w}=z4Z9s\r%s~RHJ/۷g5jT۶mkkk8裏PY{^61O/i߾ѣ'LL>.R+-[̝;WJym6I^{T*<#F֭[.cU_8x*c!sPUU˼ k}K$zjcʡRJT (D?4&B9sMqϞ= `2FXLrDORYdDos a2V1s)JӧO4 QpBN}/<`ŋׯ_k׮f|. jk <7*Lu|M 8'm3K{QY,(ł͛5KI (8eAh s9U=Gi]۝BZP.:OI7 XJ4M5+ c=v9h1=1P:7rKMm-?-?ly◿,J_}gHcrg}Ν;-[vW&Ip..Jzڼy7>|xXܰa 'k|>5jmݖ~wAއa($֮]bŊ\.\vm߾}m۶{={>|nٲe\rٳv{wI80؁)S0$ =$} TJ55U6SL !Pa,(J % 8SZQwcYsװÙRs.B!6N+XuqpD)m(cXB6PePjDPHI(U$ƣz!ETRTZk㊥DiZqʠtTkRЄ( J/ 4u(R9'`24GT+[KMcz;]2$m94a>(hZ؀WW]ڝ~wft[C ,' !EN 0]8\'C: N87޲֮Zs΂?|MJ8N1c <_~B:th&M_B JSO=ծ];JgϖRN2 aC8箿Mh+SL6umݖg|7=]vr*x<5l (rЪR+9zwCTB5hj0A$1a5 n|੽&|o/=|Ķ̓v6%V ʝ5Y [w $Isی>+;SӦMR4Z+W2R|u饗^:S}`k׮ )0 CY]]߳o>$f͚ܹSkw9 \{bts=ztKӔ $iUUUYkW^}iaGAV\R\8nԨў={+%?80ICkǩ1OR|~s_EsZdL2~mֲeKcl.{w~͛7?q_{}QXܱcǼyZk̖-[rnmÆ ۶m{:wnx2F}Z;k֌+rҤIJnA6nܸ-[`׿> zh3fؿq,Xe]6~xܘ1cx$M,YұcGƘ \>`Ƙ_s5RrWlݺ?_GQ@Jׯ_yfcgΚU*&Meٰ( gΜcǎw}߹s̙3N:s֬y;y}K%Āc Fk멬>G|kKS !N[[5=Wa-dIA΁NJJWBk\rkL%%@KX4M@Ap4駟nժUV4x0wNSl6HZ7jTS cT#r BVRJJ,Bߎ vFk"R<B(e Ə2Q#A$FK(ޱcGW^U3gNG֭[עE k}B :[nBC޽;˝zk׮}뭷0|iZkR{YfM&K.Byn;wnEW^ٌqZkΩVk;dȐ5kּ[[nݵk!dz!eVXq=DQpVWWϝ;s'VVk~vv# "$ :tq;vl=rU~mݻ ~O8(AiW^i޼sn۷ٳ1~k;q2#s8(c4\rJE2.gUk =0A?t<'g05;h(lS dFWBAؘ=@,5sJW_yoС1pm۶i4u'?ĉ} 1|?nݺM%ڹsg. Y1&NY`>ۤI#8bιZkMHm6۶mǡ:{>Hk$I> C靳"@5 Y}}1-Es&M %nm?ڴ W=RsǷժ06|Z͘Xk $Ua ڃ[w& Yb#{ӧ_O>y֬m۶ӧϠA֭[  =TzJ\.Lao>3Gٲe˧~?~|-N?}\O>fٳ\pGL81Iώ1 /?C?sX,bꢺ: afoÈ㴦Pj*ڜskLOү_?t)s5’2AH)=gqwXko&$1sR4nd[lQUUivi֭ӧOGR&Lf $0n;wA?q?B'xISnif˖-;t0~xT:'~G/СCݎpTԹNQlXC=1bĭEѬY0HTF5`f[Wʊu֭[ !n޲U+km=ȟNac`>rkx)h!d>"V2*Nx8E[dM{{Ʌ3z,KJJH<LJuz< 䫾&_5YG ~=1hښ'^4iT&E)U3OO7R:vnZ`i?^&Ovig=%NQ֜ܯr{|z𧗺t9iMiXԥW\n_re7Lݏ:Q#Ч~ `R} z]5kOs20w@~ hg#@8ʯ7ha`ba(m%g=S_:?իW ! pEq7n8vXl( Äbƍ񪪪{*=Y[X^tو#,XtR̖-[*BȘ11ZƍA_!DkiӦRk׮'^pջvb]`AYK R)LAT_hg3`45v9gO6uǮ⏯7Pn9c2GO-t>¯pa5dY/V<8DD8Tț$ [G dxg_/XMZ>ֺi]tQEI&@H9B# :tb›0m4BaP4iR E9g@i3`@$IU*u>y̋s9쳍1a Yfo^ ޿ֶk+ 'DƁ$J1b{σ{ R,Ϝ9s[NtJ|>{n$/J\.ZdI.۵k  (Jsy( .@p~P۶s 4klҥW_}9ӨQs9[\r%Ϲ/xŊ\'v!Z$_hQ߾}_|Ņ ?~ԨQ555sVR q͛qZc#^8#Ǝ;qDE܉'ԩS'pQ* \r)Iy/~q嗣 [ok8E]4eʔRc1674qҤ>}`b& (mGdXZ X5 !8B@9s&Lr2P{ e 8z?D4MSƘ\\^E1Vp')!$1UIjg`_Y$URbET*U RPb6@$UX(( 1\꟦i.),(8CŏQUB1 ïā:gb1QJ s)sueNB0qR&`-2ʚafҗID2( 55Մ^>t#FdӸP5( :X,q߇} IDATTiyXB%3E"M+"sirH9sRjIT gTL9[&¤ m1& <\as]}y ~9,K9W%+mпGRjS$I9ߘSDLRZJ9k֬{M6믿~뭷RJi0|YM@YWPN ⢬Z{(8I1ð*8 o Eɳ`"Skq!CC+A Y(pK1Fh|x)Rkmjmyk)z@Ti5ނvb|M:>/sM'bB4@{P{O^п/'zGH@(qsx%]V.d؋H!@P€PZAAn$  I)+JeE@jm10IJHIflYXDH Ae&R(LYr(1(J)QX 0G ?c`'62wZ$O$PJu8}Y0bZjE=-O~)ERqsUUUƘ P7T1e"չ9'iB q+BH) $T!@H]]]uU uرCq)(m4U\O4Bk-&Ea ,:Ix=RV@%I 9 2Z\>GuB;GBf̘ANZsn-3X|'pp8RbNy|b)R+Y :݋|'MӴ*SHWWW+aE90(MqB NR ss3B!L2Wa80A%Mt5F$I0HRCc&qQLqB+l GP*(&{ņ"ˤ„p%D0wB@i%|52 (ʈ = G|.R/ֹ(N]}B~&)@1Ha& ( J_ՔoSrO=0:@4灂g 1`DQ;!A=P֊zJNR' Աo-ƘRي!b)HzJsP]Gle+ Jq筵A s0Zqm(84NRk-TW !0(A{_. 3T80PTpش@@4pI 角wRLR9cY7!x.Ea@(1K *T5Ơ/⢤QJq'ˊx#!D}}} ky(b)xPP }ƘBQk~U>K#Cp97$ ^tv0[K6V$ %xZ۵xO?;p'Le1Ry'e᳟.Po,,J?g{9RJ@@Q9A$Xp $(37X#MRsObaMJ*ctY9G7 _8L&sP8I1AB0Fer4TR|.=!$IRcl}E91k Ss$C#LNJ)$"yA 1x~ M)/1(Qw`B)AXux<錢([xPMpJι&sJȓT%œ"&#MM>[)1 X*RRk܌1AҺ8݅R&(rR2@9<1AIR~R_*yPA90]__ߨFil qlX)ej)O616e%-~FC7q`Ɓq`|C,&uJJJWJ?Iy;c C$ߥRRk4uc#{¹LޛUUikXU < J3L (6"c*(a3 AQ!B@FA& C{oUiǪ\B@Lo'w~uO:gs^YϪ& uRk 9Pv„ a8`x]bW|F5jxg4',rI.6ι=ܳ(*y$qKu_apCQeyǡ)+Hv'8 'jgΜ)$\622RKԨQFR묙3<גYƱe,$Vш4*nBL9ʲ CAYZTj}Yy!8"(iF5jX(E^1cR= ) Ͳnٯ> "𞫊j%2""Cm@Y;o]#Q<*Jݬ *YpkԨQFRq\^ |gu։'ZϚ5;?yn:}gm6G)jo@E/kv-pmBv{ܯtCtoQF5^xǸ[g}zꩧtI̽MI{=#+kƾ/Ι3va_0'>1w ~GʲѝwyL4i޼y?=g*K+=mZoQF5^xǸBaXPN!R=>l|u p 7cѢE{o譶jܹ|xG.]c {9(.zatXEQF5j`YW̙'@{)tER^^.\(*/c*_rs̹x≟K.6m"z3<}!7gBk6K2רQFg SA݉X:扚>1b7`_|N,ˈjm6W]uRjM6G>r)=CO衇&Md}a(v;L4 Nj4ME^VeVըQFo18"k[H,K**k-H]wݏcԳ>}-MCkiӾor!pA=?L"/R .k]W&L`t+_9ZA5jԨQ 1&0Il& c.䒃:h###r D>I(0J!(P3KיըQFo%[3KcIDpFkMI'4|J9lΑ4A#ɒ,4OZylya˲0JHA73LWտ=Z5jԨ߄[#) d<0959 @g,*fzdPdTH$ lQIr @+h46ڕ"6x ijmSLQ m&ٚB_  w~Su^֡kԨQFFo Benyd\q5ԨRH:]!*>8(Dye8rh@Kؿ:?̽@c jz[=5jԨ0IX)@WP $ū\j42w;ZPA)4 UxѾb Ϫ.IȞuBF5jxŠ XŜ[%5x XkEDQ,>}P1hw4,1kkRL]bI1#"PAoRF5j9x{ r3e?~^ SsBPC <%LC)ruOqٗ6 =~\)V9ugY!{jP 1+a[l `qiPԿYըQF w?+QD, LrE/{e_XB@d<Sj6&O2Fm C-DWyʄu*#XKf\](Ix;x" 9?p#1baci)R2o4GD*/[`7uI.JMs9qq Ð,jY{y0 +5r.9$UU֪1 >"E!/w2 hgp3/,(Pw:y$dY$|j4WF6MNl6un7cUC]= uQ:{zFC⑔elĕ%*Ny]UU&> C2J0 $8yP/R Jd !0 rޏ*K+~PS8[JD)%]erPJ c,MӪrdrg{7ҌWb H2l"xǣNըQ㍃>WŇB-GX6F/,YS+ {A&aYZ1sqh$u">h_Wan٦#"|̜&!dYiV+"McD5>\TFo&vQ &11փ@;#A@e=U@k)C??T(#I E/"M^ ^3"5#ɔ{_Z5x`<@H8Ќ @*$#@Dbod]E> 5m9]_{һ7h!UZpUF!y֫v9 M6]WH=vj9$_E٬JBa(Guy\amcݮXb"@-Idy%-r<46dB4~(|I$Hƨ,+8.B$tnF~& <χNNDB$VZrBist|Er5M4hl"7O<;0uZ{%vmƘ^xO /^\Uկo,Kk>{EIjcґ;{w?Sz<Ȉbm; 7|w裏Ξ=(Yge-ٳOH)%y͘1o79wW{w%Ke&aUe(|̙3{., qqι~_A/wW976 3Xd9sI'5h ! IDATUUɬYƥ^:|f8k֬K._{n[nO' 7߭QF7oK80ḥ!IBԋK:Ͽr ԡ'RMGd9sz<[΃w[,eL[ d.&=gdfH @+b3xbb ߋRL(ɎgUUq,ulb?_ @=W 1;Et\4p>}zӹ?Jo >?p,+HDFCaZ8t!Z4cG?ѧ~: (򩧞u]vsLZ_|Gy/>Ș?E)S}N@BOQKX">_f͚%SORY/Ι3O8ٳg[k?oQk}]w]veqwRֺ9sr-ګ+O5C}4]`AO>DPCxhvٳyaO9O?CO~BDwuW\IՃ;b%QF7op A[k{1Ȋc1yF/+LLT+UIb\%J 2+' FɐVQik`Ff6ϚqĄ+@Mlq$E bIvbf$$ 4RLh؇\( & 5x'_'Q N-TԓR.PzޛT43#J^j/Ko@)1hO>df>%%CiҥQu;M6hˋϟW޷w?su gmemEY"b/rEfsڴi馛fYvqm8iҤzlnnԩSs1'O׿"wq'(<0LiIA N8kٳsHK@Zvcɲ]zWu 5/R* %˗7 q|q̙Ƙ>:82c])QF7oJ&W <Ɍ-112`rzihIJ :A/*<,؎BX;ut *Bf"We+ >soYqF<3#23{Ln53;⬢AԜp3 d4tJys_Րݠ_B$cxIa,K?3Mɓ'/,\8 l  c9&/(FFFڝs.Chcw roa K̼n]s5>Zk|?~޼ySNϙ3^kN=Au+0 ľ1$ӗpUYL$\&h^zi#M^zE>裏я~4бҗ^z韶v VE066`†y'߽馡ZK.d…Y 0;'8qh} _n?.LY]nV F5d=>{2 % DhBM3"C GyN'3alEy`Q\[#a?D/^3Z;U:\U8 øU*@UUZqv+~n.o8zh4M6}na 6n68*ϲŋf$eYAPH: GafUU)naQs΃>`oƟtZk%Y&5Fo zf,=^+ "r<+C Zyi T=9ǫ3!ǯ DF&ȕ>˺aQRXmLjхg%@=.#`с D HȄ=|*B6 Y)Di[V`ko)jN)T29b2D kp+zQ) D,]jl6vi믿>"\HCyK$06wg/${Z6{Lk^{xW]u"'?o;Gԧ>5}(^\ԉ(_k%EQYIRJ2-[y$Eo,I(<gRoru-v[QYeYog̘FbQqs% Ӆla=XYFt$*|_ 8x≝v_UW]{l6,Kƾ5jX%a1ԎA@ihxh7+!HQ{ϨВ_bA^/&RK8֕I ahm4EJU@^".̀dQf{b hk?N,>G qE܋V(:WU3i70Heqw=QKkT044!: U$I.gAJyZiWO{zvI&'?9̜9s˭߃FIy<3>G;˗x_җy,#Be#ZVEUFI<C _V[mLfecjiGa^s5-c=sϝvi޼yZ͢ehuӟjΝ J9aBHsλN8c>fy뭷tAw\'&f_\nPe;Δ)S8v[o>GD;??DQ4eʔiӦIϹF5^;ƞW(hV uJ//鼸x28"ň3 S BNQhU9Woafń&0ZNAe ɗ%Q 6 EU9BY&쀁`31`bFI {,KL(mɓ/aTf9z뭷cD2֌D~#+SW8*ko>} 7Pk&0g?0 ^{g>}a2iӦ9ӦM舴 puYwu["olvq$I`wo5{opuYs]c5>OgYe١/|:D>Q9g}s;h]w?|ѢEJg@|8BD3E(zODfCv###|:Z)owaI& MרQ*cy&@j=ȄQ0@?-Z2FLDL5Չ@4KXc+4W*npF[3p-~ᇗ_nmwq*3k0`fC =*&d $?"; 7\2s 1 OH]bPY4!E IZ\n}: Y K _l"^r/A "%0x"s9P5,jAUUi.[>$ɠfWnwEQ Qdsfq3smZBXA>P ERij-RT?E>& Dc$" D CcN< "WΑ#d09fn6v'~"Vn q#MXkJZkP=$@%_iv>F5LQ@ )@uD`]AVyeBmK#"F2%(+,= Xk]clib]c뭷:l!y%yP`|z-U)f RLWȆ{(U Չ b'&0E^(T QsnE[li\Ԛ'ی o}c&BfEQzz `1P++e8y^HQa <ϛF(6^)hn.4/D% BgT@{_UVz9 C( q^+bcL ;h\"r^iO:HON.o& j"5{Jþ w5jqxXbKxe#ڳR&ąup~' VPG GFw }Q`v޷5v2 5\T/`s`g4 9_dap67 [jdjpU!sX f+i~&ѣ>^`bU=N"P XZ_JQZen4B !ѕwXtV6:&l"- ߆[~❈)5p?5aۙ O :#yK}ޅwC-$N0p GdaPZ{"\1'GJ.p‘/0?hLJAhÕ{LN+fE1=Ȼ묽A+PUyZ+f ՋR5$qfn4H`,n G]DD,󲛂^0 O2L,ˆYEfttlxIR!s%Ƙk u9QB̐MR"ijaɵq/Fq82;"OJ"1Q:$VJEFnw4gH3#oR*C) Xu#*sF\TyiRU4oרQ_4Ǘ{jְtttނ3I+\ 'hUjHh=hրd@2M¢;0(ز%<#t3O<:4iu7ėWE!R*:iPul :u}w~Պ?>EOÔ)wQX7 K{U+?اj23Ͽa o:ht&;3inKsdY6443ZwFdžEljcP $lwruvi*hOL8c 4Ͳ2MSItEEU先0SKCZ{H㤓N1cFa,C=4w\8#˖ڷ`y.nJp,nvcӵƱxo hp(]%,+EB&Y1ŇOvWk}2{YUə9I.b qBDoF5VͤcT(@YNPJׁWp"Yr^FGb*5(6[Fq#5{vŇ?t>{Mo!UE6 x_fIcltykD4y{~O|_x5Z]w~V\uƪ5[52g" 9$r 9y@(HNH蠭bֶZM9~eZkq$%AU9kmc YQO07GF;B4Yz<)a@{/Ƙ4qX뽟})n㘈:lҥp%Kn hGFFeY6I0L%Zv/ 0o~gYe.iv]2Ԃ '{E!"&Io6¨*f(K.:Msvmy^T8POvˢ sNsF0Yj&Z)gm$_v١)f4sF#Mnh hdXѰU1EaAڪjvYY Œ 3K ᡔgvf~ՏF5pw›vA(5%;#)M*ЂUe_Ll(Q F暓jdHo_1O_Lv{E>l$I{nջxx/^뭋CF5d,|O>tɋ/l/uM Q#2#0gd&&p Z' 6YoI"_(D@K{&Y6aB駟=Wn swuǗ^z?anYg?.\ kk-Z馛1f̙{챇x!3fc=gyg3gNkw}?SK,tnaEW_}5\m3w}ϝ}Q{~hC9|?=s\rg Șg6۠U IDATW}spbysw=!Ͽ_7>Pӟ~Ϙ}fyV|刯1L`nCF#m._֞0EI}rs9]UZfCwyGZ4 s^)Ώx}{G xzGG.|a]v]lyi.yJ{I}>ܔv/9^ F?/O1A ܳv?{v$1`'?>Snѣ᠃>?뮻њkIUh4acQF 9I{@ƞjT.)1,K4﯑R$ ٟQyуb¾Y ȓ@@Ʃ* HJFov0ȫ8+ M;xhBB= nh17==@hKKW8=[kטn}%OYlдZVh0QwAyV"$񓔻ٔ=m26_{ZQG fsM_;|1 ~wI'A0wfy(8)SuQ ,xx9iZ$I~v9wy?w:N>y;:iӦE]kZkH)fΜyyy& O|gqFQ###7t[l1cƌ/|6o}[^{헾%IYk&|Rя~n:#YY'OޏivE~|Zͻq7pÆn7#8b̙sRFkԨQc%.EOu[ @y*}&VU D@*%{eR bz"rNkQ L蜋 BDlJYȧR`SڡiGfD8`6Z`OzRt5Tm[8E)e=pqΙ 9{xn'g1@Ev2CSFE3V14sĞEDTIq +`fԯxI#2c}E GY^tz'P<[{[uw=;( U{{LyŖo-1{NN ,Kb/jl& Cv։ȒRF}^~_' PBU._Mg:0+ c  ƺr30 ޲U_)<(ƐeYel qCUS0j40>m6He,V1gI"!?|Avfsf_O׍F4#$Q"M} _sn_l6nwvCy &8?=$ )_ pI 2~IQj&&I2 b2SzAΉゆP ԲHtjNHJjk2B-{$i.Abb 4Z9aSk5u]3$TbV2k"u!gEWޥamA/`^,yё!^.{}􊪬͇XϏ3L7p5\7j?}äQH .t:^m۶˿W_gwޕW^}oO9F\E$!?w.[nٲ.袋v~?.I4m۶mܸq۶mW_}ut袋>-oYv coYUUvs0(㱌1gu5\sk_[j֭[<2DTHӴ( 9o۶98jfX1OkWf͚/}K^{{^U3[:B]_[l{83Kڐ+u]a"@U E"uK)Ӱ NFcA=ȄHj{oI3 P0|I]v7E5k{_?Ӣ(@tͭZ _~9׿ ox[ 7ܰb qv}gw}}k_W޽moy[D.k4 "'\ &P\H`!R ¶ }^44E%w1Q15h=dwώٹ6YOX1aFQfz'?ES^̴gi:htܞŎ'wY70YsIgy*+;Y̲lͩv]کuOJ[mѹAM&9#+*CrŚ}d~Que~QH90R(W0?wunp/=c_w>IS)X9iĀʕӳYEѪH#J[Fę9v;hD fTawLQd{D ;W%$IZlvkjwb-&^h*VTQ~vTT,jzY1ӞL(֭[{=60kGVkmߟ**cJ|M✋1Wc9LB &PgGy P0Y&XBg=ߣT /U CHar'@ћ'2hMiO;㌓Nyf wY#l^6ŕ>(Vӝ=R;8nIw=E?_NDdcN~ڹ}g[ryw?luf\k~6<֯kmݾEqf2)v>,HwX\>RD!ޑIDD".l4 @G:`o(F3sUQ1HښtfĔsVUma6HI3AID$I&Bg %V+ fs׮+WEEFYEQivY5\i{c1"IV1ιV-,~g 5{ԑ+Zꆵib$bggF+cej}LhW DhEDEU5I2W{Դ/k/fPIyD ޅN{TI͆s^h4i"խV(*1(jŊr_L0rx T gj$" aF`q%IEv=soo'ti5۝v1/D4==bZцlu]C+XљQQ;`LT83Q(:UUwط A⻋#5c,˒ggͦ+uW&&4*cJJ$EQM>&`whMTXUԁQV ߰0xƑ7^uW pڣ/`c6 'Uчf`6EAi=Zf}54i~nʙ>eԠk ^6֭ޮg\=`5Cڵ @ee}4M7lPП7JUGmö>OԤ;|NuBB*4@%m$O::5],.q2nȡHt0qZUnbwG4k$I콌ؼ0.C$Ib N$IG0%^aZ41Tk[$ i Uݷy4Iϲ>nED$ILreY6NDL!x{A CBՊ\{榧ovUKG?}@ٝ333.n#$sk`PŝciucX(կ~ly晱nNbV` &xd)EVچTSFذ~ _T%1>j}]&$k>SOjI :e!_>O g=1'|={8Uv1&PVU5;;{ ǟ|3ʠ$"?j6Tu~~o~]U yؼY "`R&SzJK.ˁɛ1 "2O>ḩ64*tFBE魘-Б`0'?Y.u;811zr>DՊ!H,s^o|sw:~?>VFKbD$zqȆDTKUFl܈[$R1<#fEQDC< &`dx^J:Xq6옝߱TK$ҥ`aJ3c{uSOlMWظj߹ ldݹ]]Ďan_-:zӏ=vGݘJGf%clbak#$o:igUP 9}"֮\]tI{feZKuY('zZ%icg6Z$ ~:kVAI~bU PJ*GB!XW͔=e#j &wBqpV3P׮h|75ȇ}{1~v-8~]nȳn;55o|Evm'L_GiH)o}[_׉9us=;g? ߚ_|_?gy%H?~&*җƍ;g] z߽߼;38#VUqp.L00u"E.pQv!!KqQGu W% U'w=p߽i͋$9%/k/U׽\aP{G IDATjy~o+JM lK'IO~C?}G~ӟ&.ИZIikoi9stW8$R80O>ްm%g}KuU鼷yfS!cThI~0İopjI|Ɛz_/(q?ŅH{T<:3fx_cǎg}sϽbrbff+?o9k^Ƙ}sFꪫo~뭷~׻u7HUo}[?ÿۿ'>q'?1k䣏Z50 &`L8!!;MlV6u2ݹob=lx0WU]O jͧ-֤0Q .NӴT(պ*Wo5op>U,E\c_t1{++4j_m9%'ƨ9ʲJ !U'zz3 o'*LqNwZ}ϒ4<SO;, xɲ#<2) $/;v Pd[U ɕ$c'RťBwV=J.|US/^`,@1QPfGdL7ͪ+22*.W_w3LJ뮫*k13sַ?|(, onݺ@F+T{OIjsӾ8Ęz\KU+瞋/X4[.zhl6c&Iӕ+W6}=kmRL0*phiyE/.y!XֵY^zw?GOe༵6ެRAU,@m c[[ZƊz( Č 1w"(8(!kDBζH`R1ܞUf<AmԪ*lZR"'KlaC5QUDPrajE!,E7 jT߸S jʴJ1S78ĝتM9Q'IZ3'I꜏ pQ 8M}ETz~EPIB 1wVqo*FvecEID(QMU;DlDl8IB} 6156q~!o4z*۝v3S?y &!U?!8 B(+V]0NQUhjᅊ(*_հ8:JDqx$, QC:p45 jeEJNK+JladQI8)BUHPv`v=cP /akSU) xjCB uw߾FVʫ.ۖ(z.DBt8=HT+Ͽ?SO=o}[O?n|ӛt?裷~EQ"C\,vB] &`C9pT&?c/GY}D{.3,Q?0I4r;YX RDUX"P n+%\JX2 tPJR()c> ~ʂ6I1 B`*tLP(IR_ k\vYMpʱ|~+Ve/۸qW_w^N:(l}y꫋PՏ~|嗟z'pBLYvG$I5klٲ,K6fÆ 6mU+Du'` 'О~B{lxmT5;1i|wu dJSC i! ,Ae;DcwBPeB4'bVAH9n/{^m0 "V$ y@hTOreʯ_v5Fd^|&fǡ3EfUU+VcK/n_ս/|9Cvb,eYtXcuBt^Tgnnu:rV^CU.oۍFc˖-O>_{ы^lUU%I@ T4yޮL0v>o6Zr}rҀy@.Rc@EPUQgEDUTE$,oRt<$/E%Fՠ(RP*o6@7^xin3`KオC[ʕ7s̆ x㍱l~,2S@zwL?mҲ,s7njƓyǠ$*`G*~kvu]cQJX=/Xq &R-<17)Ƕo5-=%y,>($:L) gj53-Kƃ9ҲY ^FGG3’LÁY%]9EȾ?+)]8~ê\aI d\#!fB9WEt0(ZӎQЬzhYyDY1CiL?Xk秦b=ckqAU0?GFg7Mc ߔXl:ޞ` &τ,ȝ1@EP:e>&b`˼lybD|TK&Luv>`հ́#B lM :sܺ#A9,iFfߨ"CX1rLfDA.-$Q"~ǢX a#lUًR6tӈ'B@(=VjF,a`0gJbB] &`C+^ssΦWuHW?ʩ#0lzHT^hX7D-Vs B@%;$0ԡBX(3{/$']oCj#@JB&/[H&` Cp؅L6ֶU\ȍ}RMz?v1 …I>,] R/wf/RSW11[c|*81v.."eJ.$X*3G":e2PT_;_t G&P* R&!<&!L0pJBH 3sp$IMeIQ?ُ|[:ȺcxXX<KYpb]Y%y>TY:,!m|@3"1bqeYx$gF^wť^yQ'c`C(MV &` !dՔ8IrR+L*{&,Ǭy;VhX<&yе&-A5I81|Dˌ%ɏH\L[Ĉ.W9p^xq;W\&ʹTUQ1  LB &` f!2DWI f !T` rˏ @Ybu(au99it.=ƥRZZ390)PPRchR"Q4ЯFL08ワ$ {/^ŀ@ b&0k[Ab2ex *.taBDv^'M$d\SŖB^OLq>e}Xcɲ-} Z#L0p ) Z#NEeY]d5O !}`DAfZ[V4 u/zr܊APLP6INѸ*D-h&4;vP:V;79! H@#ń9L0!R%UU1`60ѩ<ICyR M/û席fEK%(3>i>C,m(}12y%%bC;Zcn4<+RI EpRUysמٙUӮ0G(L 2Hs9x$sG -Y +pDZ9`  &`[HA2;9A0 ehF"`@ABn;+Uh=<ب($D(ث*%ƐԤqeCRUIh*"k4(2-*C7L,] x Q09_ W# E -R7h%sd(F4*DE%WOt0l%uY1d$x?(f<q|&$S{₤sF6]rwYrl,ES 48j3+0R(AehP߁K{M1yCg٭o<{ gF0M ,b\e1d) b TZ~o& &0aRRfSrDCjIAV:.KC%D( %l_ڝw~fB|C"irE~D}6C:9pј"9`xJ@8Ӟ_&XES4APQE X%((jUY q2SCY&, &p¯vH@( 4!f5x($0Zλq %47]LO+z~TdlZ=v%0 ra@(a $BYLuDFT`! g&0f~lNJE!^/e&QHULxFzșA JE V!$ E8"`HB5PO &V/9#.lȟtS    $,~CP"4bP]y .; d}Aٙt:>13tub,AXrM VXF "@jVJ`!!UGI辜6΂0]<FF ܽ ^,.]:Å%,0d8QQb Bp$3v(˶RhF &y؟k;ڼR,H;ЄU!;2(35[QSݹ^#:1#Jщ, :ƻy)BIG@PU* L g2&!FET!V]@3 X,ZL"ŮP$]<xhanX~$3U.8FPEBdy uP Z&T;C'V|,(FaҢ/FL0h cn{j,aYi~1kVwM(C cYK}|&` ~ ׵`Y,ؐȰjdLTΦhP5Rx?_˿ulY 8q$PR˃򱡃H 10+@ ;!R`3% b(?=sAX',DzeQK1 AH%F#?$FF0*A!J 婃E LEʃ`@" "c^ȞAx;?zfݾs<(`HTuh"vҎM r}&` ~ *ZFXD D9Vӡ2I D9(+-A@b755UUUbWW^v֙B42[%c˜ZO@$dxI-$I\ }F^LJWn9Tu0 Vf0+R\(P/K6`BUe乒Q dG;.Z!E44;ZVwYbX7]53T4+7`L0?~C e!lBj@ L^L؊K= #j7:WXܳ73 꺮?яjO=LL,K"bf"ώRc3H(@mu5׉ubpش2Y"*dl;\\X: yXP] UU] FmK ,l3aT:hb!m7 !YVħ6:'K?+zz*ĻF,-*MB &vCN4ag]CENV Q !D-]8+>RUF+DsZoW=fnEl/&Idʪ 3GXDSBm)*TRxfj۔y N^Kc|PԤf\=ݵJ2R@N>H1BF/T +)p F!@QL$| IDATBdEぼU/??lFY&3")@(J0 f8UJ`yRWm 4ϛ0R$KbH Z.T` &xA`>%Kyd, Q D 15ɢjT!5ӕS3mdՑmTAY m4X" 7^#Iq)^<|FTG&+Nl{]slY|`f,a(8QGdDzQgE5b8@1H@ʪ#!D:2 BL%%(jRH,l^2mi\Hfv2̕ !8% L?3ۻ>EZIAa:-HhZO0] }PRb`lp U !!b$Xbu(W..Aؘ˲d3izuˈhB-,BUYYUI͋@Jt@k+Bʪs:iӛICl8y!c9I%3?+2%PPIDm:Zԕt&㝢FhVc骸xT_o~>=BlqG~fu=23ƾ;Wv+/pÑM_"f)!Nظ<~L0ůvეB& &M&buıG{+=Up4*0,]ILYyWT64=wZ톩,zILc8NJJ:qf|#-bXI .ҩ<:WKI.oAX%[֘햳3%D0%D$ f^B&>hE)X`MBX:>'%byr#TgMc3o9No"G (R(Q$˲;ؕFbwU htASQ @*̞b9-k5(q{g{?}O%*t]pyx%uϹw7QofYAFfj6O/ Cca^Y[oOγVMq)g{7}X`qu0 Q %HH` 22(+Zڟ-/ZFl0/48)JV5iG˅'gW+G nK Hx6Gƹա 5˃p` L]7!,8"v-r敆o1Nf$؜]"),QvVf3BS`>ȥۜfzAg&/s@ 2dVX&i6E^W@32LgkX.Im9f&פmF.Vm~s9'IM3A뺎|ؚ]ښ~_~#q pD[0)X`wwkHu!QEt&W "V2#8) 0$ ,ىRK; YQI(RW4}5J JtBPĂPN̈,4ooU) S#0R hgNSfJ`f3Sբ(b>03wuwεmcm,)BDrś+!q? s0m" ̕]L. >/FJF iƙHMճV}b8] eKeMĄƸ@EU9HlTdD*TRL _ͬ `hsmf"1J-1NaZJbdj* ! [MycnaƢv~:6B|OtB85IBZX`~SR!Vgpi'Ѡ@t. P;Y%p a:g=$Pg®TgxK }&UڷXj?z]MĦ{T"i2Ӥ1[y*fmʚ&gq4Ecu]g}*"L&x>Oݳӟs=׶:c>y+F&cE- .oͅ(Hą*rStnҟ ubW8_0d*>paZ6n;瀄59R ,}654]Y*3|iSɶZEQlnn2p8N7rg?n8:ygWW—t!{n'E imEH195053wr̴ͷQ,A,|;>/>/t,=v,R\,:SEvl Mӻ>Sцkۺẅ́9PZ9z u3٬#0$Ƞ"qs =0+"djk:OmS#G0U%5p  ,]L$%y"b;^3 )d% TVOmm/2kC6õ>rum?t˱{Qו_ڥ7'/}n?~f )!paFbgj0a5"%ʑ3؉u}Ÿ; )mOOY`K@ 9~PdIAH=WTfH#]y;c+vEzk xh?p u \o-)1EB+>}nvaw`zG{.5n:A1< BfdH]1\yN> fFDfN\]׽^(SJIg?{z=___}iѫ?c؏&X>[V)vik ]Txλ.m-j孤;m^QvEQ#]ֱXj*OfwO<ϟ;3YkV^9yZDtc4py=E?}y;`0M0g6O2f6*9߶wIs??|jȌ9;|7lX;03f33U}ќ]ABBf NIőTL7_?]ٻ:lcf.zwo \wWgGسϽe8pwnr5m—NH3){])h*r@Dy'i;v(mz﫪* CZ^^?O}SBYϞEDDjȑ#O!;^6~%q 1SFjw2}B+W9eclSQ cX4;3!B,xs Og^X{#y f;*(OjJqJڹUVv/{+_ 3s`}3+,7,Wj$jij2v"ǎ޸ɤecQiXແ , "12+>S*ِ\+% .XIUv܋y/L6Ox5ǯxb,~M_ORwSUu/?ۖ#tݏܹ}{aF%@<5,Dz{'4/< #!Nf Ds&($"[[[Γ7~7~g''-%UUefٞkkkk4P]_g#3v.\0NG֮,o`dJrm+~ r~IHU$B!WNO#n !ӟYct}gzG=dc(}'xl&M7(+t-} 6>#f7 }>^~GzZ>#Zތ^ٙPޤUKdfF^MM}=LŲRJ, Ht֑<}vk#gfDWqX`()}Q+g#ASRF(;ʦpj5o;O }w'H.}џ4-_~OHy ~mG>qrԅٹMlDj7z?|OYO0R0.W?Mdd)2Pe!ihR.4˲ 1z-s=]w/|.--Mg;(B;ݾ]1[_-ڮF@sJ]1, +8"HMr޷j?Q qEii˗gƨI;z'_xq$zME;|SxL5}?j\4Փ/K/©E$;wjTu2ZV7]YS3i5vL+v.Y DD, vƳUoK]uyje2- +vM;FTHa1̜8[AMu5bA6#Rԛ$5ۼ].2MdK2)Zn99mCWN>wϡcI&~}'NSg Tb*}VRJ @E^UH@m*QrPxbہ:̄SJeYf3~ïsi{sfS?S[}{7? 6gԈw]{ܬܹ[FɷmkFJfD;+?|ܶmx fD&IT4) A<\Y!W8~^q7ln_+ilJC蚮|TgW.r‰z}{Ѷn*4\,޼|!sN U2ͫƃY})I *ۼKiLGn|I-@;Ӆ- ,ns'g&cR)A  |rʕ]6͌Ӥ^jVXwRmZM|{__?|@FDf"eYE1͒Z9)L޸&^}k@ԒhU !eB)͢vs aÅӯM*G;w )g>'x駞xmz>;r曏zK]ny0\L{׶'.r-/ a5짒-Ōy:#r, |p>:wCr%cʎsMPGKތA'Fu~lVRZ#`c\0H`Ki<091v_|yWYË6~̘WE%u~9H O Hdos."PX5 1F|D$"~Ed<{ĉRJǏmwp ^o =mI\l@`45'l/V֙ DaJڥ̹sVQQlnAKI/z<7;pϦNM@~%3oȅ2$8r͟B Ebe}ǝ:ILEȹN#Qv5fm㽯ZJ ХZEYS9شqP K.NQ :5 6&`\\ r댃+`B{WM4a{s3a.'sO"+hUs-q۶'}9Y ,o]1Q|aFd*N=Gr)x1$@I ➥Roj|V Q(m삯VL2d'Ji6c]bՌ `sbO7՜m8X%v4fSg's!Q3sSJ~?w/sɄBy\{o?1#D1%3kN -6'Pv,w;@}Ȯ0^gs΢ *L IgrD$D1Q= JEGҶM $rcGYPD{E1rƓU GE\|67"b"u,%ymgqz]y26dy L5P2 F ZP4X`wUly4s1b#362s"PRw)s6/zAZ&F"W[VE@ IDATFɺt:({5ɝ#B)E8Ga%#fRCۿ#4E ٙ;Ƙ{SJ)_iLɴ_VM[W)>DO-3R`ިf2$UYhMvQy*)ހܺLpK^5ҚuD|F8<3I2.|u!YQS$dfΌTUn:kj7)x:u "Y= EYbH$*\GM69Aڦ>vu7!^exClX$A`mX`UK 2fc3UvZsDdPP1uf@똌] րEp>~Bizet!j`Y7@TJxm ]΂kYS'L  d)0#3#2ƁP!(K㤒8_ʄ;B "'vCoi4@pnU$U$Ur HDc3(mǚAU̘LM`0[RJ(q]0$"h%Qg~m$bϮY<]UQv팕\TlbiHAR2΅Lf s RӚWO\wq`QLw "uJE!fTg'yryVj" j (ZI0x$! X`K #1JsC!eRTrcɘt{ bC)}Ĺ/C[;Y{g^V|7rXໂ @HR6V`r`Tbun/ȱT$vr#/^ܿwuCꢭ6\._~kKͩ޻r퀒Mu%#YYySUV)$I`yCJ t9ߍ-鼘zmOI9DDI8eC>Ovs+mB0syeYZ6/O|G'8RoR؍>t];NҠt 8iҴze^crgKߦ$MnZUU˓1:D-ΤW*h ƛfF|ܖ,he\ׁBTlwĥ8^Ġ2kcU)wð ,qus.&S8]<*DE YRA:eB!V:˵`5co5b="җ<}#`xO'ϿV?uxX º„H0rD ȶPNoعKz>RG`f3ˎs0 =lua<Ozʲ+\o ._ꅍ^%mSHV~F #+FiٵWjQ5fz,\%zJaf"g @2ʦD)D(](qUKvҙӛl\Gw|qZ|krCͶ·7Ls^>~ݍfѤXL4SrB"M)H*e,g[ 5"ŏ@v!FϻR1ff) sriEr{?/֥{_`qHwh7_*xf6ػK/7Sgguϟxd Bggஅ"Tpav1k}>Rya|o馃O=kyG'K^kK/{᱗:ͭt4|ēOʕBm)[>1w)!S# ASp7HC( ݾsX`"\%@0ba&lH 5#S3=0- RLowqxo_SRz=~> }rnb0fۚʅ$z $s`$bۈMLY @`R?r.;XO|k!! {cGˣX cG[żQ㍮f*ddN5PՔDm[ɖ=Oh«^ww`4&[x~6-ǏݚD^>s:κ'yȐ箃ژL})gi+wX[jɧNxE;m*ICpE5ٺt+˽,5&)ֳmNpo1-=ē=u#'_5Vn߾ǿA1\O[rU=@kjF d&Au68|5sNT% ,;]RG@ƥIӜIAV4ȶs:eE"PqV\ׅ½G^zqi2-)"J3\$.7:ef)}p(ĚzH 0V0ʠ;:B1FTf|;i 1d%KpYL5" $@X3}d^v9IW#tΖCTVUT~>?rW23Ǯ}G]Ix4.ֺ%"586L`rT @>$~o3rUs"BD_8Y4M9&i6n),7Zl; m̬R"9νz W׺J/<7.֥"7u99ff%3_{nҐy5?^L:HNڳUcwF,UJ],ˤMK^N ʦ*#Kڶ]Z*H9x&k… x7sݍe1ic" _vl}wͶ6uwyH[N|C]J=Խga׎/IEjM)ۍ^Qlv :stu7H[/Evv*ac3˄Tk Wd+u) ~bU~2&rd*I"K=S. SJt5]`pw)cީEM*uD3H @y(l쳑4APiUATlǏqgzŵ^Y-쓷z.y?|邹6_w3'dpYsFֱ.0h0j$.b α7ӔTŜ{we {w[]5ݪkWY syLs@<ƦqPv.?\[/\CaMdz}<fu Gn9uO<'(j5kw;x+Ͼ MT֢qgFŠ6SW Wm^/8UǽaR9\/>{'lϣnw͗^5QGasZ[16RVUեK|9,!m?vA[- WwlĎa4Q$3 spd+7М@ffp-+knnM)a0~'o~l-_:t[k{^WV~wkD*sJȢV0)[CVۭ "39c*]wX͟7?LYRp.h%ȶcMmZCALCivt5׃ d\=ևRԕh$㭍͋=d]p3\{{_1ޘ6[ݵ-οz mzf]w($îC7{mՉPʞnLbVuC@`aMGv0v.mdG}z0{{j-QX2lȽ>>Whg:8umIġa0X]wזdQawn; qmD %͒g<32EzJ2UÀv rQ'}XD^+/ևel{U?M@ 5B6HjO͉$Ĥl`'2.x5p*{Jֶѽ-Kmt1Og3rh{l7+` R&9 {28rAPYR_-d4ZY+G^LEqja>Z¬lacK]3C*cw%TLݾꇪ{xLjTxq 3iMQ0ꭏ*{hTIeR,!Tڵϼb4I?˟椎s/_\ {gF ,\%0b` ȗiVGdPiVb 5FҨl^F% ) )b5 u*vEշ Uj'Vۖ ϮD@zǤSj4pṗ73O\ִ 5I9(5F3xW$35yhEf  APkSOb)),&uȥpuɤUu,Bj41o?H !3=tGRbJ > ,㸺K #tl실8;1ް15z9Hƙ,9[#ə# 4H`EgV*'UoY7^Yt:~ ɑA1[ZpK`NTXSR5[YGqk/nIZHhL}{MвUŹU{t L FDcԇE0Q4i1h$sNS|2FI25=霳^u o:{wժꫢ뚣K~򼙤o.Ɍ ySPDщ"yO=bŀ%8Zz7EYT#!RTi#+P%%87>>+*:/NʈQمFuUEKUc,3RM)d>YJ|pjB+BItAe &TDEtC! ;`#] vcccSU"L VaPA$ #m#0ǓKJ={~?ͻJ$Q?u P NArw4wC(yER|Y8/yDS5^vUc('Iuvf j =QOPYѱ–?6OXwޡ! .dʩ" ` ~?}%@ \1rgYLyVRFfDFJk|jLB HhD(8L)) ($ @%"½5BrBUb+dEXH%x.#rR )@P13eEUNJT i;xɒW8hk6 rNȞa@xr`ž"uaᣐCUiH6,)ϼH:́:RZճcccNGD*<_5Ƕnt<zF\6L6]"7&~?!&N/<[~t_b9ީyC)3"`_criC-P@H]e PH^Aj0kg\&\s> !dI@ /VlSY<%In;l';iǭ7B{䄹).aFOzbCI1F%˲jDm65ʪe!j|v"XU=}Ufv;DXUUe"b”yu$*ˊ$"}n6mM5PNz3v}v}O~nj|]'4|Rs fTn&6^߹rܼI80o8-3D*1Sv+Y)S]w>PȐcU1'hHbFxcdRh~/ IDAT<󬳚EcZj ȩ>я~c8ʿm^eV &(*λLVvfn;83?wvݿjʕ+S)BT UYx?L0aܕC/(6؍6s#i`;A#j%AT٩vOiPg{:VBE#R1oDٳ^5Fwm'4I@KOo,EU_[埜ҿ8UuSJEQQQUwmnimG?}}{eY ! ¤PPTFDED8N( #bbQg.fv;t:Ih_Fuw6, p"qZwu/ecU-uU^933sp!bف4Dd،0>~73]>Tf;EQկw󝙙qDfcb%"hZ *>˲U{)%'Prj ,t;3DfetR,os*,l{Nv a׹!tS6CVX߅bDV$U\2gY|#;O>I_'c>9~#0F& J?B,O\ȳ|o=-#(9E{srʋ^??]dNDNg||%aя>R**"Ʋj9*)EV !,ϘkP: Z7f">Q7 !bM*9ܯlXǾ4ed7u^xLR EDb\N"s|xѢElc !u \ik!^|3ǽǏeYZ.e\ D`.ztT " V]S 2qH$ؠf^n5q]݋7m[V>tF0Ho(`bLj`D T1~1D=0{&G\0J|.<ꪫ9YsyDD=DtuB(bA}σEQUE!"nؗO|?+VY梘;O!dcmz+i;Gt)-ڌII Z%VE(L 2T{8{a1 |dי7s]zR뮾Zz{=")ʼn3f:f.k_ Qz=aȳ^}n(7#jD?-=C n$QU|9 VSt7LyFa'F<߁^WEyށ-I)eiJy;&''1"Tull,׿[V]D]r'y'= Khb #1–j@p[Aų7'nmbFxaK;l.YKe(3M |xXdyYWEQ`]%tTq#v fP}~.|ʢs{ν%/Vvݮs.sU}e| ̧> ]s45Gݢ@SG7EՇq x#2<&(,gkэBN#0k;5<.|X~@UY/g]"8纳<fF'Rٜi6#UU圫;BF3}nxɍ#:כR=\7 GbR'sIsM #0#.@UUcf9C=t{/f@Ԕ;8yu,fB(ؘ$"Ul6(gu][J9g} C!`ܴA71vέq-~#_ݜ  ˺eυ@)* #Ȃ.(+y StĊVD[ !8~>@D3\"3>r= 2Z;h7uSmd!"Kb%DQȅBX!e'N>y(F@G>ԑ\be@HֲT'Ί:DA@@zf<I!X(HJ f3%N?\;FaƖRȜỏ ֜ge" Dd?j>aQ "П/")(Xg7^@SY֙wyޘi~_g@Df2#UmU.p[{x9PYsQRLu-'Z*Ć 1F*x vy{-<K߃l HzQ6gS᧔,+eYfCD+ײ,,sZ-f*Σsnff;leUU 9R]E]͹C6 ?`Fa-̤0čL X{ EDsO*hyVA6Y*3lo0#˛u"2k\[>xl5y0FFTLΝw羌NѡH!Y1#H_/ֈ)2mz[VJɄLzj*fW5 U-m̍h9c,DTDnw:02csjY(z^Ciڥ1^F^ilfdٌ5!ehKQaFp)D~ DJOYTY!ѺM( R1ܚYc2݈VX|~u]wD,>+a3ZDUQe$J"9.xRZoUঝֈδm\/HZ$? QUխ_p$%Qu*B>P]ײ"16=M+;yD6뵫J)eZ,SUoNn͍ B,˔R"CGH!tD<_cFR Gu]獦4W'¤y)%~r0xLu)Ia ,8CYnY9"o% Ih켞UEQnhޅRqeEnHGfX+-*9-)/r vy Hs$ D0bR߰xbWUl6-ADͳ!lhU4=,eYM2dr M eYb;j*"{_v;FNLD# CkՇunq_Fab&L Dd 1U$ګl,(" }ՠ $a}bxagBfgg/> F!FQյ fLsL f6x9uY *̑zJ0iαȣZ',FUU)y̘ ,K),KȦӥ1ZKURj46C:s{BUUyw8oTeu]gPr@!E(055e6'#_#%Yn,57 vFa._GBGKr@Q&;Z{ի'g{Q2(+N3MY4Mj3h{7 DPn3;;SUkQaaVakAu u6팆fNdw߿n<3`]vK/]`mx '__e=0B]bŭ:33c#fn_^鍊n˲w av96 ̟;mڵkC̟_ݗۿmqm/X…oMJ펍 Vk+|wFc;@EЋ5&g" B>U 1+m[+ wM5jZZ RZ&*kQ+ 9u@*RFHR&VH1x#9 ZϵʥR鹦TTi$ZKkXN*Jf;?nO":}c>V權̇^KDo<{|qwg2G"{/ U$9YE*jH'TN@Q/A m rA9)'B)UpdU^-Q 7`7YfP- p杤hG[*a,D BpvRn7{NQdW]뮻&CJu@9dF>n YP*xU]A .oz<09yտ~cZ(1d $C33e,cfOȱLގ=7{/F;׉HRaFX[FUTRªf 0;95333޼r,[V]yu=o޼O?=zW#/0"x7pCQ˖-u]-|+_=ꨣ{wFJ/~ŋm2rCēO>y[m[s= _BJϋ iK{g?`wmofD>~;|s{G DXDf]׀1UCOO?s6lD\UNM@UWV˒H… ׬Y3o<{ww?Ͱ@o~?g<裏βnUN<=k=>F;/rU=cvq㥗^z~DSN9e޼yjzdU0pA%4]H%fz՚T V%Q ȠIڄhKXLQ!*X'F%E~5s[DQ=:Q}0lmS *zSR..5M2 $tJ`l57ޕBBOΪ*DAk7S~⶧>iN'V5*Nߞp ~+ѡr]Zj _pv˭G,]6vrCk=}_z/~ٙßԕ?pל76BY:3Xr5NE#V7r/֮]{QGYkZZ),<˲Х|ws^{/~y/Xz~ӿ~_pcv]V)8yNwO=+Vz'/| ׿%\bA(zbɜ{P=SRJ_~^R/=U=/_n9 Wv, s=ʗS{dYfd‹я~} 7\g ι[oyG,v~u]&O-7߈ '_<9W}_%K}#)V_w?̇Oxox?ɩ?})?͢q7w̱wϝwϝ?n_'/zp7/x, ta$xܼtrt\ xwU@@I@hbVgt^ձ@~gTAAPQU- 3* Is5hݔםߢ("(B'G"ʬJHEڛ~h^cNw! ʦ閪j^[nmo{[UUW_}^hE|g}袋>ؔxE .ˎ8Xt;o}[_|>K:: .k_ ~K_ !LOO ""g}Nnk /pGs4vi{_h4;K.9cƼy<7F|W"ٵ^{+_y^׿ޞejiC+93dl|_ !e/;>]wu~-Ztr!{. $J!#F΂';?M<*ȱk&GsK/ /X aor 4M FeIַm(Ie"W̊a+'ժ np^OLLy˖-;#bU:]I|yGλ>h5PnwT+^^{Kzaf˖-M7u:g˖-Cm<L!s;>;lvղ,n[lV~zz\Dn{"F#0|_)}[˖-su:9 ̘׿>z}m=S?qz$3wECDޮ]vѺfoK?}==}w,)%`vzv ˿KЇ>BXfmY8;7Mguy睷dOo?jfsg?e/~S^?mlmg\eor,bѢE*"cccbŊ>h|+TDZhUUL6# $F7c֪׬j(HJY_," lXEQ[ "WL*"&bmǴEDap ,"l֪ٞH{7a!e IDAT* ܎r'uTGDd(dr6m?ePS̲;3/o^{%g~;?xyvmh''` ^υ` ccc""PwlIEQ@u = )pk4gu?O?ͦt:6x4j_bxo"v , Mѯ}w޵^{[j{M7t)Z㏿曽@Y3LT%ZUX 7-wu׽W[ƨBDKLDzg_wd2=׬͚+Wj°vklvpa&?We0oh4;op!fYP%4MӶk񭷾{SJm6nw+'v PG@i@6v:[nX "X}Fώ> ^4VX`N:ok_+D9(vyġ9 ~[F]׿/   ׾4ϊsnvN;+WFUSJ L>66fr56&"׏~#@2k#[o{gtg@AbYxHٯFocfX0UϾ+KqW<{U,{5\Ga6cʥVDDE$eWMT9DDY((yEAC`NUQ%!`Bo6(0"$Ȧh}im2Q[Ġ.r@H:a<7KY`Jlg~ T58!dN5#*ERG$(ow?ꨣVZ|억YbM>TnOԩt"#g|UD_?O9'Q佲99đ#] #= HzՑ]'t]K5دdGȎ*1^nAAEF

l2tpns is half of a complete L2TP implementation. It supports only the LNS side of the connection.

L2TP (Layer 2 Tunneling Protocol) is designed to allow any layer 2 protocol (e.g. Ethernet, PPP) to be tunneled over an IP connection. l2tpns implements PPP over L2TP only.

There are a couple of other L2TP implementations, of which l2tpd is probably the most popular. l2tpd also will handle being either end of a tunnel, and is a lot more configurable than l2tpns. However, due to the way it works, it is nowhere near as scalable.

l2tpns uses the TUN/TAP interface provided by the Linux kernel to receive and send packets. Using some packet manipulation it doesn't require a single interface per connection, as l2tpd does.

This allows it to scale extremely well to very high loads and very high numbers of connections.

It also has a plugin architecture which allows custom code to be run during processing. An example of this is in the walled garden module included.

Installation

Requirements

  • Linux kernel version 2.4 or above, with the Tun/Tap interface either compiled in, or as a module.

  • libcli 1.8.5 or greater. You can get this from SourceForge

Compiling

You can generally get away with just running make from the source directory. This will compile the daemon, associated tools and any modules shipped with the distribution.

Installing

After you have successfully compiled everything, run make install to install it. By default, the binaries are installed into /usr/sbin, the configuration into /etc/l2tpns, and the modules into /usr/lib/l2tpns.

You will definately need to edit the configuration files before you start. See Configuration for more information.

Running

You only need to run /usr/sbin/l2tpns as root to start it. It does not normally detach to a daemon process (see the -d option), so you should perhaps run it from init.

By default there is no log destination set, so all log messages will go to stdout.

Configuration

All configuration of the software is done from the files installed into /etc/l2tpns.

startup-config

This is the main configuration file for l2tpns. The format of the file is a list of commands that can be run through the command-line interface. This file can also be written directly by the l2tpns process if a user runs the write memory command, so any comments will be lost. However if your policy is not to write the config by the program, then feel free to comment the file with a # or ! at the beginning of the line.

A list of the possible configuration directives follows. Each of these should be set by a line like: set configstring "value" set ipaddress 192.168.1.1 set boolean true

debug (int)

Sets the level of messages that will be written to the log file. The value should be between 0 and 5, with 0 being no debugging, and 5 being the highest. A rough description of the levels is:

0: Critical Errors

Things are probably broken

1: Errors

Things might have gone wrong, but probably will recover

2: Warnings

Just in case you care what is not quite perfect

3: Information

Parameters of control packets

4: Calls

For tracing the execution of the code

5: Packets

Everything, including a hex dump of all packets processed... probably twice

Note that the higher you set the debugging level, the slower the program will run. Also, at level 5 a lot of information will be logged. This should only ever be used for working out why it doesn't work at all.

log_file (string)

This will be where all logging and debugging information is written to. This may be either a filename, such as /var/log/l2tpns, or the special magic string syslog:facility, where facility is any one of the syslog logging facilities, such as local5.

pid_file (string)

If set, the process id will be written to the specified file. The value must be an absolute path.

random_device (string)

Path to random data source (default /dev/urandom). Use "" to use the rand() library function.

l2tp_secret (string)

The secret used by 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.

l2tp_mtu (int)

MTU of interface for L2TP traffic (default: 1500). Used to set link MRU and adjust TCP MSS.

ppp_restart_time (int); ppp_max_configure (int); ppp_max_failure (int)

PPP counter and timer values, as described in §4.1 of RFC1661.

primary_dns (ip address); econdary_dns (ip address)

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.

primary_radius (ip address); secondary_radius (ip address)

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.

In addition to the source IP address and identifier, the RADIUS server must include the source port when detecting duplicates to suppress (in order to cope with a large number of sessions coming on-line simultaneously l2tpns uses a set of udp sockets, each with a separate identifier).

primary_radius_port (short); secondary_radius_port (short)

Sets the authentication ports for the primary and secondary RADIUS servers. The accounting port is one more than the authentication port. If no RADIUS ports are given, the authentication port defaults to 1645, and the accounting port to 1646.

radius_accounting (boolean)

If set to true, then RADIUS accounting packets will be sent. This means that a Start record will be sent when the session is successfully authenticated, and a Stop record will be sent when the session is closed.

radius_interim (int)

If radius_accounting is on, defines the interval between sending of RADIUS interim accounting records (in seconds).

radius_secret (string)

This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail.

radius_authtypes (string)

A comma separated list of supported RADIUS authentication methods (pap or chap), in order of preference (default pap).

radius_bind_min (short); radius_bind_max (short)

Define a port range in which to bind sockets used to send and receive RADIUS packets. Must be at least RADIUS_FDS (64) wide. Simplifies firewalling of RADIUS ports (default: dynamically assigned).

radius_dae_port (short)

Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) requests (default: 3799).

allow_duplicate_users (boolean)

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.

guest_account (string)

Allow multiple logins matching this specific username.

bind_address (ip 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.

peer_address (ip address)

Address to send to clients as the default gateway.

send_garp (boolean)

Determines whether or not to send a gratuitous ARP for the bind_address when the server is ready to handle traffic (default: true). This value is ignored if BGP is configured.

throttle_speed (int)

Sets the default speed (in kbits/s) which sessions will be limited to. If this is set to 0, then throttling will not be used at all. Note: You can set this by the CLI, but changes will not affect currently connected users.

throttle_buckets (int)

Number of token buckets to allocate for throttling. Each throttled session requires two buckets (in and out).

accounting_dir (string)

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. Each file dumped begins with a header, where each line is prefixed by #. Following the header is a single line for every connected user, fields separated by a space.

The fields are username, ip, qos, uptxoctets, downrxoctets. The qos field is 1 if a standard user, and 2 if the user is throttled.

dump_speed (boolean)

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 uptime command on the CLI.

multi_read_count (int)

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.

scheduler_fifo (boolean)

Sets the scheduling policy for the l2tpns process to SCHED_FIFO. This causes the kernel to immediately preempt any currently running SCHED_OTHER (normal) process in favour of l2tpns when it becomes runnable. Ignored on uniprocessor systems.

lock_pages (boolean)

Keep all pages mapped by the l2tpns process in memory.

icmp_rate (int)

Maximum number of host unreachable ICMP packets to send per second.

packet_limit (int)

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).

cluster_address (ip address)

Multicast cluster address (default: 239.192.13.13). See Clustering for more information.

cluster_port (udp port)

UDP cluster port (default: 32792). See Clustering for more information.

cluster_interface (string)

Interface for cluster packets (default: eth0)

cluster_mcast_ttl (int)

TTL for multicast packets (default: 1).

cluster_hb_interval (int)

Interval in tenths of a second between cluster heartbeat/pings.

cluster_hb_timeout (int)

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.

cluster_master_min_adv (int)

Determines the minimum number of up to date slaves required before the master will drop routes (default: 1).

ipv6_prefix (ipv6 address)

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.

BGP

BGP routing configuration is entered by the command: router bgp as where as specifies the local AS number.

Subsequent lines prefixed with neighbour peer define the attributes of BGP neighhbours. Valid commands are: neighbour peer remote-as as neighbour peer timers keepalive hold

Where peer specifies the BGP neighbour as either a hostname or IP address, as is the remote AS number and keepalive, hold are the timer values in seconds.

Access Lists

Named access-lists are configured using one of the commands: ip access-list standard name ip access-list extended name

Subsequent lines prefixed with permit or deny define the body of the access-list. Standard access-list syntax:

{permit|deny} {host|source source-wildcard|any} [{host|destination destination-wildcard|any}]

Extended access-lists:

{permit|deny} ip {host|source source-wildcard|any} {host|destination destination-wildcard|any} [fragments]

{permit|deny} udp {host|source source-wildcard|any} [{eq|neq|gt|lt} port|range from to] {host|destination destination-wildcard|any} [{eq|neq|gt|lt} port|range from to] [fragments]

{permit|deny} tcp {host|source source-wildcard|any} [{eq|neq|gt|lt} port|range from to] {host|destination destination-wildcard|any} [{eq|neq|gt|lt} port|range from to] [{established|{match-any|match-all} {+|-}{fin|syn|rst|psh|ack|urg} ...|fragments]

users

Usernames and passwords for the command-line interface are stored in this file. The format is username:password where password may either by plain text, an MD5 digest (prefixed by $1salt$) or a DES password, distinguished from plain text by the prefix {crypt}.

The username enable has a special meaning and is used to set the enable password.

If this file doesn't exist, then anyone who can get to port 23 will be allowed access without a username or password.

ip_pool

This file is used to configure the IP address pool which user addresses are assigned from. This file should contain either an IP address or a CIDR network per line. e.g.:

192.168.1.1
192.168.1.2
192.168.1.3
192.168.4.0/24
172.16.0.0/16
10.0.0.0/8

Keep in mind that l2tpns can only handle 65535 connections per process, so don't put more than 65535 IP addresses in the configuration file. They will be wasted.

build-garden

The garden plugin on startup creates a NAT table called "garden" then sources the build-garden script to populate that table. All packets from gardened users will be sent through this table. Example:

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

Operation

A running l2tpns process can be controlled in a number of ways. The primary method of control is by the Command-Line Interface (CLI).

You can also remotely send commands to modules via the nsctl client provided.

There are also a number of signals that l2tpns understands and takes action when it receives them.

Command-Line Interface

You can access the command line interface by telneting to port 23. There is no IP address restriction, so it's a good idea to firewall this port off from anyone who doesn't need access to it. See for information on restricting access based on a username and password.

The CLI gives you real-time control over almost everything in the process. The interface is designed to look like a Cisco device, and supports things like command history, line editing and context sensitive help. This is provided by linking with the libcli library. Some general documentation of the interface is here.

After you have connected to the telnet port (and perhaps logged in), you will be presented with a hostname> prompt.

Enter help to get a list of possible commands, or press ? for context-specific help.

A brief overview of the more important commands follows:

show session [ID]

: Detailed information for a specific session is presented if you specify a session ID argument.

If no ID is given, a summary of all connected sessions is produced.
Note that this summary list can be around 185 columns wide, so you
should probably use a wide terminal.

The columns listed in the summary are:

  -------------- -------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------
  `SID`          Session ID                                                                                                     
  `TID`          Tunnel ID                                                                                                      See also the [show tunnel](#operation-cli-show-tunnel) CLI command.
  `Username`     The username given in the PPP authentication.                                                                  If this is \*, then LCP authentication has not completed.
  `IP`           The IP address given to the session.                                                                           If this is 0.0.0.0, IPCP negotiation has not completed
  `I`            Intercept                                                                                                      Y or N: indicates whether the session is being snooped. See also the [snoop](#operation-cli-snoop) CLI command.
  `T`            Throttled                                                                                                      Y or N: indicates whether the session is currently throttled. See also the [throttle](#operation-cli-throttle) CLI command.
  `G`            Walled Garden                                                                                                  Y or N: indicates whether the user is trapped in the walled garden. This field is present even if the garden module is not loaded.
  `6`            IPv6                                                                                                           Y or N: indicates whether the session has IPv6 active (IPV6CP open)
  `opened`       The number of seconds since the session started                                                                
  `downloaded`   Number of bytes downloaded by the user                                                                         
  `uploaded`     Number of bytes uploaded by the user                                                                           
  `idle`         The number of seconds since traffic was detected on the session                                                
  `LAC`          The IP address of the LAC the session is connected to.                                                         
  `CLI`          The Calling-Line-Identification field provided during the session setup. This field is generated by the LAC.   
  -------------- -------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------

show users; show user username

: With no arguments, display a list of currently connected users. If an argument is given, the session details for the given username are displayed.

show tunnel [ID]

Produce a summary list of all open tunnels, or detail on a specific tunnel ID.

The columns listed in the summary are:

TID Tunnel ID
Hostname The hostname for the tunnel as provided by the LAC. This has no relation to DNS, it is just a text field.
IP The IP address of the LAC
State Tunnel state: Free, Open, Dieing, Opening
Sessions The number of open sessions on the tunnel
show pool

Displays the current IP address pool allocation. This will only display addresses that are in use, or are reserved for re-allocation to a disconnected user.

If an address is not currently in use, but has been used, then in the User column the username will be shown in square brackets, followed by the time since the address was used:

IP Address      Used  Session User
192.168.100.6     N           [joe.user] 1548s
show radius

Show a summary of the in-use RADIUS sessions. This list should not be very long, as RADIUS sessions should be cleaned up as soon as they are used. The columns listed are:

Radius The ID of the RADIUS request. This is sent in the packet to the RADIUS server for identification
State The state of the request: WAIT, CHAP, AUTH, IPCP, START, STOP or NULL
Session The session ID that this RADIUS request is associated with
Retry If a response does not appear to the request, it will retry at this time. This is a Unix timestamp
Try Retry count. The RADIUS request is discarded after 3 retries
show running-config

This will list the current running configuration. This is in a format that can either be pasted into the configuration file, or run directly at the command line.

show counters

Internally, counters are kept of key values, such as bytes and packets transferred, as well as function call counters. This function displays all these counters, and is probably only useful for debugging.

You can reset these counters by running clear counters.

show cluster

Show cluster status. Shows the cluster state for this server (Master/Slave), information about known peers and (for slaves) the master IP address, last packet seen and up-to-date status. See Clustering for more information.

write memory

This will write the current running configuration to the config file startup-config, which will be run on a restart.

snoop user IP port

: You must specify a username, IP address and port. All packets for the current session for that username will be forwarded to the given host/port. Specify no snoop username to disable interception for the session.

If you want interception to be permanent, you will have to modify
the RADIUS response for the user. See [Interception](#interception).

throttle user [in|out] rate

: You must specify a username, which will be throttled for the current session to rate Kbps. Prefix rate with in or out to set different upstream and downstream rates.

Specify `no throttle
        username` to disable throttling for the current session.

If you want throttling to be permanent, you will have to modify the
RADIUS response for the user. See [Throttling](#throttling).
drop session

This will cleanly disconnect the session specified by session ID.

drop tunnel

This will cleanly disconnect the tunnel specified by tunnel ID, as well as all sessions on that tunnel.

uptime

This will show how long the l2tpns process has been running, and the current bandwidth utilization:

17:10:35 up 8 days, 2212 users, load average: 0.21, 0.17, 0.16
Bandwidth: UDP-ETH:6/6  ETH-UDP:13/13  TOTAL:37.6   IN:3033 OUT:2569

The bandwidth line contains 4 sets of values:

UDP-ETH The current bandwidth going from the LAC to the ethernet (user uploads), in mbits/sec.
ETH-UDP The current bandwidth going from ethernet to the LAC (user downloads).
TOTAL The total aggregate bandwidth in mbits/s.
IN and OUT Packets/per-second going between UDP-ETH and ETH-UDP.

These counters are updated every second.

configure terminal

Enter configuration mode. Use exit or ^Z to exit this mode.

The following commands are valid in this mode:

load plugin name

: Load a plugin. You must specify the plugin name, and it will search in /usr/lib/l2tpns for name.so. You can unload a loaded plugin with remove plugin name.

set ...

Set a configuration variable. You must specify the variable name, and the value. If the value contains any spaces, you should quote the value with double (") or single (') quotes.

You can set any configuration value in this way, although some may require a restart to take effect. See .

router bgp ...

Configure BGP. See BGP.

ip access-list ...

Configure a named access list. See Access Lists.

nsctl

nsctl sends messages to a running l2tpns instance to be control plugins.

Arguments are command and optional args. See nsctl(8).

Built-in command are load_plugin, unload_plugin and help. Any other commands are passed to plugins for processing by the plugin_control function.

Signals

While the process is running, you can send it a few different signals, using the kill command.

killall -HUP l2tpns

The signals understood are:

SIGHUP

Reload the config from disk and re-open log file.

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. See Clustering

SIGQUIT

Shut down tunnels and sessions, exit process when complete.

Throttling

l2tpns contains support for slowing down user sessions to whatever speed you desire. The global setting throttle_speed defines the default throttle rate.

To throttle a sesion permanently, add a Cisco-AVPair RADIUS attribute. The autothrotle module interprets the following attributes:

throttle=yes Throttle upstream/downstream traffic to the configured throttle_speed.
throttle=rate Throttle upstream/downstream traffic to the specified rate Kbps.
`lcp:interface-config#1=service-policy input Alternate (Cisco) format: throttle upstream/downstream to specified rate Kbps.
rate`
`lcp:interface-config#2=service-policy output
rate`

You can also enable and disable throttling an active session using the throttle CLI command.

Interception

You may have to deal with legal requirements to be able to intercept a user's traffic at any time. l2tpns allows you to begin and end interception on the fly, as well as at authentication time.

When a user is being intercepted, a copy of every packet they send and receive will be sent wrapped in a UDP packet to a specified host.

The UDP packet contains just the raw IP frame, with no extra headers. The script scripts/l2tpns-capture may be used as the end-point for such intercepts, writing the data in PCAP format (suitable for inspection with tcpdump).

To enable or disable interception of a connected user, use the snoop and no snoop CLI commands. These will enable interception immediately.

If you wish the user to be intercepted whenever they reconnect, you will need to modify the RADIUS response to include the Vendor-Specific value Cisco-AVPair="intercept=ip:port". For this feature to be enabled, you need to have the autosnoop module loaded.

Plugins

So as to make l2tpns as flexible as possible, a plugin API is include which you can use to hook into certain events.

There are a some standard modules included which may be used as examples: autosnoop, autothrottle, garden, sessionctl, setrxspeed, snoopctl, stripdomain and throttlectl.

When an event occurs that has a hook, l2tpns looks for a predefined function name in every loaded module, and runs them in the order the modules were loaded.

The function should return PLUGIN_RET_OK if it is all OK. If it returns PLUGIN_RET_STOP, then it is assumed to have worked, but that no further modules should be run for this event.

A return of PLUGIN_RET_ERROR means that this module failed, and no further processing should be done for this event.

Use this with care.

Most event functions take a specific structure named param_event, which varies in content with each event. The function name for each event will be plugin_event, so for the event timer, the function declaration should look like:

int plugin_timer(struct param_timer *data);

A list of the available events follows, with a list of all the fields in the supplied structure:

Event Description Arguments
plugin_init

Called when the plugin is loaded. A pointer to a struct containing function pointers is passed as the only argument, allowing the plugin to call back into the main code.

Prior to loading the plugin, l2tpns checks the API version the plugin was compiled against. All plugins should contain:

int

plugin_api_version = PLUGIN_API_VERSION;

s truct pluginfuncs *
See pluginfuncs structure in plugin.h for available functions.
plugin_done Called when the plugin is unloaded or l2tpns is shutdown. void
No arguments.
plugin_pre_auth Called after a RADIUS response has been received, but before it has been processed by the code. This will allow you to modify the response in some way. struct plug in param_pre_auth *
tunnelt *t Tunnel.
sessiont *s Session.
char *username User name.
char *password Password.
int protocol Authentication protocol: 0xC023 for PAP, 0xC223 for CHAP.
int continue_auth Set to 0 to stop processing authentication modules.
plugin_post_auth Called after a RADIUS response has been received, and the basic checks have been performed. This is what the garden module uses to force authentication to be accepted. struct plugi n param_post_auth *
tunnelt *t Tunnel.
sessiont *s Session.
char *username User name.
short auth_allowed Initially true or false depending on whether authentication has been allowed so far. You can set this to 1 or 0 to force authentication to be accepted or rejected.
int protocol Authentication protocol: 0xC023 for PAP, 0xC223 for CHAP.
plugin_timer Run once per second. struct p lugin param_timer *
time_t time_now The current unix timestamp.
plugin_new_session Called after a session is fully set up. The session is now ready to handle traffic. struct plugin param_new_session *
tunnelt *t Tunnel.
sessiont *s Session.
plugin_kill_session Called when a session is about to be shut down. This may be called multiple times for the same session. struct plugin p aram_kill_session *
tunnelt *t Tunnel.
sessiont *s Session.
plugin_control

Called in whenever a nsctl packet is received. This should handle the packet and form a response if required.

Plugin-specific help strings may be included in the output of nsctl help by defining a NULL terminated list of strings as follows:

char

*plugin_control_hel p[] = { …, NULL };

struct plu gin param_control *
int iam_master If true, this node is the cluster master.
int argc nsctl arguments.
char **argc
int response Response from control message (if handled): should be either NSCTL_RES_OK or NSCTL_RES_ERR.
char *additional Additional information, output by nsctl on receiving the response.
plu gin_radius_response Called whenever a RADIUS response includes a Cisco-AVPair value. The value is split into key=value pairs. Will be called once for each pair in the response. struct plugin para m_radius_response *
tunnelt *t Tunnel.
sessiont *s Session.
char *key Key and value.
char *value
plugin_radius_reset Called whenever a RADIUS CoA request is received to reset any options to default values before the new values are applied. struct p aram_radius_reset *
tunnelt *t Tunnel.
sessiont *s Session.
pl ugin_radius_account Called when preparing a RADIUS accounting record to allow additional data to be added to the packet. struct par am_radius_account *
tunnelt *t Tunnel.
sessiont *s Session.
uint8_t **packet Pointer to the end of the currently assembled packet buffer. The value should be incremented by the length of any data added.
p lugin_become_master Called when a node elects itself cluster master. void
No arguments.
plugin _new_session_master Called once for each open session on becoming cluster master. sessiont *
Session.

Walled Garden

A "Walled Garden" is implemented so that you can provide perhaps limited service to sessions that incorrectly authenticate.

Whenever a session provides incorrect authentication, and the RADIUS server responds with Auth-Reject, the walled garden module (if loaded) will force authentication to succeed, but set the walled_garden flag in the session structure, and adds an iptables rule to the garden_users chain to cause all packets for the session to traverse the garden chain.

This doesn't just work. To set this all up, you will to setup the garden nat table with the build-garden script with rules to limit user's traffic.

For example, to force all traffic except DNS to be forwarded to 192.168.1.1, add these entries to your build-garden script:

iptables -t nat -A garden -p tcp --dport ! 53 -j DNAT --to 192.168.1.1
iptables -t nat -A garden -p udp --dport ! 53 -j DNAT --to 192.168.1.1

l2tpns will add entries to the garden_users chain as appropriate.

You can check the amount of traffic being captured using the following command:

iptables -t nat -L garden -nvx

Filtering

Sessions may be filtered by specifying Filter-Id attributes in the RADIUS reply. filter.in specifies that the named access-list filter should be applied to traffic from the customer, filter.out specifies a list for traffic to the customer.

Clustering

An l2tpns cluster consists of one* or more servers configured with the same configuration, notably the multicast cluster_address and the cluster_port

*A stand-alone server is simply a degraded cluster.

Initially servers come up as cluster slaves, and periodically (every cluster_hb_interval/10 seconds) send out ping packets containing the start time of the process to the multicast cluster_address on cluster_port.

A cluster master sends heartbeat rather than ping packets, which contain those session and tunnel changes since the last heartbeat.

When a slave has not seen a heartbeat within cluster_hb_timeout/10 seconds it "elects" a new master by examining the list of peers it has seen pings from and determines which of these and itself is the "best" candidate to be master. "Best" in this context means the server with the highest uptime (the highest IP address is used as a tie-breaker in the case of equal uptimes).

After discovering a master, and determining that it is up-to-date (has seen an update for all in-use sessions and tunnels from heartbeat packets) will raise a route (see Routing) for the bind_address and for all addresses/networks in ip_pool.

Any packets recieved by the slave which would alter the session state, as well as packets for throttled or gardened sessions are forwarded to the master for handling. In addition, byte counters for session traffic are periodically forwarded.

The master, when determining that it has at least one* up-to-date slave will drop all routes (raising them again if all slaves disappear) and subsequently handle only packets forwarded to it by the slaves.

*Configurable with cluster_master_min_adv

Multiple clusters can be run on the same network by just using different multicast cluster_address. However, for a given host to be part of multiple clusters without mixing the clusters, cluster_port must be different for each cluster.

Routing

If you are running a single instance, you may 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. Cisco uses maximum-paths ibgp for IBGP. If this is not supported by your IOS revision, you can use maximum-paths (which works for EBGP) and set as_number to a private value such as 64512.

l2tpns-2.3.3/docs/html/practical-vpns.html000066400000000000000000000544721400724550600204700ustar00rootroot00000000000000

Overview of VPNs and IPsec

Virtual Private Networks

The purpose of a VPN is to create a secure channel ontop of an un-secure medium, where a computer or a device are put in each end-point in order to establish communication, each of these end-points are often reffered to as Point of Presense, or POP. This kind of a communication allows the capability of creating a Virtual Private Network, which is accesable over a medium such as the Internet and thus, extend the physical boundaries of an existing local network.

VPNs have three forms:

Site-To-Site VPNs

these setups exist in order to extend the local network to create a much bigger LAN over the Internet.

Network-To-Host or Remote access VPNs

where a central VPN server is able to achieve multiple connections, often reffered to as RoadWarrior VPNs. (This setup is very common among ISPs)

Network-To-Network

extranet VPNs allow secure connections within branches and business partners, they are an extension of a Site-To-Site VPNs.

site to site
shows a Site-To-Site VPN diagram.

IP/VPNs are connections which are based upon IP tunnels. A tunnel is a way to encapsulate an IP packet inside another IP packet or some other type of packet. Why do we need tunneling? A Virtual Private Network is identified by IANA's private IP assignments and so such packet can not go beyond the uplink Internet interface.

tunneling process
shows the tunneling process.

Several tunneling protocols are available for manifesting VPNs.

L2F

Layer 2 Forwarding, an older implementation which assume position at the link layer of the OSI. It has no encryption capabilities and hence, deprecated.

L2TP

Layer 2 Tunneling Protocol, still no encryption capabilities.

PPTP

Point-to-Point Tunneling Protocol, and yet again, no encryption.

As seen, the requirement of encryption enhancement is urgent in order to assure authentication, data integrity and privacy. IPsec solves this by providing a suite of security measures implemented at layer 3.

IP Security Suite (IPsec)

VPN Security is now appearing, this complex things. How so? VPN tunnels by themselves are easily maintained by single-standalone tools like pppd, l2tpns, stunnel and others. Involving security with VPNs though requires more:

  • authentication, data integrity and privacy

  • keying management

Keys are secrets being shared by two end-points to provide a secure mean of communication against a third-party connection from sniffing the actual data.

Different ways to handle key management include RADIUS (Remote Authentication Dial In User Service) systems which provide AAA (Authentication, Authorization and Accounting). Another solution is ISAKMP/Oackly - Internet Security Association and Key Management Protocol. This solution requires you to posess one of the following:

  • something you have

  • something you know

  • something you are

The more requirements you meet the more secure is the medium, once established. Let's review, something we have is like a certificate, it proves who we are. Something we know, is a key, a secret password which we were told in a whisper, and something we are is our-fingerprint which identifies ourselves from other individuals.

IPsec in Depth

IPsec consists of two main protocols, an Authentication Header and Encapsulation Security Payload, also known as AH and ESP. Although it is not bound to these and can be extended (and often is) to other standarts such as

  • Data Encryption Standart (DES and 3DES)

  • Diffie-Hellman (DH)

  • Secure Hash Algorithm-1 (SHA1)

  • Message Digest 5 (MD5)

  • Internet Key Exchange (IKE)

  • Certification Authorities (CA)

We'll be deploying an IKE daemon to handle the key management, which uses the Diffie- Hellman cryptography protocol in order to allow two parties to establish a connection based upon a shared secret key that both parties posess. (Authentication within IKE is handled by MD5 hashing)

IKE is responsible for authentication of two IPsec parties, negotiation of keys for encryption algorithms and security associations. This process is commonly regarded as two phases:

Phase 1: IKE Security Association

The IKE daemon authenticates against the peers in order to achieve a secure channel, according to the Diffie-Hellman key agreement.

Phase 2: IKE IPsec Negotiation

After achieving an authenticated channel, the parties now negotiate a secure transform (the way to encrypt and secure the medium) where the sender is offering his/hers transform set after which the receiver decides upon one. An IPsec session can now safely begin.

Just to be clear, a Security Association is an agreed relation between two parties which describes how they will use security services (from IPsec) to communicate.

IPsec Modes

IPsec can operate in two different modes:

Transport mode

takes place when two devices (like a station and a gateway (now considered a host)) are establishing a connection which upon they both support IPsec.

Tunnel mode

we require tunnel mode when we proxy IPsec connetions between two stations behind the IPsec gateway. For example, in a Site-to-Site VPN a tunnel mode lives, since it exists in order to provide the stations behind these gateways runing the VPN/IPsec to communicate securely. In this situation, both end-points are runing an IPsec software.

In definition, a tunnel mode IPsec is better secured than transport. Without going too deep into the ins-and-outs of the technical side, transport mode doesn't encapsulate the actual IP layer but only the tcp/udp (Transport layer of the OSI) where-as a tunnel mode encapsulate both the Transport layer and the IP layer into a new IP packet.

To summarize, we need VPNs for data-exchange methods and a set of IPsec tools for security reasons.

VPN Deployment

I've assembled another diagram to view the actual VPN setup.
vpn deployment
gives a general description of how the network will be layed out in real-world scenario.

We notice that a single Linux box is acting as a Gateway and has all the services included with it. This is a bad idea from a security prespective but it's easy to just deploy the FreeRADIUS and MySQL servers on another machine. Of course the L2TPns and the rest of the IPsec tools suite would have to remain on the Gateway box (not necessarily the Firewall).

vpn process
attempts to explain the actual process that the VPN takes and to detail the place that each of that application-in-charge takes place.

Requirements

The Toolbox

Following is a description of the requirements you will have to meet:

A Linux box

preferably a 2.4.27 kernel or higher.

Debian is the chosen distribution which means we'll be using apt-get for installation, but I'll also focus on basic source tarballs installation.

Dependencies:

  • ipsec configuration in the kernel
L2TPns

an L2TP PPP Termination tool.

Dependencies:

  • libcli 1.8.0 or greater

  • tun/tap interface compiled in the kernel or as a module

FreeRADIUS

For authentication, and accounting.

MySQL

To act as a back-end database for the RADIUS.

OpenSwan

Provides the ipsec suite package.

Kernel Support

Debian stock kernel 2.4.27 and up are ipsec compatible although if you think otherwise check for the kernel-patch-openswan package.

Installation

L2TPns

Installation

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.

In a personal note - L2TPns is highly configurable for many cases, and extremely reliable for production/commerical use.

Step 1:

Make sure you have libcli-1.8 development package installed:

# apt-cache search libcli
libcli-dev - emulates a cisco style telnet command-line interface (dev files)
libcli1 - emulates a cisco style telnet command-line interface
# apt-get install libcli-dev
Step 2:

Download the source from SourceForge.

Step 3:

Build and install: make && make install

Alternately, you can skip these steps and simply apt-get install l2tpns.

On RPM-based distributions, you should be able to make packages from the libcli and l2tpns source tarballs with rpmbuild -ta.

Once compiliation is done you will have l2tpns in /usr/sbin/l2tpns, and all configuration files can be found in /etc/l2tpns/.

Configuration

The only configuration that L2TPns takes is centralized in the configuration file /etc/l2tpns/startup-config.

set debug 2                               # Debugging level
set log_file "/var/log/l2tpns"            # Log file: comment out to use stderr, use
                                          # "syslog:facility" for syslog
set pid_file "/var/run/l2tpns.pid"        # Write pid to this file
set l2tp_secret "secret"                  # shared secret
set primary_dns 212.117.128.6             # Only 2 DNS server entries are allowed
set secondary_dns 212.117.129.3
set primary_radius 192.168.0.1            # Can have multiple radius server entries,
                                          # but ony one radius secret
set primary_radius_port 1812
set radius_secret "radius_secret"
set radius_accounting yes
set radius_dae_port 3799
set accounting_dir "/var/run/l2tpns/acct" # Write usage accounting files into specified
                      # directory
set peer_address 192.168.0.1              # Gateway address given to clients
load plugin "sessionctl"                  # Drop/kill sessions
load plugin "autothrottle"                # Throttle/snoop based on RADIUS
load plugin "throttlectl"                 # Control throttle/snoop with nsctl
load plugin "snoopctl"

This is the trimmed down version of probably most of the common configuration and even some extra options.

Important configuration options are highlited and you should adjust these to meet your network needs. We can deploy all of the environment into one box which is of course not a very good idea from a security point of view, but will function just fine. Moreover, we will be using aliased IP addresses so once you've decided to move the FreeRADIUS daemon to another computer on the LAN it will be fairly easy and won't take too much configuration into it.

Next, we need to setup the IP pool that L2TPns will provide to each VPN client. The configuration file is located at /etc/l2tpns/ip_pool and should look like the following:

172.16.21.0/24

Of course you can change this pool to anything else (IANA IPs assigned for private internets only) just make sure it is not conflicting with your current LAN network addresses. This means that if you've assigned addresses of 192.168.0.1 and 192.168.0.2 to your LAN boxes you can't have a pool of 192.168.0.1/24 defined since L2TPns will try to route those addresses from the tun device, which is needless to say a bad idea...

Next up, creating the access-list for L2TPns.

Add a username and password into /etc/l2tpns/users:

admin:12345

The password may either be plain-text as above, or encrypted with MD5 or DES (to distinguish DES from plain-text passwords, prefix the value with {crypt}).

L2TPns utilizes a terminal connection on port 23 which you would feel very comfortable in if you have worked with routers and switches devices before. The terminal provides control over the ppp termination which is why we've created an account to log on to.

IPsec

Installation

User-space IPsec tools for various IPsec implementations exist for linux, among them is the port of KAME's libipsec, setkey, and racoon. Others are the OpenSWAN (a successor to the FreeSWAN project).

Getting IPsec installed is fairly easy with Debian:

# apt-get install openswan

The OpenSWAN project provides packages for RPM-based distributions.

Alternately, you may download the source from the OpenSWAN project:

# tar xvzf openswan-2.4.4.tar.gz
# cd openswan-2.4.4
# ./configure && make && make install

Configuration

OpenSWAN acts as the IKE daemon (remember IKE? it's job is to authenticate between the two peers and negotiate a secure medium). We will be setting up the IKE daemon as a RoadWarrior configuration, a term for remote access VPNs.

We desire this approach for compatibilty because after our VPN solution will be complete any user from a Windows machine will be easily ready to connect without any 3rd party applications, same for Linux.

Configuration files are placed in /etc/ipsec.d/, /etc/ipsec.conf and /etc/ipsec.secrets.

Let's start by choosing the remote client and it's PSK (Private Shared Key) /etc/ipsec.secrets:

hostname_or_ipaddress %any : PSK "mysecretkeyisverylong"

This is an IP/key pair. The IP or FQDN defines the local peer (like a SOHO branch), then the remote host. Here we defined %any for all hosts, though it's possible to define only a specific IP. At last, we define the key associated with it.

A better way to create a key is to utilize /dev/random for creating a unique key.

# dd if=/dev/random count=16 bs=1 2>/dev/null | xxd -ps

Next, let's prepare the configuration file /etc/ipsec.conf:

version 2.0
config setup
     nat_traversal=yes

conn l2tp
     authby=secret
     pfs=no
     keyingtries=3
     left=real_ip_address
     leftnexthop=%defaultroute
     leftprotoport=17/%any
     right=%any
     rightprotoport=17/%any
     auto=add

include /etc/ipsec.d/examples/no_oe.conf

In this file we have first defined version 2 which is a must, then enabled NAT Traversal. To understand the importance of this feature think of the following scenario: A remote user attempts to connect while he's behind a router and therefore NATed. The router has to de-encapsulate the packet, change things and then build it up again and send it. IPsec doesn't like other people messing with it's packet. That's why we solve this issue with NAT Traversal.

Next up we configure authentication type (certificates, psk, rsa keys, etc) then the left and right peers. The default mode OpenSWAN takes is tunnel unless told otherwise. I won't go into in-depth explanation of every method, you can take a quick look at /etc/ipsec.d/examples for more explanation and other variations of working with RSA keys, Certificates, host-to-host, and more.

In summary:

  • We've configured an almost complete IPsec VPN setup.

  • We've installed and configured a VPN server (L2TPns) and our IPsec security suite.

  • To control both of them we use: /etc/init.d/l2tpns and /etc/init.d/racoon (location of start-up scripts may vary on non-Debian systems, or if you've installed from source).

FreeRADIUS

The VPN setup needs to authenticate against something, that is the users database which we chose to be a FreeRADIUS server backed with a MySQL database.

Installation

FreeRADIUS is the premiere open source RADIUS server. While detailed statistics are not available, we believe that FreeRADIUS is well within the top 5 RADIUS servers world-wide, in terms of the number of people who use it daily for authentication. It scales from embedded systems with small amounts of memory, to systems with millions of users. It is fast, flexible, configurable, and supports more authentication protocols than many commercial servers.

Installing on Debian:

# apt-get install freeradius freeradius-mysql

From source: Download the latest freeradius package from freeradius.org

# tar xvzf freeradius.tar.gz
# cd freeradius
# ./configure && make && make install

Configuration

This will appear a bit complex but it isn't, it's just a lot of configuration.

Following are the configurations you need to have in your /etc/freeradius/ files.

In this section I will not give you a dump of the configuration since they are very long and mostly default. I'll just post which changes to make.

We haven't yet configured MySQL, but it'll come afterwards, don't worry.

Make the following changes to the file /etc/freeradius/sql.conf:

server = "192.168.0.1"
login = "radius"
password = "12345678"

Add the following to the file /etc/freeradius/clients.conf:

client 192.168.0.1 {
    secret    = my_secret
    shortname = localhost
    nastype   = other
}

Don't confuse the secret directive there with IPsec. RADIUS server are using secret keys also to identify their allowed NAS (Network Access Servers), these are the clients that talk to the RADIUS server.

Also, change the client 127.0.0.1 {} directive to hold the secret "my_secret" like we configured for 192.168.0.1 to avoid conflicts.

Uncomment the sql directive in the authorize, accounting, and session sections of /etc/freeradius/radiusd.conf.

Now for populating FreeRADIUS with MySQL. If you don't know or haven't set root password for MySQL you can do it now with:

# mysqladmin -u root password password_here

Then add the following to /root/.my.cnf:

[mysqladmin]
user = root
password = password_here

Create the radius database, using the schema given in /usr/share/doc/freeradius/examples/db_mysql.sql.gz.

It may be necessary to modify the column definition of id in the nas table, removing DEFAULT '0' such that the definition reads:

id int(10) NOT NULL auto_increment,
# mysqladmin create radius
# mysql radius
mysql> source db_mysql.sql
mysql> GRANT ALL ON * TO 'radius'@'localhost' IDENTIFIED BY 'radius_password';

All the configuration is now done. Let's add a user to our VPN database.

# mysql radius
mysql> INSERT INTO radcheck values (0, "test", "User-Password", "==", "1234");

We have now created a user in the database of username test and password 1234.

Testing the RADIUS setup is simple using the radtest utility provided with it.

# radtest
Usage: radtest user passwd radius-server[:port] nas-port-number secret [ppphint] [nasname]
# radtest test 1234 192.168.0.1 1812 my_secret

radtest sends an Access-Request to the RADIUS server and expects an Access-Accept back from it. If you're not getting an Access-Accept from the RADIUS you're advised to check the configuration again and see what you might have missed.

Firewall Configuration

We need to apply a few things to iptables configuration and kernel networking.

First off, we need to accept VPN-specific packets through the firewall. Of course you will have to adjust the rules to fits you needs, in this case, ppp0 is the Internet interface.

# iptables --append INPUT --in-interface  ppp0 -p udp --dport 1701 -j ACCEPT
# iptables --append INPUT --in-interface  ppp0 -p udp --dport 500 -j ACCEPT
# iptables --append INPUT --in-interface  ppp0 -p udp --dport 4500 -j ACCEPT
# iptables --append INPUT --in-interface  ppp0 -p 50 -j ACCEPT

If you haven't setup your Linux box as a gateway yet then you have to allow forwarding/masqing for the boxes on the LAN (and therefore for the VPN clients):

# iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE
# iptables --append FORWARD --in-interface eth0 -j ACCEPT
# echo 1 > /proc/sys/net/ipv4/ip_forward

References

VPN Reference

L2TPns Project

OpenSWAN Project

l2tpns-2.3.3/docs/manpages/000077500000000000000000000000001400724550600154675ustar00rootroot00000000000000l2tpns-2.3.3/docs/manpages/l2tpns.8000066400000000000000000000074171400724550600170130ustar00rootroot00000000000000.\" Automatically generated by Pandoc 2.9.2.1 .\" .TH "L2TPNS" "8" "January 31, 2021" "Layer 2 tunneling protocol network server (LNS)" "" .hy .SH NAME .PP l2tpns - Layer 2 tunneling protocol network server (LNS) .SH SYNOPSIS .PP \f[B]l2tpns\f[R] [-\f[B]d\f[R]] [-\f[B]v\f[R]] [-\f[B]c\f[R] \f[I]file\f[R]] [-\f[B]h\f[R] \f[I]hostname\f[R]] .SH DESCRIPTION .PP \f[B]l2tpns\f[R] is a daemon for terminating layer 2 tunneling protocol (L2TP: RFC2661) sessions. .PP \f[B]l2tpns\f[R] is a complete L2TP implementation. It supports the LAC, LNS, PPPOE and DHCPv6 server. .PP Once running, \f[B]l2tpns\f[R] may be controlled by telnetting to port 23 on the machine running the daemon and with the \f[B]nsctl\f[R] utility. .SH OPTIONS .PP \f[B]-d\f[R] Detach from terminal and fork into the background. By default l2tpns will stay in the foreground .PP \f[B]-v\f[R] Increase verbosity for debugging. Can be used multiple times .PP \f[B]-c\f[R] \f[I]file\f[R] Specify configuration file .PP \f[B]-h\f[R] \f[I]hostname\f[R] Force hostname to \f[I]hostname\f[R] .SH FILES .PP \f[I]/etc/l2tpns/startup-config\f[R] .PD 0 .P .PD The default configuration file .PP \f[I]/etc/l2tpns/ip_pool\f[R] .PD 0 .P .PD IP address pool configuration .PP \f[I]/etc/l2tpns/users\f[R] .PD 0 .P .PD Username/password configuration for access to admin interface .SH SIGNALS .PP \f[B]SIGHUP\f[R] .PD 0 .P .PD Reload the config from disk and re-open log file .PP \f[B]SIGTERM\f[R], \f[B]SIGINT\f[R] .PD 0 .P .PD 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 .PP \f[B]SIGQUIT\f[R] .PD 0 .P .PD Shut down tunnels and sessions, exit process when complete .SH MANAGED RADIUS ATTRIBUTE .PP \f[B]Ascend-Client-Primary-DNS\f[R], \f[B]Ascend-Client-Secondary-DNS\f[R] .PD 0 .P .PD Specifies a primary and secondary DNS server address to send to user .PP \f[B]Delegated-IPv6-Prefix\f[R] .PD 0 .P .PD Assign a network address IPv6 prefix to a user by DHCPv6 .PP \f[B]Framed-IP-Address\f[R] .PD 0 .P .PD The address to be configured for the user (IPv4 address of the interface ppp) .PP \f[B]Framed-Route\f[R] .PD 0 .P .PD provides routing information to be configured for the user .PP \f[B]Framed-IPv6-Route\f[R] .PD 0 .P .PD Has the same action as \f[B]Delegated-IPv6-Prefix\f[R]. \f[B]Delegated-IPv6-Prefix\f[R] is the correct one to use .PP \f[B]Framed-IPv6-Address\f[R] .PD 0 .P .PD IPv6 address to be assigned to the user by DHCPv6 (IPv6 address of the interface ppp) .PP \f[B]Idle-Timeout\f[R] .PD 0 .P .PD disconnects the session if no data for more than \f[B]Idle-Timeout\f[R] (in seconds) .PP \f[B]Session-Timeout\f[R] .PD 0 .P .PD disconnects the user session when the time \f[B]Session-Timeout\f[R] is reached (in seconds) .PP \f[B]Tunnel-Type\f[R], \f[B]Tunnel-Medium-Type\f[R], \f[B]Tunnel-Server-Endpoint\f[R], \f[B]Tunnel-Password\f[R], \f[B]Tunnel-Assignment-Id\f[R] .PD 0 .P .PD attributes returned by the Radius of the remote LNS server (LAC functionality) .IP .nf \f[C] example, Radius that return the information of 2 remote LNS server with which must be open a L2TP TUNNEL: - Tunnel-Type: 1 = L2TP - Tunnel-Medium-Type: 1 = IPv4 - Tunnel-Password: 1 = \[dq]TheSecretL2TP\[dq] - Tunnel-Server-Endpoint: 1 = \[dq]88.xx.xx.x1\[dq] - Tunnel-Assignment-Id: 1 = \[dq]friendisp_lns1\[dq] - Tunnel-Type: 2 = L2TP - Tunnel-Medium-Type: 2 = IPv4 - Tunnel-Password: 2 = \[dq]TheSecretL2TP\[dq] - Tunnel-Server-Endpoint: 2 = \[dq]88.xx.xx.x2\[dq] - Tunnel-Assignment-Id: 2 = \[dq]friendisp_lns2\[dq] \f[R] .fi .SH SEE ALSO .PP \f[B]startup-config\f[R](5), \f[B]nsctl\f[R](8) .SH AUTHORS This manual page was written by Jonathan McDowell and Fernando Alves (), for the Debian GNU/Linux system (but may be used by others). l2tpns-2.3.3/docs/manpages/nsctl.8000066400000000000000000000032541400724550600167070ustar00rootroot00000000000000.\" Automatically generated by Pandoc 2.9.2.1 .\" .TH "NSCTL" "8" "January 31, 2021" "Manage running l2tpns instance" "" .hy .SH NAME .PP nsctl - manage running l2tpns instance .SH SYNOPSIS .PP \f[B]nsctl\f[R] [\f[B]-d\f[R]] [\f[B]-h\f[R] \f[I]host\f[R][:\f[I]port\f[R]]] [\f[B]-t\f[R] \f[I]timeout\f[R]] \f[I]command\f[R] [\f[I]arg\f[R] ...] .SH DESCRIPTION .PP \f[B]nsctl\f[R] sends commands to a running \f[B]l2tpns\f[R] 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 .PP \f[B]-d\f[R] Enable debugging output .PP \f[B]-h \f[BI]host\f[B][:\f[BI]port\f[B]]\f[R] The host running \f[B]l2tpns\f[R] that should receive the message. By default the message is sent to UDP port 1702 on \f[B]localhost\f[R] .PP \f[B]-t \f[BI]timeout\f[B]\f[R] Timeout in seconds to wait for a response from the server .SH COMMANDS .PP The first argument specifies the command to send to \f[B]l2tpns.\f[R] The following commands are as defined: .TP \f[B]load_plugin \f[R]\f[I]plugin\f[R] Load the named \f[I]plugin\f[R] .TP \f[B]unload_plugin \f[R]\f[I]plugin\f[R] Unload the named \f[I]plugin\f[R] .TP \f[B]help\f[R] Each loaded plugin is queried for what commands it supports and the synopsis for each is output .PP Any other value of \f[I]command\f[R] (and \f[I]args\f[R] if any) are sent to \f[B]l2tpns\f[R] as-is, to be passed to each plugin which registers a \f[B]plugin_control\f[R] function in turn (in which it may be acted upon). .SH SEE ALSO .PP \f[B]l2tpns\f[R](8) .SH AUTHORS This manual page was written by Jonathan McDowell , for the Debian GNU/Linux system (but may be used by others). l2tpns-2.3.3/docs/manpages/startup-config.5000066400000000000000000000432651400724550600205340ustar00rootroot00000000000000.\" Automatically generated by Pandoc 2.9.2.1 .\" .TH "STARTUP-CONFIG" "5" "January 31, 2021" "Configuration file for l2tpns" "" .hy .SH NAME .PP startup-config - configuration file for l2tpns .SH SYNOPSIS .PP /etc/l2tpns/startup-config .SH DESCRIPTION .PP \f[B]startup-config\f[R] is the configuration file for \f[B]l2tpns\f[R] .PP The format is plain text, in the same format as accepted by the configuration mode of l2tpns\[cq]s telnet administrative interface. Comments are indicated by either the character # or !. .SS SETTINGS .PP Settings are specified with .PP \f[B]set\f[R] \f[C]variable\f[R] \f[C]value\f[R] .PP A list of the possible configuration directives follows. Each of these should be set by a line like: .PP \f[B]set\f[R] \f[I]configstring\f[R] \f[I]\[lq]value\[rq]\f[R] .PD 0 .P .PD \f[B]set\f[R] \f[I]ipaddress\f[R] \f[I]192.168.1.1\f[R] .PD 0 .P .PD \f[B]set\f[R] \f[I]boolean\f[R] \f[I]true\f[R] .PP The following \f[C]variables\f[R] may be set: .PP \f[B]accounting_dir\f[R] (string) .IP .nf \f[C] 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. Each file dumped begins with a header, where each line is prefixed by #. Following the header is a single line for every connected user, fields separated by a space. The fields are username, ip, qos, uptxoctets, downrxoctets, origin (optional). The qos field is 1 if a standard user, and 2 if the user is throttled. The origin field is dump if account_all_origin is set to true (origin value: L=LAC data, R=Remote LNS data, P=PPPOE data). \f[R] .fi .PP \f[B]account_all_origin\f[R] (boolean) .IP .nf \f[C] If set to true, all origin of the usage is dumped to the accounting file (LAC+Remote LNS+PPPOE)(default false). \f[R] .fi .PP \f[B]allow_duplicate_users\f[R] (boolean) .IP .nf \f[C] 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. \f[R] .fi .PP \f[B]auth_tunnel_change_addr_src\f[R] (boolean) .IP .nf \f[C] This parameter authorize to change the source IP of the tunnels l2tp. This parameter can be used when the remotes BAS/LAC are l2tpns server configured in cluster mode, but that the interface to remote LNS are not clustered (the tunnel can be coming from different source IP) (default: no). \f[R] .fi .PP \f[B]bind_address\f[R] (ip address) .IP .nf \f[C] It\[aq]s the listen address of the l2tp udp protocol sent and received to LAC. This address is also assigned to the tun interface if no iftun_address is specified. Packets containing user traffic should be routed via this address if given, otherwise the primary address of the machine. \f[R] .fi .PP \f[B]bind_multi_address\f[R] (ip address) .IP .nf \f[C] This parameter permit one to listen several address of the l2tp udp protocol (and set several address to the tun interface). WHEN this parameter is set, It OVERWRITE the parameters \[dq]bind_address\[dq] and \[dq]iftun_address\[dq]. these can be interesting when you want do load-balancing in cluster mode of the uploaded from the LAC. For example you can set a bgp.prepend(MY_AS) for Address1 on LNS1 and a bgp.prepend(MY_AS) for Address2 on LNS2 (see BGP AS-path prepending). example of use with 2 address: set bind_multi_address \[dq]64.14.13.41, 64.14.13.42\[dq] \f[R] .fi .PP \f[B]cluster_address\f[R] (ip address) .IP .nf \f[C] Multicast cluster address (default: 239.192.13.13). See the section on Clustering for more information. \f[R] .fi .PP \f[B]cluster_port\f[R] (int) .IP .nf \f[C] UDP cluster port (default: 32792). See the section on Clustering for more information. \f[R] .fi .PP \f[B]cluster_interface\f[R] (string) .IP .nf \f[C] Interface for cluster packets (default: eth0). \f[R] .fi .PP \f[B]cluster_mcast_ttl\f[R] (int) .IP .nf \f[C] TTL for multicast packets (default: 1). \f[R] .fi .PP \f[B]cluster_hb_interval\f[R] (int) .IP .nf \f[C] Interval in tenths of a second between cluster heartbeat/pings. \f[R] .fi .PP \f[B]cluster_hb_timeout\f[R] (int) .IP .nf \f[C] 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. \f[R] .fi .PP \f[B]cluster_master_min_adv\f[R] (int) .IP .nf \f[C] Determines the minimum number of up to date slaves required before the master will drop routes (default: 1). \f[R] .fi .PP \f[B]debug\f[R] (int) .IP .nf \f[C] 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. A rough description of the levels is: - 0. Critical Errors - Things are probably broken - 1. Errors - Things might have gone wrong, but probably will recover - 2. Warnings - Just in case you care what is not quite perfect - 3. Information - Parameters of control packets - 4. Calls - For tracing the execution of the code - 5. Packets - Everything, including a hex dump of all packets processed... probably twice Note that the higher you set the debugging level, the slower the program will run. Also, at level 5 a LOT of information will be logged. This should only ever be used for working out why it doesn\[aq]t work at all. \f[R] .fi .PP \f[B]dump_speed\f[R] (boolean) .IP .nf \f[C] 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 uptime command on the CLI. \f[R] .fi .PP \f[B]disable_sending_hello\f[R] (boolean) .IP .nf \f[C] Disable l2tp sending HELLO message for Apple compatibility. Some OS X implementation of l2tp no manage the L2TP \[dq]HELLO message\[dq]. (default: no). \f[R] .fi .PP \f[B]echo_timeout\f[R] (int) .IP .nf \f[C] Time between last packet sent and LCP ECHO generation (default: 10 (seconds)). \f[R] .fi .PP \f[B]guest_account\f[R] .IP .nf \f[C] Allow multiple logins matching this specific username. \f[R] .fi .PP \f[B]icmp_rate\f[R] (int) .IP .nf \f[C] Maximum number of host unreachable ICMP packets to send per second. \f[R] .fi .PP \f[B]idle_echo_timeout\f[R] (int) .IP .nf \f[C] Drop sessions who have not responded within idle_echo_timeout seconds (default: 240 (seconds)) \f[R] .fi .PP \f[B]iftun_address\f[R] (ip address) .IP .nf \f[C] This parameter is used when you want a tun interface address different from the address of \[dq]bind_address\[dq] (For use in cases of specific configuration). If no address is given to iftun_address and bind_address, 1.1.1.1 is used. \f[R] .fi .PP \f[B]l2tp_mtu\f[R] (int) .IP .nf \f[C] MTU of interface for L2TP traffic (default: 1500). Used to set link MRU and adjust TCP MSS. \f[R] .fi .PP \f[B]l2tp_secret\f[R] (string) .PP The secret used by 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. .PP \f[B]lock_pages\f[R] (boolean) .PP Keep all pages mapped by the l2tpns process in memory. .PP \f[B]log_file\f[R] (string) .PP This will be where all logging and debugging information is written to.This may be either a filename, such as /var/log/l2tpns, or the string syslog:facility, where facility is any one of the syslog logging facilities, such as local5. .PP \f[B]multi_read_count\f[R] (int) .PP 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. .PP \f[B]packet_limit\f[R] (int> .PP 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). .PP \f[B]peer_address\f[R] (ip address) .PP Address to send to clients as the default gateway. .PP \f[B]pid_file\f[R] (string) .PP If set, the process id will be written to the specified file. The value must be an absolute path. .PP \f[B]ppp_keepalive\f[R] (boolean) .PP Change this value to no to force generation of LCP ECHO every echo_timeout seconds, even there are activity on the link (default: yes) .PP \f[B]ppp_restart_time\f[R] (int) \f[B]ppp_max_configure\f[R] (int) \f[B]ppp_max_failure\f[R] (int) .PP PPP counter and timer values, as described in Section 4.1 of RFC1661. .PP \f[I]ppp_restart_time\f[R], Restart timer for PPP protocol negotiation in seconds (default: 3). .PP \f[I]ppp_max_configure\f[R], Number of configure requests to send before giving up (default: 10). .PP \f[I]ppp_max_failure\f[R], Number of Configure-Nak requests to send before sending a Configure-Reject (default: 5). .PP \f[B]primary_dns\f[R] (ip address), \f[B]secondary_dns\f[R] (ip address) .PP 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. .PP \f[B]primary_radius\f[R] (ip address), \f[B]secondary_radius\f[R] (ip address) .PP 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. .PP Note: in addition to the source IP address and identifier, the RADIUS server must include the source port when detecting duplicates to suppress (in order to cope with a large number of sessions coming on-line simultaneously l2tpns uses a set of udp sockets, each with a separate identifier). .PP \f[B]primary_radius_port\f[R] (short), \f[B]secondary_radius_port\f[R] (short) .PP Sets the authentication ports for the primary and secondary RADIUS servers. The accounting port is one more than the authentication port. If no RADIUS ports are given, the authentication port defaults to 1645, and the accounting port to 1646. .PP \f[B]radius_accounting\f[R] (boolean) .PP If set to true, then RADIUS accounting packets will be sent. This means that a \f[B]Start\f[R] record will be sent when the session is successfully authenticated, and a \f[B]Stop\f[R] record will be sent when the session is closed. .PP \f[B]radius_interim\f[R] (int) .PP If radius_accounting is on, defines the interval between sending of RADIUS interim accounting records (in seconds). .PP \f[B]radius_secret\f[R] (string) .PP This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail. .PP \f[B]radius_authtypes\f[R] (string) .PP A comma separated list of supported RADIUS authentication methods (\[lq]pap\[rq] or \[lq]chap\[rq]), in order of preference (default \[lq]pap\[rq]). .PP \f[B]radius_dae_port\f[R] (short) .PP Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) requests (default: 3799). .PP \f[B]radius_bind_min\f[R], \f[B]radius_bind_max\f[R] (int) .PP Define a port range in which to bind sockets used to send and receive RADIUS packets. Must be at least RADIUS_FDS (64) wide. Simplifies firewalling of RADIUS ports (default: dynamically assigned). .PP \f[B]random_device\f[R] (string) .PP Path to random data source (default /dev/urandom). Use \[dq]\[dq] to use the rand() library function. .PP \f[B]scheduler_fifo\f[R] (boolean) .PP Sets the scheduling policy for the l2tpns process to SCHED_FIFO. This causes the kernel to immediately preempt any currently running SCHED_OTHER (normal) process in favour of l2tpns when it becomes runnable. Ignored on uniprocessor systems. .PP \f[B]send_garp\f[R] (boolean) .PP Determines whether or not to send a gratuitous ARP for the bind_address when the server is ready to handle traffic (default: true). This value is ignored if BGP is configured. .PP \f[B]tundevicename\f[R] (string) .PP Name of the tun interface (default: \[lq]tun0\[rq]). .PP \f[B]throttle_speed\f[R] (int) .PP Sets the default speed (in kbits/s) which sessions will be limited to. If this is set to 0, then throttling will not be used at all. Note: You can set this by the CLI, but changes will not affect currently connected users. .PP \f[B]throttle_buckets\f[R] (int) .PP Number of token buckets to allocate for throttling. Each throttled session requires two buckets (in and out). .SS DHCPv6 And IPv6 SETTINGS .PP \f[B]dhcp6_preferred_lifetime\f[R] (int) .PP The preferred lifetime for the IPv6 address and the IPv6 prefix address, expressed in units of seconds (see rfc3315). .PP \f[B]dhcp6_valid_lifetime\f[R] (int) .PP The valid lifetime for the IPv6 address and the IPv6 prefix address, expressed in units of seconds (see rfc3315). .PP \f[B]dhcp6_server_duid\f[R] (int) .PP DUID Based on Link-layer Address (DUID-LL) (see rfc3315). .PP \f[B]primary_ipv6_dns\f[R], \f[B]secondary_ipv6_dns\f[R] (Ipv6 address) .PP IPv6 DNS servers will be sent to the user (see rfc3646). .PP \f[B]default_ipv6_domain_list\f[R] (string) .PP The Domain Search List (ex: \[lq]fdn.fr\[rq]) (see rfc3646). .PP \f[B]ipv6_prefix\f[R] (Ipv6 address) .PP 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 0. .SS LAC SETTINGS .PP \f[B]bind_address_remotelns\f[R] (ip address) .PP Address of the interface to listen the remote LNS tunnels. If no address is given, all interfaces are listened (Any Address). .PP \f[B]bind_portremotelns\f[R] (short) .PP Port to bind for the Remote LNS (default: 65432). .PP A static REMOTES LNS configuration can be entered by the command: .PP \f[B]setforward\f[R] \f[I]MASK\f[R] \f[I]IP\f[R] \f[I]PORT\f[R] \f[I]SECRET\f[R] .PP where MASK specifies the mask of users who have forwarded to remote LNS (ex: \[lq]/friendISP\[at]company.com\[rq]). .PP where IP specifies the IP of the remote LNS (ex: \[lq]66.66.66.55\[rq]). .PP where PORT specifies the L2TP Port of the remote LNS (Normally should be 1701) (ex: 1701). .PP where SECRET specifies the secret password the remote LNS (ex: mysecret). .PP The static REMOTE LNS configuration can be used when the friend ISP not have a proxied Radius. .PP If a proxied Radius is used, It will return the RADIUS attributes: .PP Tunnel-Type:1 = L2TP .PD 0 .P .PD Tunnel-Medium-Type:1 = IPv4 .PD 0 .P .PD Tunnel-Password:1 = \[lq]LESECRETL2TP\[rq] .PD 0 .P .PD Tunnel-Server-Endpoint:1 = \[lq]88.xx.xx.x1\[rq] .PD 0 .P .PD Tunnel-Assignment-Id:1 = \[lq]friendisp_lns1\[rq] .PD 0 .P .PD Tunnel-Type:2 += L2TP .PD 0 .P .PD Tunnel-Medium-Type:2 += IPv4 .PD 0 .P .PD Tunnel-Password:2 += \[lq]LESECRETL2TP\[rq] .PD 0 .P .PD Tunnel-Server-Endpoint:2 += \[lq]88.xx.xx.x2\[rq] .PD 0 .P .PD Tunnel-Assignment-Id:2 += \[lq]friendisp_lns2\[rq] .SS PPPOE SETTINGS .PP \f[B]pppoe_if_to_bind\f[R] (string) .PP PPPOE server interface to bind (ex: \[lq]eth0.12\[rq]), If not specified the server PPPOE is not enabled. For the pppoe clustering, all the interfaces PPPOE of the clusters must use the same HW address (MAC address). .PP \f[B]pppoe_service_name\f[R] (string) .PP PPPOE service name (default: NULL). .PP \f[B]pppoe_ac_name\f[R] (string) .PP PPPOE access concentrator name (default: \[lq]l2tpns-pppoe\[rq]). .PP \f[B]pppoe_only_equal_svc_name\f[R] (boolean) .PP If set to yes, the PPPOE server only accepts clients with a \[lq]service-name\[rq] different from NULL and a \[lq]service-name\[rq] equal to server \[lq]service-name\[rq] (default: no). .SS BGP ROUTING .PP The routing configuration section is entered by the command .PP \f[B]router\f[R] \f[B]bgp\f[R] \f[I]as\f[R] .PP where \f[I]as\f[R] specifies the local AS number. .PP Subsequent lines prefixed with \f[B]neighbour\f[R] \f[I]peer\f[R] define the attributes of BGP neighhbours. Valid commands are: .PP \f[B]neighbour\f[R] \f[I]peer\f[R] \f[B]remote-as\f[R] \f[I]as\f[R] .PP \f[B]neighbour\f[R] \f[I]peer\f[R] \f[B]timers\f[R] \f[I]keepalive\f[R] \f[I]hold\f[R] .PP Where \f[I]peer\f[R] specifies the BGP neighbour as either a hostname or IP address, \f[I]as\f[R] is the remote AS number and \f[I]keepalive\f[R], \f[I]hold\f[R] are the timer values in seconds. .SS NAMED ACCESS LISTS .PP Named access lists may be defined with either of .PP \f[B]ip\f[R] \f[B]access-list\f[R] \f[B]standard\f[R] \f[I]name\f[R] .PD 0 .P .PD \f[B]ip\f[R] \f[B]access-list\f[R] \f[B]extended\f[R] \f[I]name\f[R] .PP Subsequent lines starting with permit or deny define the body of the access-list. .SS Standard Access Lists .PP Standard access lists are defined with: .PP {\f[B]permit\f[R]|\f[B]deny\f[R]} \f[I]source\f[R] [\f[I]dest\f[R]] .PP Where \f[I]source\f[R] and \f[I]dest\f[R] specify IP matches using one of: .PP \f[I]address\f[R] \f[I]wildard\f[R] .PD 0 .P .PD \f[B]host\f[R] \f[I]address\f[R] .PD 0 .P .PD \f[B]any\f[R] .PP \f[I]address\f[R] and \f[I]wildard\f[R] are in dotted-quad notation, bits in the \f[I]wildard\f[R] indicate which address bits in \f[I]address\f[R] are relevant to the match (0 = exact match; 1 = don\[cq]t care). .PP The shorthand `host address' is equivalent to `\f[I]address\f[R] \f[B]0.0.0.0\f[R]'; `\f[B]any\f[R]' to `\f[B]0.0.0.0\f[R] \f[B]255.255.255.255\f[R]'. .SS Extended Access Lists .PP Extended access lists are defined with: .PP {\f[B]permit\f[R]|\f[B]deny\f[R]} \f[I]proto\f[R] \f[I]source\f[R] [\f[I]ports\f[R]] \f[I]dest\f[R] [\f[I]ports\f[R]] [\f[I]flags\f[R]] .PP Where \f[I]proto\f[R] is one of \f[B]ip\f[R], \f[B]tcp\f[R] or \f[B]udp\f[R], and \f[I]source\f[R] and \f[I]dest\f[R] are as described above for standard lists. .PP For TCP and UDP matches, source and destination may be optionally followed by a ports specification: .PP {\f[B]eq|neq|gt|lt\f[R]} \f[I]port\f[R] .PD 0 .P .PD \f[B]range\f[R] \f[I]from\f[R] \f[I]to\f[R] .PP \f[I]flags\f[R] may be one of: .PP {\f[B]match-any|match-all\f[R]} {\f[B]+|-\f[R]}{\f[B]fin|syn|rst|psh|ack|urg\f[R]} \&... .IP .nf \f[C] Match packets with any or all of the tcp flags set (+) or clear (-). \f[R] .fi .PP \f[B]established\f[R] .IP .nf \f[C] Match \[dq]established\[dq] TCP connections: packets with RST or ACK set, and SYN clear. \f[R] .fi .PP \f[B]fragments\f[R] .IP .nf \f[C] Match IP fragments. May not be specified on rules with layer 4 matches. \f[R] .fi .SH SEE ALSO .PP l2tpns(8), nsctl(8) .SH AUTHORS l2tpns developers . l2tpns-2.3.3/docs/src/000077500000000000000000000000001400724550600144635ustar00rootroot00000000000000l2tpns-2.3.3/docs/src/html/000077500000000000000000000000001400724550600154275ustar00rootroot00000000000000l2tpns-2.3.3/docs/src/html/manual.md000066400000000000000000001424541400724550600172400ustar00rootroot00000000000000Overview ======== `l2tpns` is half of a complete L2TP implementation. It supports only the LNS side of the connection. L2TP (Layer 2 Tunneling Protocol) is designed to allow any layer 2 protocol (e.g. Ethernet, PPP) to be tunneled over an IP connection. `l2tpns` implements PPP over L2TP only. There are a couple of other L2TP implementations, of which [l2tpd](http://sourceforge.net/projects/l2tpd) is probably the most popular. l2tpd also will handle being either end of a tunnel, and is a lot more configurable than `l2tpns`. However, due to the way it works, it is nowhere near as scalable. `l2tpns` uses the TUN/TAP interface provided by the Linux kernel to receive and send packets. Using some packet manipulation it doesn\'t require a single interface per connection, as l2tpd does. This allows it to scale extremely well to very high loads and very high numbers of connections. It also has a plugin architecture which allows custom code to be run during processing. An example of this is in the walled garden module included. Installation ============ Requirements {#installation-requirements} ------------ - Linux kernel version 2.4 or above, with the Tun/Tap interface either compiled in, or as a module. - libcli 1.8.5 or greater. You can get this from [SourceForge](http://sourceforge.net/projects/libcli) Compiling {#installation-compile} --------- You can generally get away with just running `make` from the source directory. This will compile the daemon, associated tools and any modules shipped with the distribution. Installing {#installation-install} ---------- After you have successfully compiled everything, run `make install` to install it. By default, the binaries are installed into `/usr/sbin`, the configuration into `/etc/l2tpns`, and the modules into `/usr/lib/l2tpns`. You will definately need to edit the configuration files before you start. See [Configuration](#configuration) for more information. Running {#installation-run} ------- You only need to run `/usr/sbin/l2tpns` as root to start it. It does not normally detach to a daemon process (see the `-d` option), so you should perhaps run it from `init`. By default there is no log destination set, so all log messages will go to stdout. Configuration ============= All configuration of the software is done from the files installed into `/etc/l2tpns`. `startup-config` {#config-startup} ---------------- This is the main configuration file for `l2tpns`. The format of the file is a list of commands that can be run through the command-line interface. This file can also be written directly by the `l2tpns` process if a user runs the `write memory` command, so any comments will be lost. However if your policy is not to write the config by the program, then feel free to comment the file with a `#` or `!` at the beginning of the line. A list of the possible configuration directives follows. Each of these should be set by a line like: set configstring \"value\" set ipaddress 192.168.1.1 set boolean true `debug` (int) : Sets the level of messages that will be written to the log file. The value should be between 0 and 5, with 0 being no debugging, and 5 being the highest. A rough description of the levels is: `0`: Critical Errors : Things are probably broken `1`: Errors : Things might have gone wrong, but probably will recover `2`: Warnings : Just in case you care what is not quite perfect `3`: Information : Parameters of control packets `4`: Calls : For tracing the execution of the code `5`: Packets : Everything, including a hex dump of all packets processed\... probably twice Note that the higher you set the debugging level, the slower the program will run. Also, at level 5 a *lot* of information will be logged. This should only ever be used for working out why it doesn\'t work at all. `log_file` (string) : This will be where all logging and debugging information is written to. This may be either a filename, such as `/var/log/l2tpns`, or the special magic string `syslog:`facility, where facility is any one of the syslog logging facilities, such as `local5`. `pid_file` (string) : If set, the process id will be written to the specified file. The value must be an absolute path. `random_device` (string) : Path to random data source (default `/dev/urandom`). Use \"\" to use the rand() library function. `l2tp_secret` (string) : The secret used by `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. `l2tp_mtu` (int) : MTU of interface for L2TP traffic (default: `1500`). Used to set link MRU and adjust TCP MSS. `ppp_restart_time` (int); `ppp_max_configure` (int); `ppp_max_failure` (int) : PPP counter and timer values, as described in §4.1 of [RFC1661](ftp://ftp.rfc-editor.org/in-notes/rfc1661.txt). `primary_dns` (ip address); `econdary_dns` (ip address) : 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. `primary_radius` (ip address); `secondary_radius` (ip address) : 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. ::: {.note} In addition to the source IP address and identifier, the RADIUS server *must* include the source port when detecting duplicates to suppress (in order to cope with a large number of sessions coming on-line simultaneously `l2tpns` uses a set of udp sockets, each with a separate identifier). ::: `primary_radius_port` (short); `secondary_radius_port` (short) : Sets the authentication ports for the primary and secondary RADIUS servers. The accounting port is one more than the authentication port. If no RADIUS ports are given, the authentication port defaults to 1645, and the accounting port to 1646. `radius_accounting` (boolean) : If set to true, then RADIUS accounting packets will be sent. This means that a Start record will be sent when the session is successfully authenticated, and a Stop record will be sent when the session is closed. `radius_interim` (int) : If `radius_accounting` is on, defines the interval between sending of RADIUS interim accounting records (in seconds). `radius_secret` (string) : This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail. `radius_authtypes` (string) : A comma separated list of supported RADIUS authentication methods (`pap` or `chap`), in order of preference (default `pap`). `radius_bind_min` (short); `radius_bind_max` (short) : Define a port range in which to bind sockets used to send and receive RADIUS packets. Must be at least RADIUS\_FDS (64) wide. Simplifies firewalling of RADIUS ports (default: dynamically assigned). `radius_dae_port` (short) : Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) requests (default: `3799`). `allow_duplicate_users` (boolean) : 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. `guest_account` (string) : Allow multiple logins matching this specific username. `bind_address` (ip 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. `peer_address` (ip address) : Address to send to clients as the default gateway. `send_garp` (boolean) : Determines whether or not to send a gratuitous ARP for the bind\_address when the server is ready to handle traffic (default: `true`). This value is ignored if BGP is configured. `throttle_speed` (int) : Sets the default speed (in kbits/s) which sessions will be limited to. If this is set to 0, then throttling will not be used at all. Note: You can set this by the CLI, but changes will not affect currently connected users. `throttle_buckets` (int) : Number of token buckets to allocate for throttling. Each throttled session requires two buckets (in and out). `accounting_dir` (string) : 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. Each file dumped begins with a header, where each line is prefixed by `#`. Following the header is a single line for every connected user, fields separated by a space. The fields are username, ip, qos, uptxoctets, downrxoctets. The qos field is 1 if a standard user, and 2 if the user is throttled. `dump_speed` (boolean) : 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 `uptime` command on the CLI. `multi_read_count` (int) : 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. `scheduler_fifo` (boolean) : Sets the scheduling policy for the `l2tpns` process to `SCHED_FIFO`. This causes the kernel to immediately preempt any currently running `SCHED_OTHER` (normal) process in favour of `l2tpns` when it becomes runnable. Ignored on uniprocessor systems. `lock_pages` (boolean) : Keep all pages mapped by the `l2tpns` process in memory. `icmp_rate` (int) : Maximum number of host unreachable ICMP packets to send per second. `packet_limit` (int) : 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). `cluster_address` (ip address) : Multicast cluster address (default: 239.192.13.13). See [Clustering](#clustering) for more information. `cluster_port` (udp port) : UDP cluster port (default: 32792). See [Clustering](#clustering) for more information. `cluster_interface` (string) : Interface for cluster packets (default: eth0) `cluster_mcast_ttl` (int) : TTL for multicast packets (default: 1). `cluster_hb_interval` (int) : Interval in tenths of a second between cluster heartbeat/pings. `cluster_hb_timeout` (int) : 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. `cluster_master_min_adv` (int) : Determines the minimum number of up to date slaves required before the master will drop routes (default: 1). `ipv6_prefix` (ipv6 address) : 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. ### BGP {#config-startup-bgp} BGP routing configuration is entered by the command: router bgp as where as specifies the local AS number. Subsequent lines prefixed with neighbour peer define the attributes of BGP neighhbours. Valid commands are: neighbour peer remote-as as neighbour peer timers keepalive hold Where peer specifies the BGP neighbour as either a hostname or IP address, as is the remote AS number and keepalive, hold are the timer values in seconds. ### Access Lists {#config-startup-acl} Named access-lists are configured using one of the commands: ip access-list standard name ip access-list extended name Subsequent lines prefixed with `permit` or `deny` define the body of the access-list. Standard access-list syntax: {`permit`\|`deny`} {host\|source source-wildcard\|`any`} \[{host\|destination destination-wildcard\|`any`}\] Extended access-lists: {`permit`\|`deny`} `ip` {host\|source source-wildcard\|`any`} {host\|destination destination-wildcard\|`any`} \[`fragments`\] {`permit`\|`deny`} `udp` {host\|source source-wildcard\|`any`} \[{`eq`\|`neq`\|`gt`\|`lt`} port\|`range` from to\] {host\|destination destination-wildcard\|`any`} \[{`eq`\|`neq`\|`gt`\|`lt`} port\|`range` from to\] \[`fragments`\] {`permit`\|`deny`} `tcp` {host\|source source-wildcard\|`any`} \[{`eq`\|`neq`\|`gt`\|`lt`} port\|`range` from to\] {host\|destination destination-wildcard\|`any`} \[{`eq`\|`neq`\|`gt`\|`lt`} port\|`range` from to\] \[{`established`\|{`match-any`\|`match-all`} {`+`\|`-`}{`fin`\|`syn`\|`rst`\|`psh`\|`ack`\|`urg`} \...\|`fragments`\] `users` {#config-users} ------- Usernames and passwords for the command-line interface are stored in this file. The format is username:password where password may either by plain text, an MD5 digest (prefixed by `$1`salt`$`) or a DES password, distinguished from plain text by the prefix `{crypt}`. The username `enable` has a special meaning and is used to set the enable password. ::: {.important} If this file doesn\'t exist, then anyone who can get to port 23 will be allowed access without a username or password. ::: `ip_pool` {#config-ip-pool} --------- This file is used to configure the IP address pool which user addresses are assigned from. This file should contain either an IP address or a CIDR network per line. e.g.: 192.168.1.1 192.168.1.2 192.168.1.3 192.168.4.0/24 172.16.0.0/16 10.0.0.0/8 Keep in mind that `l2tpns` can only handle 65535 connections per process, so don\'t put more than 65535 IP addresses in the configuration file. They will be wasted. `build-garden` {#config-build-garden} -------------- The garden plugin on startup creates a NAT table called \"garden\" then sources the `build-garden` script to populate that table. All packets from gardened users will be sent through this table. Example: 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 Operation ========= A running l2tpns process can be controlled in a number of ways. The primary method of control is by the Command-Line Interface (CLI). You can also remotely send commands to modules via the `nsctl` client provided. There are also a number of signals that l2tpns understands and takes action when it receives them. Command-Line Interface {#operation-cli} ---------------------- You can access the command line interface by telneting to port 23. There is no IP address restriction, so it\'s a good idea to firewall this port off from anyone who doesn\'t need access to it. See [](#config-users) for information on restricting access based on a username and password. The CLI gives you real-time control over almost everything in the process. The interface is designed to look like a Cisco device, and supports things like command history, line editing and context sensitive help. This is provided by linking with the [libcli](http://sourceforge.net/projects/libcli) library. Some general documentation of the interface is [here](http://sourceforge.net/docman/display_doc.php?docid=20501&group_id=79019). After you have connected to the telnet port (and perhaps logged in), you will be presented with a `hostname>` prompt. Enter `help` to get a list of possible commands, or press `?` for context-specific help. A brief overview of the more important commands follows: `show session [ID] ` : Detailed information for a specific session is presented if you specify a session ID argument. If no ID is given, a summary of all connected sessions is produced. Note that this summary list can be around 185 columns wide, so you should probably use a wide terminal. The columns listed in the summary are: -------------- -------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------ `SID` Session ID `TID` Tunnel ID See also the [show tunnel](#operation-cli-show-tunnel) CLI command. `Username` The username given in the PPP authentication. If this is \*, then LCP authentication has not completed. `IP` The IP address given to the session. If this is 0.0.0.0, IPCP negotiation has not completed `I` Intercept Y or N: indicates whether the session is being snooped. See also the [snoop](#operation-cli-snoop) CLI command. `T` Throttled Y or N: indicates whether the session is currently throttled. See also the [throttle](#operation-cli-throttle) CLI command. `G` Walled Garden Y or N: indicates whether the user is trapped in the walled garden. This field is present even if the garden module is not loaded. `6` IPv6 Y or N: indicates whether the session has IPv6 active (IPV6CP open) `opened` The number of seconds since the session started `downloaded` Number of bytes downloaded by the user `uploaded` Number of bytes uploaded by the user `idle` The number of seconds since traffic was detected on the session `LAC` The IP address of the LAC the session is connected to. `CLI` The Calling-Line-Identification field provided during the session setup. This field is generated by the LAC. -------------- -------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------ `show users`; `show user username ` : With no arguments, display a list of currently connected users. If an argument is given, the session details for the given username are displayed. `show tunnel [ID]` : Produce a summary list of all open tunnels, or detail on a specific tunnel ID. The columns listed in the summary are: ---------- ----------------------------------------------------------------------------------------------------------- TID Tunnel ID Hostname The hostname for the tunnel as provided by the LAC. This has no relation to DNS, it is just a text field. IP The IP address of the LAC State Tunnel state: Free, Open, Dieing, Opening Sessions The number of open sessions on the tunnel ---------- ----------------------------------------------------------------------------------------------------------- `show pool` : Displays the current IP address pool allocation. This will only display addresses that are in use, or are reserved for re-allocation to a disconnected user. If an address is not currently in use, but has been used, then in the User column the username will be shown in square brackets, followed by the time since the address was used: IP Address Used Session User 192.168.100.6 N [joe.user] 1548s `show radius` : Show a summary of the in-use RADIUS sessions. This list should not be very long, as RADIUS sessions should be cleaned up as soon as they are used. The columns listed are: --------- ---------------------------------------------------------------------------------------------------- Radius The ID of the RADIUS request. This is sent in the packet to the RADIUS server for identification State The state of the request: WAIT, CHAP, AUTH, IPCP, START, STOP or NULL Session The session ID that this RADIUS request is associated with Retry If a response does not appear to the request, it will retry at this time. This is a Unix timestamp Try Retry count. The RADIUS request is discarded after 3 retries --------- ---------------------------------------------------------------------------------------------------- `show running-config` : This will list the current running configuration. This is in a format that can either be pasted into the configuration file, or run directly at the command line. `show counters` : Internally, counters are kept of key values, such as bytes and packets transferred, as well as function call counters. This function displays all these counters, and is probably only useful for debugging. You can reset these counters by running `clear counters`. `show cluster` : Show cluster status. Shows the cluster state for this server (Master/Slave), information about known peers and (for slaves) the master IP address, last packet seen and up-to-date status. See [Clustering](#clustering) for more information. `write memory` : This will write the current running configuration to the config file `startup-config`, which will be run on a restart. `snoop user IP port` : You must specify a username, IP address and port. All packets for the current session for that username will be forwarded to the given host/port. Specify `no snoop username` to disable interception for the session. If you want interception to be permanent, you will have to modify the RADIUS response for the user. See [Interception](#interception). `throttle user [in|out] rate` : You must specify a username, which will be throttled for the current session to rate Kbps. Prefix rate with `in` or `out` to set different upstream and downstream rates. Specify `no throttle username` to disable throttling for the current session. If you want throttling to be permanent, you will have to modify the RADIUS response for the user. See [Throttling](#throttling). `drop session` : This will cleanly disconnect the session specified by session ID. `drop tunnel` : This will cleanly disconnect the tunnel specified by tunnel ID, as well as all sessions on that tunnel. `uptime` : This will show how long the `l2tpns` process has been running, and the current bandwidth utilization: 17:10:35 up 8 days, 2212 users, load average: 0.21, 0.17, 0.16 Bandwidth: UDP-ETH:6/6 ETH-UDP:13/13 TOTAL:37.6 IN:3033 OUT:2569 The bandwidth line contains 4 sets of values: ------------ ---------------------------------------------------------------------------------------- UDP-ETH The current bandwidth going from the LAC to the ethernet (user uploads), in mbits/sec. ETH-UDP The current bandwidth going from ethernet to the LAC (user downloads). TOTAL The total aggregate bandwidth in mbits/s. IN and OUT Packets/per-second going between UDP-ETH and ETH-UDP. ------------ ---------------------------------------------------------------------------------------- These counters are updated every second. `configure terminal` : Enter configuration mode. Use `exit` or `^Z` to exit this mode. The following commands are valid in this mode: `load plugin name` : Load a plugin. You must specify the plugin name, and it will search in `/usr/lib/l2tpns` for `name.so`. You can unload a loaded plugin with `remove plugin name`. `set` \... : Set a configuration variable. You must specify the variable name, and the value. If the value contains any spaces, you should quote the value with double (\") or single (\') quotes. You can set any configuration value in this way, although some may require a restart to take effect. See [](#config-startup). `router bgp` \... : Configure BGP. See [BGP](#config-startup-bgp). `ip access-list` \... : Configure a named access list. See [Access Lists](#config-startup-acl). nsctl {#operation-nsctl} ----- `nsctl` sends messages to a running `l2tpns` instance to be control plugins. Arguments are `command` and optional args. See `nsctl(8)`. Built-in command are `load_plugin`, `unload_plugin` and `help`. Any other commands are passed to plugins for processing by the `plugin_control` function. Signals {#operation-signals} ------- While the process is running, you can send it a few different signals, using the `kill` command. killall -HUP l2tpns The signals understood are: SIGHUP : Reload the config from disk and re-open log file. 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. See [Clustering](#clustering) SIGQUIT : Shut down tunnels and sessions, exit process when complete. Throttling ========== `l2tpns` contains support for slowing down user sessions to whatever speed you desire. The global setting `throttle_speed` defines the default throttle rate. To throttle a sesion permanently, add a `Cisco-AVPair` RADIUS attribute. The `autothrotle` module interprets the following attributes: ----------------------------------------------- -------------------------------------------------------------------------------- `throttle=yes` Throttle upstream/downstream traffic to the configured `throttle_speed`. `throttle=rate` Throttle upstream/downstream traffic to the specified rate Kbps. `lcp:interface-config#1=service-policy input Alternate (Cisco) format: throttle upstream/downstream to specified rate Kbps. rate` `lcp:interface-config#2=service-policy output rate` ----------------------------------------------- -------------------------------------------------------------------------------- You can also enable and disable throttling an active session using the [throttle](#operation-cli-throttle) CLI command. Interception ============ You may have to deal with legal requirements to be able to intercept a user\'s traffic at any time. `l2tpns` allows you to begin and end interception on the fly, as well as at authentication time. When a user is being intercepted, a copy of every packet they send and receive will be sent wrapped in a UDP packet to a specified host. The UDP packet contains just the raw IP frame, with no extra headers. The script `scripts/l2tpns-capture` may be used as the end-point for such intercepts, writing the data in PCAP format (suitable for inspection with `tcpdump`). To enable or disable interception of a connected user, use the [snoop](#operation-cli-snoop) and `no snoop` CLI commands. These will enable interception immediately. If you wish the user to be intercepted whenever they reconnect, you will need to modify the RADIUS response to include the Vendor-Specific value `Cisco-AVPair="intercept=ip:port"`. For this feature to be enabled, you need to have the `autosnoop` module loaded. Plugins ======= So as to make `l2tpns` as flexible as possible, a plugin API is include which you can use to hook into certain events. There are a some standard modules included which may be used as examples: `autosnoop`, `autothrottle`, `garden`, `sessionctl`, `setrxspeed`, `snoopctl`, `stripdomain` and `throttlectl`. When an event occurs that has a hook, `l2tpns` looks for a predefined function name in every loaded module, and runs them in the order the modules were loaded. The function should return `PLUGIN_RET_OK` if it is all OK. If it returns `PLUGIN_RET_STOP`, then it is assumed to have worked, but that no further modules should be run for this event. A return of `PLUGIN_RET_ERROR` means that this module failed, and no further processing should be done for this event. ::: {.note} Use this with care. ::: Most event functions take a specific structure named `param_event`, which varies in content with each event. The function name for each event will be `plugin_event`, so for the event timer, the function declaration should look like: int plugin_timer(struct param_timer *data); A list of the available events follows, with a list of all the fields in the supplied structure: +----------------------+----------------------+----------------------+ | Event | Description | Arguments | +======================+======================+======================+ | `plugin_init` | Called when the | `s | | | plugin is loaded. A | truct pluginfuncs *` | | | pointer to a struct | | | | containing function | | | | pointers is passed | | | | as the only | | | | argument, allowing | | | | the plugin to call | | | | back into the main | | | | code. | | | | | | | | Prior to loading the | | | | plugin, `l2tpns` | | | | checks the API | | | | version the plugin | | | | was compiled | | | | against. All plugins | | | | should contain: | | | | | | | | int | | | | plugin_api_version = | | | | PLUGIN_API_VERSION; | | +----------------------+----------------------+----------------------+ | See `pluginfuncs` | | | | structure in | | | | `plugin.h` for | | | | available functions. | | | +----------------------+----------------------+----------------------+ | `plugin_done` | Called when the | `void` | | | plugin is unloaded | | | | or `l2tpns` is | | | | shutdown. | | +----------------------+----------------------+----------------------+ | No arguments. | | | +----------------------+----------------------+----------------------+ | `plugin_pre_auth` | Called after a | `struct plug | | | RADIUS response has | in param_pre_auth *` | | | been received, but | | | | before it has been | | | | processed by the | | | | code. This will | | | | allow you to modify | | | | the response in some | | | | way. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | `char *username` | User name. | | +----------------------+----------------------+----------------------+ | `char *password` | Password. | | +----------------------+----------------------+----------------------+ | `int protocol` | Authentication | | | | protocol: `0xC023` | | | | for PAP, `0xC223` | | | | for CHAP. | | +----------------------+----------------------+----------------------+ | `int continue_auth` | Set to 0 to stop | | | | processing | | | | authentication | | | | modules. | | +----------------------+----------------------+----------------------+ | `plugin_post_auth` | Called after a | `struct plugi | | | RADIUS response has | n param_post_auth *` | | | been received, and | | | | the basic checks | | | | have been performed. | | | | This is what the | | | | `garden` module uses | | | | to force | | | | authentication to be | | | | accepted. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | `char *username` | User name. | | +----------------------+----------------------+----------------------+ | `short auth_allowed` | Initially true or | | | | false depending on | | | | whether | | | | authentication has | | | | been allowed so far. | | | | You can set this to | | | | 1 or 0 to force | | | | authentication to be | | | | accepted or | | | | rejected. | | +----------------------+----------------------+----------------------+ | `int protocol` | Authentication | | | | protocol: `0xC023` | | | | for PAP, `0xC223` | | | | for CHAP. | | +----------------------+----------------------+----------------------+ | `plugin_timer` | Run once per second. | `struct p | | | | lugin param_timer *` | +----------------------+----------------------+----------------------+ | `time_t time_now` | The current unix | | | | timestamp. | | +----------------------+----------------------+----------------------+ | `plugin_new_session` | Called after a | `struct plugin | | | session is fully set | param_new_session *` | | | up. The session is | | | | now ready to handle | | | | traffic. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | ` | Called when a | `struct plugin p | | plugin_kill_session` | session is about to | aram_kill_session *` | | | be shut down. This | | | | may be called | | | | multiple times for | | | | the same session. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | `plugin_control` | Called in whenever a | `struct plu | | | `nsctl` packet is | gin param_control *` | | | received. This | | | | should handle the | | | | packet and form a | | | | response if | | | | required. | | | | | | | | Plugin-specific help | | | | strings may be | | | | included in the | | | | output of | | | | `nsctl help` by | | | | defining a `NULL` | | | | terminated list of | | | | strings as follows: | | | | | | | | char | | | | *plugin_control_hel | | | | p[] = { ..., NULL }; | | +----------------------+----------------------+----------------------+ | `int iam_master` | If true, this node | | | | is the cluster | | | | master. | | +----------------------+----------------------+----------------------+ | `int argc` | `nsctl` arguments. | | +----------------------+----------------------+----------------------+ | `char **argc` | | | +----------------------+----------------------+----------------------+ | `int response` | Response from | | | | control message (if | | | | handled): should be | | | | either | | | | `NSCTL_RES_OK` or | | | | `NSCTL_RES_ERR`. | | +----------------------+----------------------+----------------------+ | `char *additional` | Additional | | | | information, output | | | | by `nsctl` on | | | | receiving the | | | | response. | | +----------------------+----------------------+----------------------+ | `plu | Called whenever a | `struct plugin para | | gin_radius_response` | RADIUS response | m_radius_response *` | | | includes a | | | | `Cisco-AVPair` | | | | value. The value is | | | | split into | | | | key`=`value pairs. | | | | Will be called once | | | | for each pair in the | | | | response. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | `char *key` | Key and value. | | +----------------------+----------------------+----------------------+ | `char *value` | | | +----------------------+----------------------+----------------------+ | ` | Called whenever a | `struct p | | plugin_radius_reset` | RADIUS CoA request | aram_radius_reset *` | | | is received to reset | | | | any options to | | | | default values | | | | before the new | | | | values are applied. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | `pl | Called when | `struct par | | ugin_radius_account` | preparing a RADIUS | am_radius_account *` | | | accounting record to | | | | allow additional | | | | data to be added to | | | | the packet. | | +----------------------+----------------------+----------------------+ | `tunnelt *t` | Tunnel. | | +----------------------+----------------------+----------------------+ | `sessiont *s` | Session. | | +----------------------+----------------------+----------------------+ | `uint8_t **packet` | Pointer to the end | | | | of the currently | | | | assembled packet | | | | buffer. The value | | | | should be | | | | incremented by the | | | | length of any data | | | | added. | | +----------------------+----------------------+----------------------+ | `p | Called when a node | `void` | | lugin_become_master` | elects itself | | | | cluster master. | | +----------------------+----------------------+----------------------+ | No arguments. | | | +----------------------+----------------------+----------------------+ | `plugin | Called once for each | `sessiont *` | | _new_session_master` | open session on | | | | becoming cluster | | | | master. | | +----------------------+----------------------+----------------------+ | Session. | | | +----------------------+----------------------+----------------------+ Walled Garden ============= A \"Walled Garden\" is implemented so that you can provide perhaps limited service to sessions that incorrectly authenticate. Whenever a session provides incorrect authentication, and the RADIUS server responds with Auth-Reject, the walled garden module (if loaded) will force authentication to succeed, but set the `walled_garden` flag in the session structure, and adds an `iptables` rule to the `garden_users` chain to cause all packets for the session to traverse the `garden` chain. This doesn\'t *just work*. To set this all up, you will to setup the `garden` nat table with the [build-garden](#config-build-garden) script with rules to limit user\'s traffic. For example, to force all traffic except DNS to be forwarded to 192.168.1.1, add these entries to your `build-garden` script: iptables -t nat -A garden -p tcp --dport ! 53 -j DNAT --to 192.168.1.1 iptables -t nat -A garden -p udp --dport ! 53 -j DNAT --to 192.168.1.1 `l2tpns` will add entries to the `garden_users` chain as appropriate. You can check the amount of traffic being captured using the following command: iptables -t nat -L garden -nvx Filtering ========= Sessions may be filtered by specifying `Filter-Id` attributes in the RADIUS reply. filter.`in` specifies that the named access-list filter should be applied to traffic from the customer, filter.`out` specifies a list for traffic to the customer. Clustering ========== An `l2tpns` cluster consists of one\* or more servers configured with the same configuration, notably the multicast `cluster_address` and the `cluster_port` \*A stand-alone server is simply a degraded cluster. Initially servers come up as cluster slaves, and periodically (every `cluster_hb_interval`/10 seconds) send out ping packets containing the start time of the process to the multicast `cluster_address` on `cluster_port`. A cluster master sends heartbeat rather than ping packets, which contain those session and tunnel changes since the last heartbeat. When a slave has not seen a heartbeat within `cluster_hb_timeout`/10 seconds it \"elects\" a new master by examining the list of peers it has seen pings from and determines which of these and itself is the \"best\" candidate to be master. \"Best\" in this context means the server with the highest uptime (the highest IP address is used as a tie-breaker in the case of equal uptimes). After discovering a master, and determining that it is up-to-date (has seen an update for all in-use sessions and tunnels from heartbeat packets) will raise a route (see [Routing](#routing)) for the `bind_address` and for all addresses/networks in `ip_pool`. Any packets recieved by the slave which would alter the session state, as well as packets for throttled or gardened sessions are forwarded to the master for handling. In addition, byte counters for session traffic are periodically forwarded. The master, when determining that it has at least one\* up-to-date slave will drop all routes (raising them again if all slaves disappear) and subsequently handle only packets forwarded to it by the slaves. \*Configurable with `cluster_master_min_adv` Multiple clusters can be run on the same network by just using different multicast `cluster_address`. However, for a given host to be part of multiple clusters without mixing the clusters, `cluster_port` must be different for each cluster. Routing ======= If you are running a single instance, you may 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. Cisco uses `maximum-paths ibgp` for IBGP. If this is not supported by your IOS revision, you can use `maximum-paths` (which works for EBGP) and set `as_number` to a private value such as 64512. l2tpns-2.3.3/docs/src/html/practical-vpns.md000066400000000000000000000503561400724550600207100ustar00rootroot00000000000000Overview of VPNs and IPsec {#overview} ========================== Virtual Private Networks {#vpns} ------------------------ The purpose of a VPN is to create a secure channel ontop of an un-secure medium, where a computer or a device are put in each end-point in order to establish communication, each of these end-points are often reffered to as Point of Presense, or POP. This kind of a communication allows the capability of creating a Virtual Private Network, which is accesable over a medium such as the Internet and thus, extend the physical boundaries of an existing local network. VPNs have three forms: Site-To-Site VPNs : these setups exist in order to extend the local network to create a much bigger LAN over the Internet. Network-To-Host or Remote access VPNs : where a central VPN server is able to achieve multiple connections, often reffered to as RoadWarrior VPNs. (This setup is very common among ISPs) Network-To-Network : extranet VPNs allow secure connections within branches and business partners, they are an extension of a Site-To-Site VPNs. ![site to site](./images/site-to-site-vpn.png) shows a Site-To-Site VPN diagram. IP/VPNs are connections which are based upon IP tunnels. A tunnel is a way to encapsulate an IP packet inside another IP packet or some other type of packet. Why do we need tunneling? A Virtual Private Network is identified by IANA\'s private IP assignments and so such packet can not go beyond the uplink Internet interface. ![tunneling process](./images/tunneling-process.png) shows the tunneling process. Several tunneling protocols are available for manifesting VPNs. L2F : Layer 2 Forwarding, an older implementation which assume position at the link layer of the OSI. It has no encryption capabilities and hence, deprecated. L2TP : Layer 2 Tunneling Protocol, still no encryption capabilities. PPTP : Point-to-Point Tunneling Protocol, and yet again, no encryption. As seen, the requirement of encryption enhancement is urgent in order to assure authentication, data integrity and privacy. IPsec solves this by providing a suite of security measures implemented at layer 3. IP Security Suite (IPsec) {#ipsec} ------------------------- VPN Security is now appearing, this complex things. How so? VPN tunnels by themselves are easily maintained by single-standalone tools like pppd, l2tpns, stunnel and others. Involving security with VPNs though requires more: - authentication, data integrity and privacy - keying management ::: {.note} Keys are secrets being shared by two end-points to provide a secure mean of communication against a third-party connection from sniffing the actual data. ::: Different ways to handle key management include RADIUS (Remote Authentication Dial In User Service) systems which provide AAA (Authentication, Authorization and Accounting). Another solution is ISAKMP/Oackly - Internet Security Association and Key Management Protocol. This solution requires you to posess one of the following: - something you have - something you know - something you are The more requirements you meet the more secure is the medium, once established. Let\'s review, something we have is like a certificate, it proves who we are. Something we know, is a key, a secret password which we were told in a whisper, and something we are is our-fingerprint which identifies ourselves from other individuals. ### IPsec in Depth IPsec consists of two main protocols, an Authentication Header and Encapsulation Security Payload, also known as AH and ESP. Although it is not bound to these and can be extended (and often is) to other standarts such as - Data Encryption Standart (DES and 3DES) - Diffie-Hellman (DH) - Secure Hash Algorithm-1 (SHA1) - Message Digest 5 (MD5) - Internet Key Exchange (IKE) - Certification Authorities (CA) We\'ll be deploying an IKE daemon to handle the key management, which uses the Diffie- Hellman cryptography protocol in order to allow two parties to establish a connection based upon a shared secret key that both parties posess. (Authentication within IKE is handled by MD5 hashing) IKE is responsible for authentication of two IPsec parties, negotiation of keys for encryption algorithms and security associations. This process is commonly regarded as two phases: Phase 1: IKE Security Association : The IKE daemon authenticates against the peers in order to achieve a secure channel, according to the Diffie-Hellman key agreement. Phase 2: IKE IPsec Negotiation : After achieving an authenticated channel, the parties now negotiate a secure transform (the way to encrypt and secure the medium) where the sender is offering his/hers transform set after which the receiver decides upon one. An IPsec session can now safely begin. Just to be clear, a Security Association is an agreed relation between two parties which describes how they will use security services (from IPsec) to communicate. ### IPsec Modes IPsec can operate in two different modes: Transport mode : takes place when two devices (like a station and a gateway (now considered a host)) are establishing a connection which upon they both support IPsec. Tunnel mode : we require tunnel mode when we proxy IPsec connetions between two stations behind the IPsec gateway. For example, in a Site-to-Site VPN a tunnel mode lives, since it exists in order to provide the stations behind these gateways runing the VPN/IPsec to communicate securely. In this situation, both end-points are runing an IPsec software. In definition, a tunnel mode IPsec is better secured than transport. Without going too deep into the ins-and-outs of the technical side, transport mode doesn\'t encapsulate the actual IP layer but only the tcp/udp (Transport layer of the OSI) where-as a tunnel mode encapsulate both the Transport layer and the IP layer into a new IP packet. To summarize, we need VPNs for data-exchange methods and a set of IPsec tools for security reasons. VPN Deployment {#deployment} ============== I\'ve assembled another diagram to view the actual VPN setup. ![vpn deployment](./images/vpn-deployment.png) gives a general description of how the network will be layed out in real-world scenario. We notice that a single Linux box is acting as a Gateway and has all the services included with it. This is a bad idea from a security prespective but it\'s easy to just deploy the FreeRADIUS and MySQL servers on another machine. Of course the L2TPns and the rest of the IPsec tools suite would have to remain on the Gateway box (not necessarily the Firewall). ![vpn process](./images/vpn-process.png) attempts to explain the actual process that the VPN takes and to detail the place that each of that application-in-charge takes place. Requirements {#deployment-requirements} ------------ ### The Toolbox {#deployment-requirements-toolbox} Following is a description of the requirements you will have to meet: A Linux box : preferably a 2.4.27 kernel or higher. Debian is the chosen distribution which means we\'ll be using apt-get for installation, but I\'ll also focus on basic source tarballs installation. Dependencies: - ipsec configuration in the kernel L2TPns : an L2TP PPP Termination tool. Dependencies: - libcli 1.8.0 or greater - tun/tap interface compiled in the kernel or as a module FreeRADIUS : For authentication, and accounting. MySQL : To act as a back-end database for the RADIUS. OpenSwan : Provides the ipsec suite package. ### Kernel Support {#deployment-requirements-kernel} Debian stock kernel 2.4.27 and up are ipsec compatible although if you think otherwise check for the kernel-patch-openswan package. Installation {#deployment-installation} ------------ ### L2TPns {#deployment-installation-l2tpns} #### Installation {#deployment-installation-l2tpns-install} > 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. In a personal note - L2TPns is highly configurable for many cases, and extremely reliable for production/commerical use. Step 1: : Make sure you have libcli-1.8 development package installed: # apt-cache search libcli libcli-dev - emulates a cisco style telnet command-line interface (dev files) libcli1 - emulates a cisco style telnet command-line interface # apt-get install libcli-dev Step 2: : Download the source from [SourceForge](http://sourceforge.net/projects/l2tpns/). Step 3: : Build and install: `make && make install` ::: {.note} Alternately, you can skip these steps and simply `apt-get install l2tpns`. ::: ::: {.note} On RPM-based distributions, you should be able to make packages from the libcli and l2tpns source tarballs with `rpmbuild -ta`. ::: Once compiliation is done you will have l2tpns in `/usr/sbin/l2tpns`, and all configuration files can be found in `/etc/l2tpns/`. #### Configuration {#deployment-installation-l2tpns-config} The only configuration that L2TPns takes is centralized in the configuration file `/etc/l2tpns/startup-config`. set debug 2 # Debugging level set log_file "/var/log/l2tpns" # Log file: comment out to use stderr, use # "syslog:facility" for syslog set pid_file "/var/run/l2tpns.pid" # Write pid to this file set l2tp_secret "secret" # shared secret set primary_dns 212.117.128.6 # Only 2 DNS server entries are allowed set secondary_dns 212.117.129.3 set primary_radius 192.168.0.1 # Can have multiple radius server entries, # but ony one radius secret set primary_radius_port 1812 set radius_secret "radius_secret" set radius_accounting yes set radius_dae_port 3799 set accounting_dir "/var/run/l2tpns/acct" # Write usage accounting files into specified # directory set peer_address 192.168.0.1 # Gateway address given to clients load plugin "sessionctl" # Drop/kill sessions load plugin "autothrottle" # Throttle/snoop based on RADIUS load plugin "throttlectl" # Control throttle/snoop with nsctl load plugin "snoopctl" This is the trimmed down version of probably most of the common configuration and even some extra options. Important configuration options are highlited and you should adjust these to meet your network needs. We can deploy all of the environment into one box which is of course not a very good idea from a security point of view, but will function just fine. Moreover, we will be using aliased IP addresses so once you\'ve decided to move the FreeRADIUS daemon to another computer on the LAN it will be fairly easy and won\'t take too much configuration into it. Next, we need to setup the IP pool that L2TPns will provide to each VPN client. The configuration file is located at `/etc/l2tpns/ip_pool` and should look like the following: 172.16.21.0/24 ::: {.important} Of course you can change this pool to anything else (IANA IPs assigned for private internets only) just make sure it is not conflicting with your current LAN network addresses. This means that if you\'ve assigned addresses of 192.168.0.1 and 192.168.0.2 to your LAN boxes you can\'t have a pool of 192.168.0.1/24 defined since L2TPns will try to route those addresses from the tun device, which is needless to say a bad idea\... ::: Next up, creating the access-list for L2TPns. Add a username and password into `/etc/l2tpns/users`: admin:12345 The password may either be plain-text as above, or encrypted with MD5 or DES (to distinguish DES from plain-text passwords, prefix the value with `{crypt}`). L2TPns utilizes a terminal connection on port 23 which you would feel very comfortable in if you have worked with routers and switches devices before. The terminal provides control over the ppp termination which is why we\'ve created an account to log on to. ### IPsec {#deployment-installation-ipsec} #### Installation {#deployment-installation-ipsec-install} User-space IPsec tools for various IPsec implementations exist for linux, among them is the port of KAME\'s libipsec, setkey, and racoon. Others are the OpenSWAN (a successor to the FreeSWAN project). Getting IPsec installed is fairly easy with Debian: # apt-get install openswan The OpenSWAN project provides packages for RPM-based distributions. Alternately, you may download the [source](http://www.openswan.org/code/) from the OpenSWAN project: # tar xvzf openswan-2.4.4.tar.gz # cd openswan-2.4.4 # ./configure && make && make install #### Configuration {#deployment-installation-ipsec-config} OpenSWAN acts as the IKE daemon (remember IKE? it\'s job is to authenticate between the two peers and negotiate a secure medium). We will be setting up the IKE daemon as a RoadWarrior configuration, a term for remote access VPNs. We desire this approach for compatibilty because after our VPN solution will be complete any user from a Windows machine will be easily ready to connect without any 3rd party applications, same for Linux. Configuration files are placed in `/etc/ipsec.d/`, `/etc/ipsec.conf` and `/etc/ipsec.secrets`. Let\'s start by choosing the remote client and it\'s PSK (Private Shared Key) `/etc/ipsec.secrets`: hostname_or_ipaddress %any : PSK "mysecretkeyisverylong" This is an IP/key pair. The IP or FQDN defines the local peer (like a SOHO branch), then the remote host. Here we defined %any for all hosts, though it\'s possible to define only a specific IP. At last, we define the key associated with it. A better way to create a key is to utilize /dev/random for creating a unique key. # dd if=/dev/random count=16 bs=1 2>/dev/null | xxd -ps Next, let\'s prepare the configuration file `/etc/ipsec.conf`: version 2.0 config setup nat_traversal=yes conn l2tp authby=secret pfs=no keyingtries=3 left=real_ip_address leftnexthop=%defaultroute leftprotoport=17/%any right=%any rightprotoport=17/%any auto=add include /etc/ipsec.d/examples/no_oe.conf In this file we have first defined version 2 which is a must, then enabled NAT Traversal. To understand the importance of this feature think of the following scenario: A remote user attempts to connect while he\'s behind a router and therefore NATed. The router has to de-encapsulate the packet, change things and then build it up again and send it. IPsec doesn\'t like other people messing with it\'s packet. That\'s why we solve this issue with NAT Traversal. Next up we configure authentication type (certificates, psk, rsa keys, etc) then the left and right peers. The default mode OpenSWAN takes is tunnel unless told otherwise. I won\'t go into in-depth explanation of every method, you can take a quick look at `/etc/ipsec.d/examples` for more explanation and other variations of working with RSA keys, Certificates, host-to-host, and more. In summary: - We\'ve configured an almost complete IPsec VPN setup. - We\'ve installed and configured a VPN server (L2TPns) and our IPsec security suite. - To control both of them we use: `/etc/init.d/l2tpns` and `/etc/init.d/racoon` (location of start-up scripts may vary on non-Debian systems, or if you\'ve installed from source). ### FreeRADIUS {#deployment-installation-freeradius} The VPN setup needs to authenticate against something, that is the users database which we chose to be a FreeRADIUS server backed with a MySQL database. #### Installation {#deployment-installation-freeradius-install} > FreeRADIUS is the premiere open source RADIUS server. While detailed > statistics are not available, we believe that FreeRADIUS is well > within the top 5 RADIUS servers world-wide, in terms of the number of > people who use it daily for authentication. It scales from embedded > systems with small amounts of memory, to systems with millions of > users. It is fast, flexible, configurable, and supports more > authentication protocols than many commercial servers. Installing on Debian: # apt-get install freeradius freeradius-mysql From source: Download the latest freeradius package from [freeradius.org](http://freeradius.org/getting.html) # tar xvzf freeradius.tar.gz # cd freeradius # ./configure && make && make install #### Configuration {#deployment-installation-freeradius-config} This will appear a bit complex but it isn\'t, it\'s just a lot of configuration. Following are the configurations you need to have in your `/etc/freeradius/` files. In this section I will not give you a dump of the configuration since they are very long and mostly default. I\'ll just post which changes to make. We haven\'t yet configured MySQL, but it\'ll come afterwards, don\'t worry. Make the following changes to the file `/etc/freeradius/sql.conf`: server = "192.168.0.1" login = "radius" password = "12345678" Add the following to the file `/etc/freeradius/clients.conf`: client 192.168.0.1 { secret = my_secret shortname = localhost nastype = other } Don\'t confuse the secret directive there with IPsec. RADIUS server are using secret keys also to identify their allowed NAS (Network Access Servers), these are the clients that talk to the RADIUS server. Also, change the `client 127.0.0.1 {}` directive to hold the secret \"my\_secret\" like we configured for 192.168.0.1 to avoid conflicts. Uncomment the `sql` directive in the `authorize`, `accounting`, and `session` sections of `/etc/freeradius/radiusd.conf`. Now for populating FreeRADIUS with MySQL. If you don\'t know or haven\'t set root password for MySQL you can do it now with: # mysqladmin -u root password password_here Then add the following to `/root/.my.cnf`: [mysqladmin] user = root password = password_here Create the `radius` database, using the schema given in `/usr/share/doc/freeradius/examples/db_mysql.sql.gz `. ::: {.note} It may be necessary to modify the column definition of `id` in the `nas` table, removing `DEFAULT '0'` such that the definition reads: id int(10) NOT NULL auto_increment, ::: # mysqladmin create radius # mysql radius mysql> source db_mysql.sql mysql> GRANT ALL ON * TO 'radius'@'localhost' IDENTIFIED BY 'radius_password'; All the configuration is now done. Let\'s add a user to our VPN database. # mysql radius mysql> INSERT INTO radcheck values (0, "test", "User-Password", "==", "1234"); We have now created a user in the database of username `test` and password `1234`. Testing the RADIUS setup is simple using the radtest utility provided with it. # radtest Usage: radtest user passwd radius-server[:port] nas-port-number secret [ppphint] [nasname] # radtest test 1234 192.168.0.1 1812 my_secret radtest sends an Access-Request to the RADIUS server and expects an Access-Accept back from it. If you\'re not getting an Access-Accept from the RADIUS you\'re advised to check the configuration again and see what you might have missed. ### Firewall Configuration {#deployment-installation-firewall} We need to apply a few things to iptables configuration and kernel networking. First off, we need to accept VPN-specific packets through the firewall. Of course you will have to adjust the rules to fits you needs, in this case, ppp0 is the Internet interface. # iptables --append INPUT --in-interface ppp0 -p udp --dport 1701 -j ACCEPT # iptables --append INPUT --in-interface ppp0 -p udp --dport 500 -j ACCEPT # iptables --append INPUT --in-interface ppp0 -p udp --dport 4500 -j ACCEPT # iptables --append INPUT --in-interface ppp0 -p 50 -j ACCEPT If you haven\'t setup your Linux box as a gateway yet then you have to allow forwarding/masqing for the boxes on the LAN (and therefore for the VPN clients): # iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE # iptables --append FORWARD --in-interface eth0 -j ACCEPT # echo 1 > /proc/sys/net/ipv4/ip_forward References ========== VPN Reference : [](http://www.jacco2.dds.nl/networking/freeswan-l2tp.html) L2TPns Project : [](http://l2tpns.sourceforge.net) OpenSWAN Project : [](http://www.openswan.org) l2tpns-2.3.3/docs/src/man/000077500000000000000000000000001400724550600152365ustar00rootroot00000000000000l2tpns-2.3.3/docs/src/man/l2tpns.8.md000066400000000000000000000063441400724550600171570ustar00rootroot00000000000000% L2TPNS(8) Layer 2 tunneling protocol network server (LNS) % This manual page was written by Jonathan McDowell and Fernando Alves (), for the Debian GNU/Linux system (but may be used by others) % January 31, 2021 # NAME l2tpns - Layer 2 tunneling protocol network server (LNS) # SYNOPSIS **l2tpns** \[-**d**\] \[-**v**\] \[-**c** _file_\] \[-**h** _hostname_\] # DESCRIPTION **l2tpns** is a daemon for terminating layer 2 tunneling protocol (L2TP: RFC2661) sessions. **l2tpns** is a complete L2TP implementation. It supports the LAC, LNS, PPPOE and DHCPv6 server. Once running, **l2tpns** may be controlled by telnetting to port 23 on the machine running the daemon and with the **nsctl** utility. # OPTIONS **-d** Detach from terminal and fork into the background. By default l2tpns will stay in the foreground **-v** Increase verbosity for debugging. Can be used multiple times **-c** _file_ Specify configuration file **-h** _hostname_ Force hostname to _hostname_ # FILES _/etc/l2tpns/startup-config_ The default configuration file _/etc/l2tpns/ip\_pool_ IP address pool configuration _/etc/l2tpns/users_ Username/password configuration for access to admin interface # SIGNALS **SIGHUP** Reload the config from disk and re-open log file **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 **SIGQUIT** Shut down tunnels and sessions, exit process when complete # MANAGED RADIUS ATTRIBUTE **Ascend-Client-Primary-DNS**, **Ascend-Client-Secondary-DNS** Specifies a primary and secondary DNS server address to send to user **Delegated-IPv6-Prefix** Assign a network address IPv6 prefix to a user by DHCPv6 **Framed-IP-Address** The address to be configured for the user (IPv4 address of the interface ppp) **Framed-Route** provides routing information to be configured for the user **Framed-IPv6-Route** Has the same action as **Delegated-IPv6-Prefix**. **Delegated-IPv6-Prefix** is the correct one to use **Framed-IPv6-Address** IPv6 address to be assigned to the user by DHCPv6 (IPv6 address of the interface ppp) **Idle-Timeout** disconnects the session if no data for more than **Idle-Timeout** (in seconds) **Session-Timeout** disconnects the user session when the time **Session-Timeout** is reached (in seconds) **Tunnel-Type**, **Tunnel-Medium-Type**, **Tunnel-Server-Endpoint**, **Tunnel-Password**, **Tunnel-Assignment-Id** attributes returned by the Radius of the remote LNS server (LAC functionality) example, Radius that return the information of 2 remote LNS server with which must be open a L2TP TUNNEL: - Tunnel-Type: 1 = L2TP - Tunnel-Medium-Type: 1 = IPv4 - Tunnel-Password: 1 = "TheSecretL2TP" - Tunnel-Server-Endpoint: 1 = "88.xx.xx.x1" - Tunnel-Assignment-Id: 1 = "friendisp_lns1" - Tunnel-Type: 2 = L2TP - Tunnel-Medium-Type: 2 = IPv4 - Tunnel-Password: 2 = "TheSecretL2TP" - Tunnel-Server-Endpoint: 2 = "88.xx.xx.x2" - Tunnel-Assignment-Id: 2 = "friendisp_lns2" # SEE ALSO **startup-config**(5), **nsctl**(8) l2tpns-2.3.3/docs/src/man/nsctl.8.md000066400000000000000000000026541400724550600170600ustar00rootroot00000000000000% NSCTL(8) Manage running l2tpns instance % This manual page was written by Jonathan McDowell , for the Debian GNU/Linux system (but may be used by others) % January 31, 2021 NAME ==== nsctl - manage running l2tpns instance SYNOPSIS ======== **nsctl** \[**-d**\] \[**-h** *host*\[:*port*\]\] \[**-t** *timeout*\] *command* \[*arg* \...\] DESCRIPTION =========== **nsctl** sends commands to a running **l2tpns** process. It provides both for the loading or unloading of plugins and also the management of sessions via functions provided by those plugins. OPTIONS ======= **-d** Enable debugging output **-h *host*\[:*port*\]** The host running **l2tpns** that should receive the message. By default the message is sent to UDP port 1702 on **localhost** **-t *timeout*** Timeout in seconds to wait for a response from the server COMMANDS ======== The first argument specifies the command to send to **l2tpns.** The following commands are as defined: **load\_plugin ***plugin* : Load the named *plugin* **unload\_plugin ***plugin* : Unload the named *plugin* **help** : Each loaded plugin is queried for what commands it supports and the synopsis for each is output Any other value of *command* (and *args* if any) are sent to **l2tpns** as-is, to be passed to each plugin which registers a **plugin\_control** function in turn (in which it may be acted upon). SEE ALSO ======== **l2tpns**(8) l2tpns-2.3.3/docs/src/man/startup-config.5.md000066400000000000000000000370541400724550600207010ustar00rootroot00000000000000% STARTUP-CONFIG(5) Configuration file for l2tpns % l2tpns developers % January 31, 2021 # NAME startup-config - configuration file for l2tpns # SYNOPSIS /etc/l2tpns/startup-config # DESCRIPTION **startup-config** is the configuration file for **l2tpns** The format is plain text, in the same format as accepted by the configuration mode of l2tpns's telnet administrative interface. Comments are indicated by either the character # or !. ## SETTINGS Settings are specified with **set** `variable` `value` A list of the possible configuration directives follows. Each of these should be set by a line like: **set** _configstring_ _"value"_ **set** _ipaddress_ _192.168.1.1_ **set** _boolean_ _true_ The following `variables` may be set: **accounting\_dir** (string) 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. Each file dumped begins with a header, where each line is prefixed by #. Following the header is a single line for every connected user, fields separated by a space. The fields are username, ip, qos, uptxoctets, downrxoctets, origin (optional). The qos field is 1 if a standard user, and 2 if the user is throttled. The origin field is dump if account_all_origin is set to true (origin value: L=LAC data, R=Remote LNS data, P=PPPOE data). **account\_all\_origin** (boolean) If set to true, all origin of the usage is dumped to the accounting file (LAC+Remote LNS+PPPOE)(default false). **allow\_duplicate\_users** (boolean) 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. **auth\_tunnel\_change\_addr\_src** (boolean) This parameter authorize to change the source IP of the tunnels l2tp. This parameter can be used when the remotes BAS/LAC are l2tpns server configured in cluster mode, but that the interface to remote LNS are not clustered (the tunnel can be coming from different source IP) (default: no). **bind\_address** (ip address) It's the listen address of the l2tp udp protocol sent and received to LAC. This address is also assigned to the tun interface if no iftun_address is specified. Packets containing user traffic should be routed via this address if given, otherwise the primary address of the machine. **bind\_multi\_address** (ip address) This parameter permit one to listen several address of the l2tp udp protocol (and set several address to the tun interface). WHEN this parameter is set, It OVERWRITE the parameters "bind_address" and "iftun_address". these can be interesting when you want do load-balancing in cluster mode of the uploaded from the LAC. For example you can set a bgp.prepend(MY_AS) for Address1 on LNS1 and a bgp.prepend(MY_AS) for Address2 on LNS2 (see BGP AS-path prepending). example of use with 2 address: set bind_multi_address "64.14.13.41, 64.14.13.42" **cluster\_address** (ip address) Multicast cluster address (default: 239.192.13.13). See the section on Clustering for more information. **cluster\_port** (int) UDP cluster port (default: 32792). See the section on Clustering for more information. **cluster\_interface** (string) Interface for cluster packets (default: eth0). **cluster\_mcast\_ttl** (int) TTL for multicast packets (default: 1). **cluster\_hb\_interval** (int) Interval in tenths of a second between cluster heartbeat/pings. **cluster\_hb\_timeout** (int) 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. **cluster\_master\_min\_adv** (int) Determines the minimum number of up to date slaves required before the master will drop routes (default: 1). **debug** (int) 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. A rough description of the levels is: - 0. Critical Errors - Things are probably broken - 1. Errors - Things might have gone wrong, but probably will recover - 2. Warnings - Just in case you care what is not quite perfect - 3. Information - Parameters of control packets - 4. Calls - For tracing the execution of the code - 5. Packets - Everything, including a hex dump of all packets processed... probably twice Note that the higher you set the debugging level, the slower the program will run. Also, at level 5 a LOT of information will be logged. This should only ever be used for working out why it doesn't work at all. **dump\_speed** (boolean) 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 uptime command on the CLI. **disable\_sending\_hello** (boolean) Disable l2tp sending HELLO message for Apple compatibility. Some OS X implementation of l2tp no manage the L2TP "HELLO message". (default: no). **echo\_timeout** (int) Time between last packet sent and LCP ECHO generation (default: 10 (seconds)). **guest\_account** Allow multiple logins matching this specific username. **icmp\_rate** (int) Maximum number of host unreachable ICMP packets to send per second. **idle\_echo\_timeout** (int) Drop sessions who have not responded within idle_echo_timeout seconds (default: 240 (seconds)) **iftun\_address** (ip address) This parameter is used when you want a tun interface address different from the address of "bind_address" (For use in cases of specific configuration). If no address is given to iftun_address and bind_address, 1.1.1.1 is used. **l2tp\_mtu** (int) MTU of interface for L2TP traffic (default: 1500). Used to set link MRU and adjust TCP MSS. **l2tp\_secret** (string) The secret used by 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. **lock\_pages** (boolean) Keep all pages mapped by the l2tpns process in memory. **log\_file** (string) This will be where all logging and debugging information is written to.This may be either a filename, such as /var/log/l2tpns, or the string syslog:facility, where facility is any one of the syslog logging facilities, such as local5. **multi\_read\_count** (int) 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. **packet\_limit** (int> 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). **peer\_address** (ip address) Address to send to clients as the default gateway. **pid\_file** (string) If set, the process id will be written to the specified file. The value must be an absolute path. **ppp\_keepalive** (boolean) Change this value to no to force generation of LCP ECHO every echo\_timeout seconds, even there are activity on the link (default: yes) **ppp\_restart\_time** (int) **ppp\_max\_configure** (int) **ppp\_max\_failure** (int) PPP counter and timer values, as described in Section 4.1 of RFC1661. _ppp\_restart\_time_, Restart timer for PPP protocol negotiation in seconds (default: 3). _ppp\_max\_configure_, Number of configure requests to send before giving up (default: 10). _ppp\_max\_failure_, Number of Configure-Nak requests to send before sending a Configure-Reject (default: 5). **primary\_dns** (ip address), **secondary\_dns** (ip address) 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. **primary\_radius** (ip address), **secondary\_radius** (ip address) 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. Note: in addition to the source IP address and identifier, the RADIUS server must include the source port when detecting duplicates to suppress (in order to cope with a large number of sessions coming on-line simultaneously l2tpns uses a set of udp sockets, each with a separate identifier). **primary\_radius\_port** (short), **secondary\_radius\_port** (short) Sets the authentication ports for the primary and secondary RADIUS servers. The accounting port is one more than the authentication port. If no RADIUS ports are given, the authentication port defaults to 1645, and the accounting port to 1646. **radius\_accounting** (boolean) If set to true, then RADIUS accounting packets will be sent. This means that a **Start** record will be sent when the session is successfully authenticated, and a **Stop** record will be sent when the session is closed. **radius\_interim** (int) If radius\_accounting is on, defines the interval between sending of RADIUS interim accounting records (in seconds). **radius\_secret** (string) This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail. **radius\_authtypes** (string) A comma separated list of supported RADIUS authentication methods ("pap" or "chap"), in order of preference (default "pap"). **radius\_dae\_port** (short) Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) requests (default: 3799). **radius\_bind\_min**, **radius\_bind\_max** (int) Define a port range in which to bind sockets used to send and receive RADIUS packets. Must be at least RADIUS\_FDS (64) wide. Simplifies firewalling of RADIUS ports (default: dynamically assigned). **random\_device** (string) Path to random data source (default /dev/urandom). Use "" to use the rand() library function. **scheduler\_fifo** (boolean) Sets the scheduling policy for the l2tpns process to SCHED\_FIFO. This causes the kernel to immediately preempt any currently running SCHED\_OTHER (normal) process in favour of l2tpns when it becomes runnable. Ignored on uniprocessor systems. **send\_garp** (boolean) Determines whether or not to send a gratuitous ARP for the bind\_address when the server is ready to handle traffic (default: true). This value is ignored if BGP is configured. **tundevicename** (string) Name of the tun interface (default: "tun0"). **throttle\_speed** (int) Sets the default speed (in kbits/s) which sessions will be limited to. If this is set to 0, then throttling will not be used at all. Note: You can set this by the CLI, but changes will not affect currently connected users. **throttle\_buckets** (int) Number of token buckets to allocate for throttling. Each throttled session requires two buckets (in and out). ## DHCPv6 And IPv6 SETTINGS **dhcp6\_preferred\_lifetime** (int) The preferred lifetime for the IPv6 address and the IPv6 prefix address, expressed in units of seconds (see rfc3315). **dhcp6\_valid\_lifetime** (int) The valid lifetime for the IPv6 address and the IPv6 prefix address, expressed in units of seconds (see rfc3315). **dhcp6\_server\_duid** (int) DUID Based on Link-layer Address (DUID-LL) (see rfc3315). **primary\_ipv6\_dns**, **secondary\_ipv6\_dns** (Ipv6 address) IPv6 DNS servers will be sent to the user (see rfc3646). **default\_ipv6\_domain\_list** (string) The Domain Search List (ex: "fdn.fr") (see rfc3646). **ipv6\_prefix** (Ipv6 address) 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 0. ## LAC SETTINGS **bind\_address\_remotelns** (ip address) Address of the interface to listen the remote LNS tunnels. If no address is given, all interfaces are listened (Any Address). **bind\_portremotelns** (short) Port to bind for the Remote LNS (default: 65432). A static REMOTES LNS configuration can be entered by the command: **setforward** _MASK_ _IP_ _PORT_ _SECRET_ where MASK specifies the mask of users who have forwarded to remote LNS (ex: "/friendISP@company.com"). where IP specifies the IP of the remote LNS (ex: "66.66.66.55"). where PORT specifies the L2TP Port of the remote LNS (Normally should be 1701) (ex: 1701). where SECRET specifies the secret password the remote LNS (ex: mysecret). The static REMOTE LNS configuration can be used when the friend ISP not have a proxied Radius. If a proxied Radius is used, It will return the RADIUS attributes: Tunnel-Type:1 = L2TP Tunnel-Medium-Type:1 = IPv4 Tunnel-Password:1 = "LESECRETL2TP" Tunnel-Server-Endpoint:1 = "88.xx.xx.x1" Tunnel-Assignment-Id:1 = "friendisp\_lns1" Tunnel-Type:2 += L2TP Tunnel-Medium-Type:2 += IPv4 Tunnel-Password:2 += "LESECRETL2TP" Tunnel-Server-Endpoint:2 += "88.xx.xx.x2" Tunnel-Assignment-Id:2 += "friendisp\_lns2" ## PPPOE SETTINGS **pppoe\_if\_to\_bind** (string) PPPOE server interface to bind (ex: "eth0.12"), If not specified the server PPPOE is not enabled. For the pppoe clustering, all the interfaces PPPOE of the clusters must use the same HW address (MAC address). **pppoe\_service\_name** (string) PPPOE service name (default: NULL). **pppoe\_ac\_name** (string) PPPOE access concentrator name (default: "l2tpns-pppoe"). **pppoe\_only\_equal\_svc\_name** (boolean) If set to yes, the PPPOE server only accepts clients with a "service-name" different from NULL and a "service-name" equal to server "service-name" (default: no). ## BGP ROUTING The routing configuration section is entered by the command **router** **bgp** _as_ where _as_ specifies the local AS number. Subsequent lines prefixed with **neighbour** _peer_ define the attributes of BGP neighhbours. Valid commands are: **neighbour** _peer_ **remote-as** _as_ **neighbour** _peer_ **timers** _keepalive_ _hold_ Where _peer_ specifies the BGP neighbour as either a hostname or IP address, _as_ is the remote AS number and _keepalive_, _hold_ are the timer values in seconds. ## NAMED ACCESS LISTS Named access lists may be defined with either of **ip** **access-list** **standard** _name_ **ip** **access-list** **extended** _name_ Subsequent lines starting with permit or deny define the body of the access-list. ### Standard Access Lists Standard access lists are defined with: {**permit**|**deny**} _source_ \[_dest_\] Where _source_ and _dest_ specify IP matches using one of: _address_ _wildard_ **host** _address_ **any** _address_ and _wildard_ are in dotted-quad notation, bits in the _wildard_ indicate which address bits in _address_ are relevant to the match (0 = exact match; 1 = don't care). The shorthand 'host address' is equivalent to '_address_ **0.0.0.0**'; '**any**' to '**0.0.0.0** **255.255.255.255**'. ### Extended Access Lists Extended access lists are defined with: {**permit**|**deny**} _proto_ _source_ \[_ports_\] _dest_ \[_ports_\] \[_flags_\] Where _proto_ is one of **ip**, **tcp** or **udp**, and _source_ and _dest_ are as described above for standard lists. For TCP and UDP matches, source and destination may be optionally followed by a ports specification: {**eq|neq|gt|lt**} _port_ **range** _from_ _to_ _flags_ may be one of: {**match-any|match-all**} {**+|-**}{**fin|syn|rst|psh|ack|urg**} ... Match packets with any or all of the tcp flags set (+) or clear (-). **established** Match "established" TCP connections: packets with RST or ACK set, and SYN clear. **fragments** Match IP fragments. May not be specified on rules with layer 4 matches. # SEE ALSO l2tpns(8), nsctl(8) l2tpns-2.3.3/etc/000077500000000000000000000000001400724550600135175ustar00rootroot00000000000000l2tpns-2.3.3/etc/ip_pool.default000066400000000000000000000000341400724550600165230ustar00rootroot0000000000000010.10.10.0/24 10.13.10.0/24 l2tpns-2.3.3/etc/l2tpns.logrotate000066400000000000000000000001641400724550600166640ustar00rootroot00000000000000/var/log/l2tpns { daily missingok rotate 14 compress postrotate /usr/bin/killall -HUP l2tpns endscript } l2tpns-2.3.3/etc/startup-config.default000066400000000000000000000065461400724550600200450ustar00rootroot00000000000000# 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" # This host name, if different from the OS one #set hostname "localhost" # 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 # Kill timedout sessions ? (default yes) #set kill_timedout_sessions no # Allow multiple logins for specific username #set guest_account "" # 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 # Listen address for CLI #set cli_bind_address 127.0.0.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 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_port 32792 #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 # IPv6 address prefix #set ipv6_prefix :: # BGP NEXT_HOP path attribute #set nexthop 10.0.1.1 #set nexthop6 2001:db8::1 # Time between last packet sent and LCP ECHO generation (default 10 seconds) #set echo_timeout 10 # Drop sessions who have not responded within idle_echo_timeout seconds (default 240 seconds) #set idle_echo_timeout 240 # Change this value to no to force generation of LCP ECHO every echo_timeout seconds, even there are activity on the link (default yes) set ppp_keepalive yes # 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" l2tpns-2.3.3/etc/users.default000066400000000000000000000000711400724550600162240ustar00rootroot00000000000000# List username:password combinations here for cli users l2tpns-2.3.3/fake_epoll.h000066400000000000000000000065331400724550600152250ustar00rootroot00000000000000/* kludge up some limited epoll semantics using select for 2.4 kernels */ /* $Id: fake_epoll.h,v 1.2 2007-06-28 07:22:50 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 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 < 0) return n; 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__ */ l2tpns-2.3.3/garden.c000066400000000000000000000162411400724550600143540ustar00rootroot00000000000000#include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" #include "control.h" /* walled garden */ 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]); if (-1 == system(up_commands[i])) f->log(0, 0, 0, "error command %s\n", 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; int status; 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); status = 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) { 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]); if (-1 == system(down_commands[i])) f->log(0, 0, 0, "error command %s\n", 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]); if (-1 == system(down_commands[i])) f->log(0, 0, 0, "error command %s\n", down_commands[i]); } } l2tpns-2.3.3/icmp.c000066400000000000000000000134711400724550600140460ustar00rootroot00000000000000// L2TPNS: icmp #include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "ipv6_u.h" static uint16_t _checksum(uint8_t *addr, int count); 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(*(uint16_t *) addr); addr += 2; } if (count > 0) 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); } struct nd_opt_rdnss_info_l2tpns { uint8_t nd_opt_rdnssi_type; uint8_t nd_opt_rdnssi_len; uint16_t nd_opt_rdnssi_pref_flag_reserved; uint32_t nd_opt_rdnssi_lifetime; struct in6_addr nd_opt_rdnssi[0]; }; void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip) { struct nd_opt_prefix_info *pinfo; struct ip6_hdr *p_ip6_hdr; struct nd_router_advert *p_nra; struct nd_opt_rdnss_info_l2tpns *p_rdnss; uint8_t b[MAXETHER + 20]; struct ipv6_pseudo_hdr pseudo_hdr; int l; LOG(3, s, t, "Sending IPv6 RA\n"); memset(b, 0, sizeof(b)); p_ip6_hdr = (struct ip6_hdr *) makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0); if (!p_ip6_hdr) { LOG(3, s, t, "failed to send IPv6 RA\n"); return; } p_ip6_hdr->ip6_vfc = 0x60; // IPv6 p_ip6_hdr->ip6_plen = 0; // Length of payload (not header) (calculation below) p_ip6_hdr->ip6_nxt = IPPROTO_ICMPV6; // icmp6 is next p_ip6_hdr->ip6_hlim = 255; // Hop limit // IPv6 0xFE80::1 inet_pton(AF_INET6, "FE80::1", &p_ip6_hdr->ip6_src.s6_addr); if (ip != NULL) { memcpy(p_ip6_hdr->ip6_dst.s6_addr, ip, 16); // dest = ip } else { // FF02::1 - all hosts inet_pton(AF_INET6, "FF02::1", &p_ip6_hdr->ip6_dst.s6_addr); } // RA message after Ipv6 header p_nra = (struct nd_router_advert *) &p_ip6_hdr[1]; p_nra->nd_ra_type = ND_ROUTER_ADVERT; // RA message (134) p_nra->nd_ra_code = 0; // Code p_nra->nd_ra_cksum = 0; // Checksum p_nra->nd_ra_curhoplimit = 64; // Hop count p_nra->nd_ra_flags_reserved = (ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER); // Flags p_nra->nd_ra_router_lifetime = 0xFFFF; // Lifetime p_nra->nd_ra_reachable = 0; // Reachable time p_nra->nd_ra_retransmit = 0; // Retrans timer // Option PI after RA message (rfc4861) pinfo = (struct nd_opt_prefix_info *) &p_nra[1]; pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; pinfo->nd_opt_pi_len = 4; pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK | 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_len = 64; // prefix length if (session[s].ipv6address.s6_addr[0]) { // MSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address) memcpy(&pinfo->nd_opt_pi_prefix, &session[s].ipv6address.s6_addr[0], 8); } else pinfo->nd_opt_pi_prefix = config->ipv6_prefix; // Length of payload (not header) l = sizeof(*pinfo) + sizeof(*p_nra); if (config->default_ipv6_dns1.s6_addr32[0]) { struct in6_addr *ptr_in6_addr; p_rdnss = (struct nd_opt_rdnss_info_l2tpns *) &pinfo[1]; p_rdnss->nd_opt_rdnssi_type = 25; //RDNSS OPTION INFORMATION; p_rdnss->nd_opt_rdnssi_len = 3; // 1 + 2 * nb DNS p_rdnss->nd_opt_rdnssi_lifetime = htonl(config->dns6_lifetime); ptr_in6_addr = &p_rdnss->nd_opt_rdnssi[0]; memcpy(ptr_in6_addr, &config->default_ipv6_dns1, sizeof(*ptr_in6_addr)); l += sizeof(*p_rdnss) + sizeof(*ptr_in6_addr); if (config->default_ipv6_dns2.s6_addr32[0]) { ptr_in6_addr = &p_rdnss->nd_opt_rdnssi[1]; memcpy(ptr_in6_addr, &config->default_ipv6_dns2, sizeof(*ptr_in6_addr)); p_rdnss->nd_opt_rdnssi_len += 2; // 1 + 2 * nb DNS l += sizeof(*ptr_in6_addr); } } // Length of payload (not header) p_ip6_hdr->ip6_plen = htons(l); /* Use pseudo hearder for checksum calculation */ memset(&pseudo_hdr, 0, sizeof(pseudo_hdr)); memcpy(&pseudo_hdr.src, &p_ip6_hdr->ip6_src, 16); memcpy(&pseudo_hdr.dest, &p_ip6_hdr->ip6_dst, 16); pseudo_hdr.ulp_length = htonl(l); // Lenght whitout Ipv6 header pseudo_hdr.nexthdr = IPPROTO_ICMPV6; // Checksum is over the icmp6 payload plus the pseudo header p_nra->nd_ra_cksum = ipv6_checksum(&pseudo_hdr, (uint8_t *) p_nra, l); // Length + hearder length l += sizeof(*p_ip6_hdr); tunnelsend(b, l + (((uint8_t *) p_ip6_hdr)-b), t); // send it... return; } l2tpns-2.3.3/ipv6_u.c000066400000000000000000000015151400724550600143220ustar00rootroot00000000000000/* * Fernando ALVES 2014 * GPL licenced */ #include #include "ipv6_u.h" uint16_t ipv6_checksum(struct ipv6_pseudo_hdr *p_pshdr, uint8_t *buff, int lenbuff) { uint32_t sum = 0; uint16_t *ptrw = (uint16_t *) p_pshdr; uint16_t word16; int i; // Size pseudo header 40 byte (20 word) for (i = 0; i < (sizeof(*p_pshdr)/2); i++) { word16 = ntohs(*((uint16_t *)ptrw)); sum += word16; ++ptrw; } ptrw = (uint16_t *) buff; while (lenbuff > 1) { word16 = ntohs(*((uint16_t *) ptrw)); sum += word16; ++ptrw; lenbuff -= 2; } if (lenbuff > 0) { word16 = ntohs(*((uint8_t *) ptrw)); sum += word16; } // 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); } l2tpns-2.3.3/ipv6_u.h000066400000000000000000000005171400724550600143300ustar00rootroot00000000000000 #ifndef __IPV6_U_H__ #define __IPV6_U_H__ struct ipv6_pseudo_hdr { struct in6_addr src; struct in6_addr dest; uint32_t ulp_length; uint32_t zero : 24; uint32_t nexthdr : 8; } __attribute__((packed)); // dhcp6.c uint16_t ipv6_checksum(struct ipv6_pseudo_hdr *p_pshdr, uint8_t *buff, int lenbuff); #endif /* __IPV6_U_H__ */ l2tpns-2.3.3/l2tplac.c000066400000000000000000000467561400724550600144730ustar00rootroot00000000000000/* * Fernando ALVES 2013 * Add functionality "LAC" to l2tpns. * Used to forward a ppp session to another "LNS". * GPL licenced */ #include #include #include #include "md5.h" #include "dhcp6.h" #include "l2tpns.h" #include "util.h" #include "cluster.h" #include "l2tplac.h" #include "pppoe.h" /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2 * * LCP Negotiation * Client <-------------------> LAC * Challenge (CHAP/PAP) * Client <-------------------> LAC * SCCRQ * LAC --------------------> LNS1 (Tunnel Open) * SCCRP * LAC <-------------------- LNS1 (Tunnel Open) * SCCCN * LAC --------------------> LNS1 (Tunnel Open) * ZLB * LAC <-------------------- LNS1 (Tunnel Open) * ICRQ * LAC --------------------> LNS1 (Session Open) * ICRP * LAC <-------------------- LNS1 (Session Open) * ICCN * LAC --------------------> LNS1 (Session Open) * ZLB * LAC <-------------------- LNS1 (Session Open) * LCP Negotiation * Client <---------------------------------------------> LNS1 * Challenge (CHAP/PAP) * Client <---------------------------------------------> LNS1 * SCCRQ * LNS1 --------------------> LNS2 (Tunnel Open) * SCCRP * LNS1 <-------------------- LNS2 (Tunnel Open) * SCCCN * LNS1 --------------------> LNS2 (Tunnel Open) * ZLB * LNS1 <-------------------- LNS2 (Tunnel Open) * ICRQ * LNS1 --------------------> LNS2 (Session Open) * ICRP * LNS1 <-------------------- LNS2 (Session Open) * ICCN * LNS1 --------------------> LNS2 (Session Open) * ZLB * LNS1 <-------------------- LNS2 (Session Open) * LCP Negotiation * Client <------------------------------------------------------------------------> LNS2 * PAP/CHAP Authentification * Client <------------------------------------------------------------------------> LNS2 * DATA (ppp) * Client <------------------------------------------------------------------------> LNS2 * */ typedef struct { uint32_t tunnel_type; uint32_t tunnel_medium_type; in_addr_t tunnel_server_endpoint; /* IP remote LNS */ char tunnel_password[64]; /* l2tpsecret remote LNS */ char tunnel_assignment_id[256]; } tunnelrlnst; // Max Radius Tunnels by remote LNS #define MAXTAGTUNNEL 0x20 static tunnelrlnst ptunnelrlns[MAXTAGTUNNEL]; /* * Possible configrlns states * CONFRLNSFREE -> CONFRLNSSET -> CONFRLNSFREE */ enum { CONFRLNSFREE = 0, // Not in use CONFRLNSSET, // Config Set CONFRLNSSETBYRADIUS // Config Set }; // struct remote lns typedef struct { int state; // conf state (tunnelstate enum) in_addr_t ip; // Ip for far end uint16_t port; // port for far end hasht auth; // request authenticator char strmaskuser[MAXUSER]; char l2tp_secret[64]; // L2TP shared secret char tunnel_assignment_id[256]; } configrlns; configrlns *pconfigrlns = NULL; // Init data structures void lac_initremotelnsdata() { confrlnsidt i; if ( !(pconfigrlns = shared_malloc(sizeof(pconfigrlns[0]) * MAXRLNSTUNNEL)) ) { LOG(0, 0, 0, "Error doing malloc for tunnels lac: %s\n", strerror(errno)); exit(1); } memset(pconfigrlns, 0, sizeof(pconfigrlns[0]) * MAXRLNSTUNNEL); // Mark all the conf as free. for (i = 1; i < MAXRLNSTUNNEL; i++) pconfigrlns[i].state = CONFRLNSFREE; // mark it as not filled in. config->highest_rlnsid = 0; lac_reset_rad_tag_tunnel_ctxt(); } // Reset Radius TAG tunnel context void lac_reset_rad_tag_tunnel_ctxt() { memset(ptunnelrlns, 0, sizeof(ptunnelrlns[0]) * MAXTAGTUNNEL); } // Add tunnel_type radius TAG tunnel to context void lac_set_rad_tag_tunnel_type(uint8_t tag, uint32_t tunnel_type) { if (tag < MAXTAGTUNNEL) ptunnelrlns[tag].tunnel_type = tunnel_type; } // Add tunnel_medium_type Radius TAG tunnel to context void lac_set_rad_tag_tunnel_medium_type(uint8_t tag, uint32_t tunnel_medium_type) { if (tag < MAXTAGTUNNEL) ptunnelrlns[tag].tunnel_medium_type = tunnel_medium_type; } // Add tunnel_server_endpoint Radius TAG tunnel to context void lac_set_rad_tag_tunnel_serv_endpt(uint8_t tag, char *tunnel_server_endpoint) { if (tag < MAXTAGTUNNEL) { ptunnelrlns[tag].tunnel_server_endpoint = ntohl(inet_addr(tunnel_server_endpoint)); } } // Add tunnel_password Radius TAG tunnel to context void lac_set_rad_tag_tunnel_password(uint8_t tag, char *tunnel_password) { if ((tag < MAXTAGTUNNEL) && (strlen(tunnel_password) < 64)) { strcpy(ptunnelrlns[tag].tunnel_password, tunnel_password); } } // Add tunnel_assignment_id Radius TAG tunnel to context void lac_set_rad_tag_tunnel_assignment_id(uint8_t tag, char *tunnel_assignment_id) { if ((tag < MAXTAGTUNNEL) && (strlen(tunnel_assignment_id) < 256)) { strcpy(ptunnelrlns[tag].tunnel_assignment_id, tunnel_assignment_id); } } // Select a tunnel_assignment_id int lac_rad_select_assignment_id(sessionidt s, char *assignment_id) { int idtag; int nbtagfound = 0; int bufidtag[MAXTAGTUNNEL]; for (idtag = 0; idtag < MAXTAGTUNNEL; ++idtag) { if (ptunnelrlns[idtag].tunnel_type == 0) continue; else if (ptunnelrlns[idtag].tunnel_type != 3) // 3 == L2TP tunnel type LOG(1, s, session[s].tunnel, "Error, Only L2TP tunnel type supported\n"); else if (ptunnelrlns[idtag].tunnel_medium_type != 1) LOG(1, s, session[s].tunnel, "Error, Only IP tunnel medium type supported\n"); else if (ptunnelrlns[idtag].tunnel_server_endpoint == 0) LOG(1, s, session[s].tunnel, "Error, Bad IP tunnel server endpoint \n"); else if (strlen(ptunnelrlns[idtag].tunnel_assignment_id) > 0) { bufidtag[nbtagfound] = idtag; nbtagfound++; } } if (nbtagfound > 0) { // random between 0 and nbtagfound-1 idtag = (rand() % nbtagfound); if (idtag >= nbtagfound) idtag = 0; //Sanity checks. strcpy(assignment_id, ptunnelrlns[bufidtag[idtag]].tunnel_assignment_id); return 1; } // Error no tunnel_assignment_id found return 0; } // Save the 'radius tag tunnels' context on global configuration void lac_save_rad_tag_tunnels(sessionidt s) { confrlnsidt idrlns; int idtag; for (idtag = 0; idtag < MAXTAGTUNNEL; ++idtag) { if (ptunnelrlns[idtag].tunnel_type == 0) continue; else if (ptunnelrlns[idtag].tunnel_type != 3) // 3 == L2TP tunnel type LOG(1, s, session[s].tunnel, "Error, Only L2TP tunnel type supported\n"); else if (ptunnelrlns[idtag].tunnel_medium_type != 1) LOG(1, s, session[s].tunnel, "Error, Only IP tunnel medium type supported\n"); else if (ptunnelrlns[idtag].tunnel_server_endpoint == 0) LOG(1, s, session[s].tunnel, "Error, Bad IP tunnel server endpoint \n"); else if (strlen(ptunnelrlns[idtag].tunnel_assignment_id) <= 0) LOG(1, s, session[s].tunnel, "Error, No tunnel_assignment_id \n"); else if (ptunnelrlns[idtag].tunnel_server_endpoint == ntohl(config->bind_address)) LOG(0, s, session[s].tunnel, "Error, IP Remote LNS == IP local bind address (%s) !!!\n", fmtaddr(config->bind_address, 0)); else { for (idrlns = 1; idrlns < MAXRLNSTUNNEL; ++idrlns) { if (pconfigrlns[idrlns].state == CONFRLNSFREE) { pconfigrlns[idrlns].ip = ptunnelrlns[idtag].tunnel_server_endpoint; pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP port strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password); strcpy(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id); config->highest_rlnsid = idrlns; pconfigrlns[idrlns].state = CONFRLNSSETBYRADIUS; break; } else if (pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS) { if ( (pconfigrlns[idrlns].ip == ptunnelrlns[idtag].tunnel_server_endpoint) && (strcmp(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id) == 0) ) { // l2tp_secret may be changed strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password); pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP poart if (config->highest_rlnsid < idrlns) config->highest_rlnsid = idrlns; break; } } } if (idrlns >= MAXRLNSTUNNEL) { LOG(0, s, session[s].tunnel, "No more Remote LNS Conf Free\n"); return; } } } } // Create Remote LNS a Tunnel or Session static int lac_create_tunnelsession(tunnelidt t, sessionidt s, confrlnsidt i_conf, char * puser) { if (t == 0) { if (main_quit == QUIT_SHUTDOWN) return 0; // Start Open Tunnel if (!(t = lac_new_tunnel())) { LOG(1, 0, 0, "No more tunnels\n"); STAT(tunnel_overflow); return 0; } lac_tunnelclear(t); tunnel[t].ip = pconfigrlns[i_conf].ip; tunnel[t].port = pconfigrlns[i_conf].port; tunnel[t].window = 4; // default window tunnel[t].isremotelns = i_conf; tunnel[t].indexudp = config->indexlacudpfd; STAT(tunnel_created); random_data(pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth)); LOG(2, 0, t, "Create New tunnel to REMOTE LNS %s for user %s\n", fmtaddr(htonl(tunnel[t].ip), 0), puser); lac_send_SCCRQ(t, pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth)); } else if (tunnel[t].state == TUNNELOPEN) { if (main_quit != QUIT_SHUTDOWN) { /**********************/ /** Open New session **/ /**********************/ sessionidt new_sess = sessionfree; sessionfree = session[new_sess].next; memset(&session[new_sess], 0, sizeof(session[new_sess])); if (new_sess > config->cluster_highest_sessionid) config->cluster_highest_sessionid = new_sess; session[new_sess].opened = time_now; session[new_sess].tunnel = t; session[new_sess].last_packet = session[s].last_data = time_now; session[new_sess].ppp.phase = Establish; session[new_sess].ppp.lcp = Starting; session[s].ppp.phase = Establish; LOG(2, 0, t, "Open New session to REMOTE LNS %s for user: %s\n", fmtaddr(htonl(tunnel[t].ip), 0), puser); // Sent ICRQ Incoming-call-request lac_send_ICRQ(t, new_sess); // Set session to forward to another LNS session[s].forwardtosession = new_sess; session[new_sess].forwardtosession = s; strncpy(session[s].user, puser, sizeof(session[s].user) - 1); strncpy(session[new_sess].user, puser, sizeof(session[new_sess].user) - 1); STAT(session_created); } else { lac_tunnelshutdown(t, "Shutting down", 6, 0, 0); } } else { /** TODO **/ LOG(1, 0, t, "(REMOTE LNS) tunnel is not open\n"); } return 1; } // Check if session must be forwarded to another LNS // return 1 if the session must be forwarded (and Creating a tunnel/session has been started) // else 0. // Note: check from the configuration read on the startup-config (see setforward) int lac_conf_forwardtoremotelns(sessionidt s, char * puser) { tunnelidt t, j; confrlnsidt i; for (i = 1; i <= config->highest_rlnsid ; ++i) { if ( (pconfigrlns[i].state == CONFRLNSSET) && (NULL != strstr(puser, pconfigrlns[i].strmaskuser)) ) { t = 0; for (j = 0; j <= config->cluster_highest_tunnelid ; ++j) { if ((tunnel[j].isremotelns) && (tunnel[j].ip == pconfigrlns[i].ip) && (tunnel[j].port == pconfigrlns[i].port) && (tunnel[j].state != TUNNELDIE)) { t = j; if (tunnel[t].isremotelns != i) { if ( (tunnel[t].state == TUNNELOPEN) || (tunnel[t].state == TUNNELOPENING) ) { LOG(1, 0, t, "Tunnel Remote LNS ID inconsistency (IP RLNS:%s)\n", fmtaddr(htonl(pconfigrlns[i].ip), 0)); tunnel[t].isremotelns = i; } else t = 0; } break; } } return lac_create_tunnelsession(t, s, i, puser); } } return 0; } // return 1 if the session must be forwarded (and Creating a tunnel/session has been started) // else 0. // Note: Started from a radius response int lac_rad_forwardtoremotelns(sessionidt s, char *assignment_id, char * puser) { tunnelidt t, j; confrlnsidt i; for (i = 1; i <= config->highest_rlnsid ; ++i) { if ((pconfigrlns[i].state == CONFRLNSSETBYRADIUS) && (strcmp(pconfigrlns[i].tunnel_assignment_id, assignment_id) == 0)) { t = 0; for (j = 1; j <= config->cluster_highest_tunnelid ; ++j) { if ((tunnel[j].isremotelns == i) && (tunnel[j].ip == pconfigrlns[i].ip) && (tunnel[j].port == pconfigrlns[i].port) && (tunnel[j].state != TUNNELDIE)) { if ( (tunnel[j].state == TUNNELOPEN) || (tunnel[j].state == TUNNELOPENING) ) { t = j; LOG(3, 0, t, "Tunnel Remote LNS already open(ing) (RLNS IP:%s)\n", fmtaddr(htonl(pconfigrlns[i].ip), 0)); break; } } } return lac_create_tunnelsession(t, s, i, puser); } } return 0; } // Calcul the remote LNS auth void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out) { MD5_CTX ctx; confrlnsidt idrlns; idrlns = tunnel[t].isremotelns; MD5_Init(&ctx); MD5_Update(&ctx, &id, 1); MD5_Update(&ctx, pconfigrlns[idrlns].l2tp_secret, strlen(pconfigrlns[idrlns].l2tp_secret)); MD5_Update(&ctx, pconfigrlns[idrlns].auth, 16); MD5_Final(out, &ctx); } // Forward session to LAC or Remote LNS int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto, in_addr_t s_addr, int sin_port, uint16_t indexudpfd) { uint16_t t = 0, s = 0; uint8_t *p = buf + 2; // First word L2TP options s = session[sess].forwardtosession; if (session[s].forwardtosession != sess) { LOG(0, sess, session[sess].tunnel, "Link Session (%u) broken\n", s); return 0; } t = session[s].tunnel; if (t >= MAXTUNNEL) { LOG(1, s, t, "Session with invalid tunnel ID\n"); return 0; } if ((!tunnel[t].isremotelns) && (!tunnel[session[sess].tunnel].isremotelns)) { LOG(0, sess, session[sess].tunnel, "Link Tunnel Session (%u/%u) broken\n", s, t); return 0; } if (!config->cluster_iam_master) { if ( (proto == PPPIPCP) || (proto == PPPLCP) || (proto == PPPPAP) || (proto == PPPCHAP) || (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0]) || (proto == PPPCCP) ) { session[sess].last_packet = time_now; master_forward_packet(buf, len, s_addr, sin_port, indexudpfd); return 1; } } if (t == TUNNEL_ID_PPPOE) { pppoe_forwardto_session_pppoe(buf, len, sess, proto); return 1; } if (*buf & 0x40) { // length p += 2; } *(uint16_t *) p = htons(tunnel[t].far); // tunnel p += 2; *(uint16_t *) p = htons(session[s].far); // session p += 2; if (*buf & 0x08) { // ns/nr *(uint16_t *) p = htons(tunnel[t].ns); // sequence p += 2; *(uint16_t *) p = htons(tunnel[t].nr); // sequence p += 2; } if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])) { session[sess].last_packet = session[sess].last_data = time_now; // Update STAT IN increment_counter(&session[sess].cin, &session[sess].cin_wrap, len); session[sess].cin_delta += len; session[sess].pin++; sess_local[sess].cin += len; sess_local[sess].pin++; session[s].last_data = time_now; // Update STAT OUT increment_counter(&session[s].cout, &session[s].cout_wrap, len); // byte count session[s].cout_delta += len; session[s].pout++; sess_local[s].cout += len; sess_local[s].pout++; } else session[sess].last_packet = time_now; tunnelsend(buf, len, t); // send it... return 1; } // Add new Remote LNS from CLI // return: // 0 = Error // 1 = New Remote LNS conf ADD // 2 = Remote LNS Conf Updated int lac_addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS) { confrlnsidt idrlns; for (idrlns = 1; idrlns < MAXRLNSTUNNEL; ++idrlns) { if (pconfigrlns[idrlns].state == CONFRLNSFREE) { snprintf((char *) pconfigrlns[idrlns].strmaskuser, sizeof(pconfigrlns[idrlns].strmaskuser), "%s", mask); pconfigrlns[idrlns].ip = ntohl(inet_addr(IP_RemoteLNS)); pconfigrlns[idrlns].port = atoi(Port_RemoteLNS); snprintf((char *) pconfigrlns[idrlns].l2tp_secret, sizeof(pconfigrlns[idrlns].l2tp_secret), "%s", SecretRemoteLNS); config->highest_rlnsid = idrlns; pconfigrlns[idrlns].state = CONFRLNSSET; return 1; } else if ((pconfigrlns[idrlns].state == CONFRLNSSET) && (strcmp(pconfigrlns[idrlns].strmaskuser, mask) == 0)) { if ( (pconfigrlns[idrlns].ip != ntohl(inet_addr(IP_RemoteLNS))) || (pconfigrlns[idrlns].port != atoi(Port_RemoteLNS)) || (strcmp(pconfigrlns[idrlns].l2tp_secret, SecretRemoteLNS) != 0) ) { memset(&pconfigrlns[idrlns], 0, sizeof(pconfigrlns[idrlns])); snprintf((char *) pconfigrlns[idrlns].strmaskuser, sizeof(pconfigrlns[idrlns].strmaskuser), "%s", mask); pconfigrlns[idrlns].ip = ntohl(inet_addr(IP_RemoteLNS)); pconfigrlns[idrlns].port = atoi(Port_RemoteLNS); snprintf((char *) pconfigrlns[idrlns].l2tp_secret, sizeof(pconfigrlns[idrlns].l2tp_secret), "%s", SecretRemoteLNS); if (config->highest_rlnsid < idrlns) config->highest_rlnsid = idrlns; pconfigrlns[idrlns].state = CONFRLNSSET; // Conf Updated, the tunnel must be dropped return 2; } return 1; } } LOG(0, 0, 0, "No more Remote LNS Conf Free\n"); return 0; } // Cli Show remote LNS defined int lac_cli_show_remotelns(confrlnsidt idrlns, char *strout) { if (idrlns > config->highest_rlnsid) return 0; if (idrlns == 0) // Show Summary sprintf(strout, "%15s %3s %-32s %-32s %11s %7s %10s", "IP Remote LNS", "TID", "l2tp secret", "assignment Id", "File/Radius", "State", "Count Sess"); else { tunnelidt t, tfound = 0; sessionidt s; int countsess = 0; char state[20]; strcpy(state, "Close"); for (t = 0; t <= config->cluster_highest_tunnelid ; ++t) { if ((tunnel[t].isremotelns == idrlns) && (tunnel[t].ip == pconfigrlns[idrlns].ip) && (tunnel[t].port == pconfigrlns[idrlns].port) && (tunnel[t].state != TUNNELDIE)) { if (tunnel[t].state == TUNNELOPENING) strcpy(state, "Opening"); else if (tunnel[t].state == TUNNELOPEN) strcpy(state, "Open"); for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) countsess++; tfound = t; break; } } sprintf(strout, "%15s %3u %-32s %-32s %11s %7s %10u", fmtaddr(htonl(pconfigrlns[idrlns].ip), 0), tfound, pconfigrlns[idrlns].l2tp_secret, pconfigrlns[idrlns].tunnel_assignment_id, (pconfigrlns[idrlns].state == CONFRLNSSET?"File":(pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS?"Radius":"Free")), state, countsess); } return 1; } l2tpns-2.3.3/l2tplac.h000066400000000000000000000026121400724550600144570ustar00rootroot00000000000000/* L2TPLAC */ /* $Id: l2tplac.h,v 1.0 2012-07-01 14:49:28 fendo Exp $ */ #ifndef __L2TPLAC_H__ #define __L2TPLAC_H__ #define L2TPLACPORT 65432 // L2TP port for Remote LNS // Limits #define MAXRLNSTUNNEL 201 typedef uint16_t confrlnsidt; // l2tplac.c void lac_initremotelnsdata(); int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto, in_addr_t s_addr, int sin_port, uint16_t indexudpfd); int lac_conf_forwardtoremotelns(sessionidt s, char * puser); void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out); int lac_addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS); /* Function for Tunnels creating from radius responses */ void lac_reset_rad_tag_tunnel_ctxt(); void lac_set_rad_tag_tunnel_type(uint8_t tag, uint32_t tunnel_type); void lac_set_rad_tag_tunnel_medium_type(uint8_t tag, uint32_t tunnel_medium_type); void lac_set_rad_tag_tunnel_serv_endpt(uint8_t tag, char *tunnel_server_endpoint); void lac_set_rad_tag_tunnel_password(uint8_t tag, char *tunnel_password); void lac_set_rad_tag_tunnel_assignment_id(uint8_t tag, char *tunnel_assignment_id); void lac_save_rad_tag_tunnels(sessionidt s); int lac_rad_select_assignment_id(sessionidt s, char *assignment_id); int lac_rad_forwardtoremotelns(sessionidt s, char *assignment_id, char * puser); int lac_cli_show_remotelns(confrlnsidt idrlns, char *strout); #endif /* __L2TPLAC_H__ */ l2tpns-2.3.3/l2tpns.c000066400000000000000000005231411400724550600143400ustar00rootroot00000000000000// L2TP Network Server // Adrian Kennard 2002 // Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 #include #include #include #include #include #define SYSLOG_NAMES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "md5.h" #include "dhcp6.h" #include "l2tpns.h" #include "cluster.h" #include "plugin.h" #include "ll.h" #include "constants.h" #include "control.h" #include "util.h" #include "tbf.h" #ifdef BGP #include "bgp.h" #endif #include "l2tplac.h" #include "pppoe.h" #include "dhcp6.h" char * Vendor_name = "Linux L2TPNS"; uint32_t call_serial_number = 0; // Globals configt *config = NULL; // all configuration int nlfd = -1; // netlink socket int tunfd = -1; // tun interface file handle. (network device) int udpfd[MAX_UDPFD + 1] = INIT_TABUDPFD; // array UDP file handle + 1 for lac udp int udplacfd = -1; // UDP LAC file handle int controlfd = -1; // Control signal handle int clifd = -1; // Socket listening for CLI connections. int daefd = -1; // Socket listening for DAE connections. int snoopfd = -1; // UDP file handle for sending out intercept data int *radfds = NULL; // RADIUS requests file handles int rand_fd = -1; // Random data source int cluster_sockfd = -1; // Intra-cluster communications socket. int epollfd = -1; // event polling time_t basetime = 0; // base clock char hostname[MAXHOSTNAME] = ""; // us. static int tunidx; // ifr_ifindex of tun device int nlseqnum = 0; // netlink sequence number int min_initok_nlseqnum = 0; // minimun seq number for messages after init is ok static int syslog_log = 0; // are we logging to syslog static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog). uint32_t last_id = 0; // Unique ID for radius accounting // Guest change char guest_users[10][32]; // Array of guest users int guest_accounts_num = 0; // Number of guest users // calculated from config->l2tp_mtu uint16_t MRU = 0; // PPP MRU uint16_t MSS = 0; // TCP MSS struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI union iphash { sessionidt sess; union iphash *idx; } ip_hash[256]; // Mapping from IP address to session structures. struct ipv6radix { sessionidt sess; struct ipv6radix *branch; } ipv6_hash[16]; // Mapping from IPv6 address to session structures. // Traffic counters. static uint32_t udp_rx = 0, udp_rx_pkt = 0, udp_tx = 0; static uint32_t eth_rx = 0, eth_rx_pkt = 0; uint32_t eth_tx = 0; static uint32_t ip_pool_size = 1; // Size of the pool of addresses used for dynamic address allocation. time_t time_now = 0; // Current time in seconds since epoch. uint64_t time_now_ms = 0; // Current time in milliseconds since epoch. static char time_now_string[64] = {0}; // Current time as a string. static int time_changed = 0; // time_now changed char main_quit = 0; // True if we're in the process of exiting. static char main_reload = 0; // Re-load pending linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; #define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER) #define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(configt, MEMBER), membersize(configt, MEMBER), TYPE } config_descriptt config_values[] = { CONFIG("debug", debug, INT), CONFIG("log_file", log_filename, STRING), CONFIG("pid_file", pid_file, STRING), CONFIG("random_device", random_device, STRING), CONFIG("l2tp_secret", l2tp_secret, STRING), CONFIG("l2tp_mtu", l2tp_mtu, INT), CONFIG("ppp_restart_time", ppp_restart_time, INT), CONFIG("ppp_max_configure", ppp_max_configure, INT), CONFIG("ppp_max_failure", ppp_max_failure, INT), CONFIG("ppp_keepalive", ppp_keepalive, BOOL), CONFIG("primary_dns", default_dns1, IPv4), CONFIG("secondary_dns", default_dns2, IPv4), CONFIG("primary_radius", radiusserver[0], IPv4), CONFIG("secondary_radius", radiusserver[1], IPv4), CONFIG("primary_radius_port", radiusport[0], SHORT), CONFIG("secondary_radius_port", radiusport[1], SHORT), CONFIG("radius_accounting", radius_accounting, BOOL), CONFIG("radius_interim", radius_interim, INT), CONFIG("radius_secret", radiussecret, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), CONFIG("radius_dae_port", radius_dae_port, SHORT), CONFIG("radius_bind_min", radius_bind_min, SHORT), CONFIG("radius_bind_max", radius_bind_max, SHORT), CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL), CONFIG("kill_timedout_sessions", kill_timedout_sessions, BOOL), CONFIG("guest_account", guest_user, STRING), CONFIG("bind_address", bind_address, IPv4), CONFIG("peer_address", peer_address, IPv4), CONFIG("send_garp", send_garp, BOOL), CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG), CONFIG("throttle_buckets", num_tbfs, INT), CONFIG("accounting_dir", accounting_dir, STRING), CONFIG("account_all_origin", account_all_origin, BOOL), CONFIG("dump_speed", dump_speed, BOOL), CONFIG("multi_read_count", multi_read_count, INT), CONFIG("scheduler_fifo", scheduler_fifo, BOOL), CONFIG("lock_pages", lock_pages, BOOL), CONFIG("icmp_rate", icmp_rate, INT), CONFIG("packet_limit", max_packets, INT), CONFIG("cluster_address", cluster_address, IPv4), CONFIG("cluster_port", cluster_port, INT), CONFIG("cluster_interface", cluster_interface, STRING), CONFIG("cluster_mcast_ttl", cluster_mcast_ttl, INT), CONFIG("cluster_hb_interval", cluster_hb_interval, INT), CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT), CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT), CONFIG("ipv6_prefix", ipv6_prefix, IPv6), CONFIG("cli_bind_address", cli_bind_address, IPv4), CONFIG("hostname", hostname, STRING), #ifdef BGP CONFIG("nexthop_address", nexthop_address, IPv4), CONFIG("nexthop6_address", nexthop6_address, IPv6), #endif CONFIG("echo_timeout", echo_timeout, INT), CONFIG("idle_echo_timeout", idle_echo_timeout, INT), CONFIG("iftun_address", iftun_address, IPv4), CONFIG("tundevicename", tundevicename, STRING), CONFIG("disable_lac_func", disable_lac_func, BOOL), CONFIG("auth_tunnel_change_addr_src", auth_tunnel_change_addr_src, BOOL), CONFIG("bind_address_remotelns", bind_address_remotelns, IPv4), CONFIG("bind_portremotelns", bind_portremotelns, SHORT), CONFIG("pppoe_if_to_bind", pppoe_if_to_bind, STRING), CONFIG("pppoe_service_name", pppoe_service_name, STRING), CONFIG("pppoe_ac_name", pppoe_ac_name, STRING), CONFIG("disable_sending_hello", disable_sending_hello, BOOL), CONFIG("disable_no_spoof", disable_no_spoof, BOOL), CONFIG("bind_multi_address", bind_multi_address, STRING), CONFIG("pppoe_only_equal_svc_name", pppoe_only_equal_svc_name, BOOL), CONFIG("multi_hostname", multi_hostname, STRING), CONFIG("no_throttle_local_IP", no_throttle_local_IP, BOOL), CONFIG("dhcp6_preferred_lifetime", dhcp6_preferred_lifetime, INT), CONFIG("dhcp6_valid_lifetime", dhcp6_valid_lifetime, INT), CONFIG("dhcp6_server_duid", dhcp6_server_duid, INT), CONFIG("dns6_lifetime", dns6_lifetime, INT), CONFIG("primary_ipv6_dns", default_ipv6_dns1, IPv6), CONFIG("secondary_ipv6_dns", default_ipv6_dns2, IPv6), CONFIG("default_ipv6_domain_list", default_ipv6_domain_list, STRING), { NULL, 0, 0, 0 } }; static char *plugin_functions[] = { NULL, "plugin_pre_auth", "plugin_post_auth", "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 max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) // Counters for shutdown sessions static sessiont shut_acct[8192]; static sessionidt shut_acct_n = 0; tunnelt *tunnel = NULL; // Array of tunnel structures. bundlet *bundle = NULL; // Array of bundle structures. fragmentationt *frag = NULL; // Array of fragmentation structures. sessiont *session = NULL; // Array of session structures. sessionlocalt *sess_local = NULL; // Array of local per-session counters. radiust *radius = NULL; // Array of radius structures. ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses. ip_filtert *ip_filters = NULL; // Array of named filters. static controlt *controlfree = 0; struct Tstats *_statistics = NULL; #ifdef RINGBUFFER struct Tringbuffer *ringbuffer = NULL; #endif static ssize_t netlink_send(struct nlmsghdr *nh); static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen); static void cache_ipmap(in_addr_t ip, sessionidt s); static void uncache_ipmap(in_addr_t ip); static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s); static void free_ip_address(sessionidt s); static void dump_acct_info(int all); static void sighup_handler(int sig); static void shutdown_handler(int sig); static void sigchild_handler(int sig); static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response); static void update_config(void); static void read_config_file(void); static void initplugins(void); static int add_plugin(char *plugin_name); static int remove_plugin(char *plugin_name); static void plugins_done(void); static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local); static tunnelidt new_tunnel(void); static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len); static void bundleclear(bundleidt b); // return internal time (10ths since process startup), set f if given // as a side-effect sets time_now, and time_changed static clockt now(double *f) { struct timeval t; gettimeofday(&t, 0); if (f) *f = t.tv_sec + t.tv_usec / 1000000.0; if (t.tv_sec != time_now) { time_now = t.tv_sec; time_changed++; } // Time in milliseconds // TODO FOR MLPPP DEV //time_now_ms = (t.tv_sec * 1000) + (t.tv_usec/1000); return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1; } // work out a retry time based on try number // This is a straight bounded exponential backoff. // Maximum re-try time is 32 seconds. (2^5). clockt backoff(uint8_t try) { if (try > 5) try = 5; // max backoff return now(NULL) + 10 * (1 << try); } // // Log a debug message. Typically called via the LOG macro // void _log(int level, sessionidt s, tunnelidt t, const char *format, ...) { static char message[65536] = {0}; va_list ap; #ifdef RINGBUFFER if (ringbuffer) { if (++ringbuffer->tail >= RINGBUFFER_SIZE) ringbuffer->tail = 0; if (ringbuffer->tail == ringbuffer->head) if (++ringbuffer->head >= RINGBUFFER_SIZE) ringbuffer->head = 0; ringbuffer->buffer[ringbuffer->tail].level = level; ringbuffer->buffer[ringbuffer->tail].session = s; ringbuffer->buffer[ringbuffer->tail].tunnel = t; va_start(ap, format); vsnprintf(ringbuffer->buffer[ringbuffer->tail].message, MAX_LOG_LENGTH, format, ap); va_end(ap); } #endif if (config->debug < level) return; va_start(ap, format); vsnprintf(message, sizeof(message), format, ap); if (log_stream) fprintf(log_stream, "%s %02d/%02d %s", time_now_string, t, s, message); else if (syslog_log) syslog(level + 2, "%02d/%02d %s", t, s, message); // We don't need LOG_EMERG or LOG_ALERT va_end(ap); } void _log_hex(int level, const char *title, const uint8_t *data, int maxsize) { int i, j; const uint8_t *d = data; if (config->debug < level) return; // No support for _log_hex to syslog if (log_stream) { _log(level, 0, 0, "%s (%d bytes):\n", title, maxsize); setvbuf(log_stream, NULL, _IOFBF, 16384); for (i = 0; i < maxsize; ) { fprintf(log_stream, "%4X: ", i); for (j = i; j < maxsize && j < (i + 16); j++) { fprintf(log_stream, "%02X ", d[j]); if (j == i + 7) fputs(": ", log_stream); } for (; j < i + 16; j++) { fputs(" ", log_stream); if (j == i + 7) fputs(": ", log_stream); } fputs(" ", log_stream); for (j = i; j < maxsize && j < (i + 16); j++) { if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) fputc(d[j], log_stream); else fputc('.', log_stream); if (j == i + 7) fputs(" ", log_stream); } i = j; fputs("\n", log_stream); } fflush(log_stream); setbuf(log_stream, NULL); } } // update a counter, accumulating 2^32 wraps void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta) { uint32_t new = *counter + delta; if (new < *counter) (*wrap)++; *counter = new; } // initialise the random generator static void initrandom(char *source) { static char path[sizeof(config->random_device)] = "*undefined*"; // reinitialise only if we are forced to do so or if the config has changed if (source && !strncmp(path, source, sizeof(path))) return; // close previous source, if any if (rand_fd >= 0) close(rand_fd); rand_fd = -1; if (source) { // register changes snprintf(path, sizeof(path), "%s", source); if (*path == '/') { rand_fd = open(path, O_RDONLY|O_NONBLOCK); if (rand_fd < 0) LOG(0, 0, 0, "Error opening the random device %s: %s\n", path, strerror(errno)); } } } // fill buffer with random data void random_data(uint8_t *buf, int len) { int n = 0; CSTAT(random_data); if (rand_fd >= 0) { n = read(rand_fd, buf, len); if (n >= len) return; if (n < 0) { if (errno != EAGAIN) { LOG(0, 0, 0, "Error reading from random source: %s\n", strerror(errno)); // fall back to rand() initrandom(NULL); } n = 0; } } // append missing data while (n < len) // not using the low order bits from the prng stream buf[n++] = (rand() >> 4) & 0xff; } // Add a route // // This adds it to the routing table, advertises it // via BGP if enabled, and stuffs it into the // 'sessionbyip' cache. // // 'ip' must be in _host_ order. // static void routeset(sessionidt s, in_addr_t ip, int prefixlen, in_addr_t gw, int add) { struct { struct nlmsghdr nh; struct rtmsg rt; char buf[32]; } req; int i; in_addr_t n_ip; if (!prefixlen) prefixlen = 32; ip &= 0xffffffff << (32 - prefixlen);; // Force the ip to be the first one in the route. memset(&req, 0, sizeof(req)); if (add) { req.nh.nlmsg_type = RTM_NEWROUTE; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; } else { req.nh.nlmsg_type = RTM_DELROUTE; req.nh.nlmsg_flags = NLM_F_REQUEST; } req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)); req.rt.rtm_family = AF_INET; req.rt.rtm_dst_len = prefixlen; req.rt.rtm_table = RT_TABLE_MAIN; req.rt.rtm_protocol = 42; req.rt.rtm_scope = RT_SCOPE_LINK; req.rt.rtm_type = RTN_UNICAST; netlink_addattr(&req.nh, RTA_OIF, &tunidx, sizeof(int)); n_ip = htonl(ip); netlink_addattr(&req.nh, RTA_DST, &n_ip, sizeof(n_ip)); if (gw) { n_ip = htonl(gw); netlink_addattr(&req.nh, RTA_GATEWAY, &n_ip, sizeof(n_ip)); } LOG(1, s, session[s].tunnel, "Route %s %s/%d%s%s\n", add ? "add" : "del", fmtaddr(htonl(ip), 0), prefixlen, gw ? " via" : "", gw ? fmtaddr(htonl(gw), 2) : ""); if (netlink_send(&req.nh) < 0) LOG(0, 0, 0, "routeset() error in sending netlink message: %s\n", strerror(errno)); #ifdef BGP if (add) bgp_add_route(htonl(ip), prefixlen); else bgp_del_route(htonl(ip), prefixlen); #endif /* BGP */ // Add/Remove the IPs to the 'sessionbyip' cache. // Note that we add the zero address in the case of // a network route. Roll on CIDR. // Note that 's == 0' implies this is the address pool. // We still cache it here, because it will pre-fill // the malloc'ed tree. if (s) { if (!add) // Are we deleting a route? s = 0; // Caching the session as '0' is the same as uncaching. for (i = ip; i < ip+(1<<(32-prefixlen)) ; ++i) cache_ipmap(i, s); } } void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add) { struct { struct nlmsghdr nh; struct rtmsg rt; char buf[64]; } req; int metric; char ipv6addr[INET6_ADDRSTRLEN]; if (!config->ipv6_prefix.s6_addr[0]) { LOG(0, 0, 0, "Asked to set IPv6 route, but IPv6 not setup.\n"); return; } memset(&req, 0, sizeof(req)); if (add) { req.nh.nlmsg_type = RTM_NEWROUTE; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; } else { req.nh.nlmsg_type = RTM_DELROUTE; req.nh.nlmsg_flags = NLM_F_REQUEST; } req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)); req.rt.rtm_family = AF_INET6; req.rt.rtm_dst_len = prefixlen; req.rt.rtm_table = RT_TABLE_MAIN; req.rt.rtm_protocol = 42; req.rt.rtm_scope = RT_SCOPE_LINK; req.rt.rtm_type = RTN_UNICAST; netlink_addattr(&req.nh, RTA_OIF, &tunidx, sizeof(int)); netlink_addattr(&req.nh, RTA_DST, &ip, sizeof(ip)); metric = 1; netlink_addattr(&req.nh, RTA_METRICS, &metric, sizeof(metric)); LOG(1, s, session[s].tunnel, "Route %s %s/%d\n", add ? "add" : "del", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), prefixlen); if (netlink_send(&req.nh) < 0) LOG(0, 0, 0, "route6set() error in sending netlink message: %s\n", strerror(errno)); #ifdef BGP if (add) bgp_add_route6(ip, prefixlen); else bgp_del_route6(ip, prefixlen); #endif /* BGP */ if (s) { if (!add) // Are we deleting a route? s = 0; // Caching the session as '0' is the same as uncaching. cache_ipv6map(ip, prefixlen, s); } return; } // // Set up netlink socket static void initnetlink(void) { struct sockaddr_nl nladdr; nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (nlfd < 0) { LOG(0, 0, 0, "Can't create netlink socket: %s\n", strerror(errno)); exit(1); } memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); if (bind(nlfd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) { LOG(0, 0, 0, "Can't bind netlink socket: %s\n", strerror(errno)); exit(1); } } static ssize_t netlink_send(struct nlmsghdr *nh) { struct sockaddr_nl nladdr; struct iovec iov; struct msghdr msg; nh->nlmsg_pid = getpid(); nh->nlmsg_seq = ++nlseqnum; // set kernel address memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; iov = (struct iovec){ (void *)nh, nh->nlmsg_len }; msg = (struct msghdr){ (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; return sendmsg(nlfd, &msg, 0); } static ssize_t netlink_recv(void *buf, ssize_t len) { struct sockaddr_nl nladdr; struct iovec iov; struct msghdr msg; // set kernel address memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; iov = (struct iovec){ buf, len }; msg = (struct msghdr){ (void *)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; return recvmsg(nlfd, &msg, 0); } /* adapted from iproute2 */ static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; rta = (struct rtattr *)(((void *)nh) + NLMSG_ALIGN(nh->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, alen); nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + RTA_ALIGN(len); } // messages corresponding to different phases seq number static char *tun_nl_phase_msg[] = { "initialized", "getting tun interface index", "setting tun interface parameters", "setting tun IPv4 address", "setting tun LL IPv6 address", "setting tun global IPv6 address", }; // // Set up TUN interface static void inittun(void) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN; tunfd = open(TUNDEVICE, O_RDWR); if (tunfd < 0) { // fatal LOG(0, 0, 0, "Can't open %s: %s\n", TUNDEVICE, strerror(errno)); exit(1); } { int flags = fcntl(tunfd, F_GETFL, 0); fcntl(tunfd, F_SETFL, flags | O_NONBLOCK); } if (*config->tundevicename) strncpy(ifr.ifr_name, config->tundevicename, IFNAMSIZ); if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) { LOG(0, 0, 0, "Can't set tun interface: %s\n", strerror(errno)); exit(1); } assert(strlen(ifr.ifr_name) < sizeof(config->tundevicename) - 1); strncpy(config->tundevicename, ifr.ifr_name, sizeof(config->tundevicename)); tunidx = if_nametoindex(config->tundevicename); if (tunidx == 0) { LOG(0, 0, 0, "Can't get tun interface index\n"); exit(1); } { struct { // interface setting struct nlmsghdr nh; union { struct ifinfomsg ifinfo; struct ifaddrmsg ifaddr; } ifmsg; char rtdata[32]; // 32 should be enough } req; uint32_t txqlen, mtu; in_addr_t ip; memset(&req, 0, sizeof(req)); req.nh.nlmsg_type = RTM_NEWLINK; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_MULTI; req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifinfo)); req.ifmsg.ifinfo.ifi_family = AF_UNSPEC; req.ifmsg.ifinfo.ifi_index = tunidx; req.ifmsg.ifinfo.ifi_flags |= IFF_UP; // set interface up req.ifmsg.ifinfo.ifi_change = IFF_UP; // only change this flag /* Bump up the qlen to deal with bursts from the network */ txqlen = 1000; netlink_addattr(&req.nh, IFLA_TXQLEN, &txqlen, sizeof(txqlen)); /* set MTU to modem MRU */ mtu = MRU; netlink_addattr(&req.nh, IFLA_MTU, &mtu, sizeof(mtu)); if (netlink_send(&req.nh) < 0) goto senderror; memset(&req, 0, sizeof(req)); req.nh.nlmsg_type = RTM_NEWADDR; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_MULTI; req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr)); req.ifmsg.ifaddr.ifa_family = AF_INET; req.ifmsg.ifaddr.ifa_prefixlen = 32; req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_UNIVERSE; req.ifmsg.ifaddr.ifa_index = tunidx; if (config->nbmultiaddress > 1) { int i; for (i = 0; i < config->nbmultiaddress ; i++) { ip = config->iftun_n_address[i]; netlink_addattr(&req.nh, IFA_LOCAL, &ip, sizeof(ip)); if (netlink_send(&req.nh) < 0) goto senderror; } } else { if (config->iftun_address) ip = config->iftun_address; else ip = 0x01010101; // 1.1.1.1 netlink_addattr(&req.nh, IFA_LOCAL, &ip, sizeof(ip)); if (netlink_send(&req.nh) < 0) goto senderror; } // Only setup IPv6 on the tun device if we have a configured prefix if (config->ipv6_prefix.s6_addr[0]) { struct in6_addr ip6; memset(&req, 0, sizeof(req)); req.nh.nlmsg_type = RTM_NEWADDR; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_MULTI; req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr)); req.ifmsg.ifaddr.ifa_family = AF_INET6; req.ifmsg.ifaddr.ifa_prefixlen = 64; req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_LINK; req.ifmsg.ifaddr.ifa_index = tunidx; // Link local address is FE80::1 memset(&ip6, 0, sizeof(ip6)); ip6.s6_addr[0] = 0xFE; ip6.s6_addr[1] = 0x80; ip6.s6_addr[15] = 1; netlink_addattr(&req.nh, IFA_LOCAL, &ip6, sizeof(ip6)); if (netlink_send(&req.nh) < 0) goto senderror; memset(&req, 0, sizeof(req)); req.nh.nlmsg_type = RTM_NEWADDR; req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_MULTI; req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifmsg.ifaddr)); req.ifmsg.ifaddr.ifa_family = AF_INET6; req.ifmsg.ifaddr.ifa_prefixlen = 64; req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_UNIVERSE; req.ifmsg.ifaddr.ifa_index = tunidx; // Global address is prefix::1 ip6 = config->ipv6_prefix; ip6.s6_addr[15] = 1; netlink_addattr(&req.nh, IFA_LOCAL, &ip6, sizeof(ip6)); if (netlink_send(&req.nh) < 0) goto senderror; } memset(&req, 0, sizeof(req)); req.nh.nlmsg_type = NLMSG_DONE; req.nh.nlmsg_len = NLMSG_LENGTH(0); if (netlink_send(&req.nh) < 0) goto senderror; // if we get an error for seqnum < min_initok_nlseqnum, // we must exit as initialization went wrong if (config->ipv6_prefix.s6_addr[0]) min_initok_nlseqnum = 5 + 1; // idx + if + addr + 2*addr6 else min_initok_nlseqnum = 3 + 1; // idx + if + addr } return; senderror: LOG(0, 0, 0, "Error while setting up tun device: %s\n", strerror(errno)); exit(1); } // set up LAC UDP ports static void initlacudp(void) { int on = 1; struct sockaddr_in addr; // Tunnel to Remote LNS memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(config->bind_portremotelns); addr.sin_addr.s_addr = config->bind_address_remotelns; udplacfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(udplacfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); { int flags = fcntl(udplacfd, F_GETFL, 0); fcntl(udplacfd, F_SETFL, flags | O_NONBLOCK); } if (bind(udplacfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in UDP REMOTE LNS bind: %s\n", strerror(errno)); exit(1); } } // set up control ports static void initcontrol(void) { int on = 1; struct sockaddr_in addr; // Control memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(NSCTL_PORT); controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); setsockopt(controlfd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto if (bind(controlfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); exit(1); } } // set up Dynamic Authorization Extensions to RADIUS port static void initdae(void) { int on = 1; struct sockaddr_in addr; // Dynamic Authorization Extensions to RADIUS memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(config->radius_dae_port); daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); setsockopt(daefd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto if (bind(daefd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); exit(1); } } // set up UDP ports static void initudp(int * pudpfd, in_addr_t ip_bind) { int on = 1; struct sockaddr_in addr; // Tunnel memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(L2TPPORT); addr.sin_addr.s_addr = ip_bind; (*pudpfd) = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt((*pudpfd), SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); { int flags = fcntl((*pudpfd), F_GETFL, 0); fcntl((*pudpfd), F_SETFL, flags | O_NONBLOCK); } if (bind((*pudpfd), (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); exit(1); } } // // Find session by IP, < 1 for not found // // Confusingly enough, this 'ip' must be // in _network_ order. This being the common // case when looking it up from IP packet headers. // // We actually use this cache for two things. // #1. For used IP addresses, this maps to the // session ID that it's used by. // #2. For un-used IP addresses, this maps to the // index into the pool table that contains that // IP address. // static sessionidt lookup_ipmap(in_addr_t ip) { uint8_t *a = (uint8_t *) &ip; union iphash *h = ip_hash; if (!(h = h[*a++].idx)) return 0; if (!(h = h[*a++].idx)) return 0; if (!(h = h[*a++].idx)) return 0; return h[*a].sess; } static sessionidt lookup_ipv6map(struct in6_addr ip) { struct ipv6radix *curnode; int i; int s; char ipv6addr[INET6_ADDRSTRLEN]; curnode = &ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4]; i = 1; s = curnode->sess; while (s == 0 && i < 32 && curnode->branch != NULL) { if (i & 1) curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F]; else curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4]; s = curnode->sess; i++; } LOG(4, s, session[s].tunnel, "Looking up address %s and got %d\n", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), s); return s; } sessionidt sessionbyip(in_addr_t ip) { sessionidt s = lookup_ipmap(ip); CSTAT(sessionbyip); if (s > 0 && s < MAXSESSION && session[s].opened) return s; return 0; } sessionidt sessionbyipv6(struct in6_addr ip) { sessionidt s; CSTAT(sessionbyipv6); if (!memcmp(&config->ipv6_prefix, &ip, 8) || (ip.s6_addr[0] == 0xFE && ip.s6_addr[1] == 0x80 && ip.s6_addr16[1] == 0 && ip.s6_addr16[2] == 0 && ip.s6_addr16[3] == 0)) { in_addr_t *pipv4 = (in_addr_t *) &ip.s6_addr[8]; s = lookup_ipmap(*pipv4); } else { s = lookup_ipv6map(ip); } if (s > 0 && s < MAXSESSION && session[s].opened) return s; return 0; } sessionidt sessionbyipv6new(struct in6_addr ip) { sessionidt s; CSTAT(sessionbyipv6new); s = lookup_ipv6map(ip); if (s > 0 && s < MAXSESSION && session[s].opened) return s; return 0; } // // Take an IP address in HOST byte order and // add it to the sessionid by IP cache. // // (It's actually cached in network order) // static void cache_ipmap(in_addr_t ip, sessionidt s) { in_addr_t nip = htonl(ip); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0] uint8_t *a = (uint8_t *) &nip; union iphash *h = ip_hash; int i; for (i = 0; i < 3; i++) { if (!(h[a[i]].idx || (h[a[i]].idx = calloc(256, sizeof(union iphash))))) return; h = h[a[i]].idx; } h[a[3]].sess = s; if (s > 0) LOG(4, s, session[s].tunnel, "Caching ip address %s\n", fmtaddr(nip, 0)); else if (s == 0) LOG(4, 0, 0, "Un-caching ip address %s\n", fmtaddr(nip, 0)); // else a map to an ip pool index. } static void uncache_ipmap(in_addr_t ip) { cache_ipmap(ip, 0); // Assign it to the NULL session. } static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s) { int i; int niblles; struct ipv6radix *curnode; char ipv6addr[INET6_ADDRSTRLEN]; curnode = &ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4]; niblles = prefixlen >> 2; i = 1; while (i < niblles) { if (curnode->branch == NULL) { if (!(curnode->branch = calloc(16, sizeof (struct ipv6radix)))) return; } if (i & 1) curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F]; else curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4]; i++; } curnode->sess = s; if (s > 0) LOG(4, s, session[s].tunnel, "Caching ip address %s/%d\n", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), prefixlen); else if (s == 0) LOG(4, 0, 0, "Un-caching ip address %s/%d\n", inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), prefixlen); } // // CLI list to dump current ipcache. // int cmd_show_ipcache(struct cli_def *cli, const char *command, char **argv, int argc) { union iphash *d = ip_hash, *e, *f, *g; int i, j, k, l; int count = 0; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; cli_print(cli, "%7s %s", "Sess#", "IP Address"); for (i = 0; i < 256; ++i) { if (!d[i].idx) continue; e = d[i].idx; for (j = 0; j < 256; ++j) { if (!e[j].idx) continue; f = e[j].idx; for (k = 0; k < 256; ++k) { if (!f[k].idx) continue; g = f[k].idx; for (l = 0; l < 256; ++l) { if (!g[l].sess) continue; cli_print(cli, "%7d %d.%d.%d.%d", g[l].sess, i, j, k, l); ++count; } } } } cli_print(cli, "%d entries in cache", count); return CLI_OK; } // Find session by username, 0 for not found // walled garden users aren't authenticated, so the username is // reasonably useless. Ignore them to avoid incorrect actions // // This is VERY inefficent. Don't call it often. :) // sessionidt sessionbyuser(char *username) { int s; CSTAT(sessionbyuser); for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { if (!session[s].opened) continue; if (session[s].walled_garden) continue; // Skip walled garden users. if (!strncmp(session[s].user, username, 128)) return s; } return 0; // Not found. } void send_garp(in_addr_t ip) { int s; struct ifreq ifr; uint8_t mac[6]; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { LOG(0, 0, 0, "Error creating socket for GARP: %s\n", strerror(errno)); return; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) { LOG(0, 0, 0, "Error getting eth0 hardware address for GARP: %s\n", strerror(errno)); close(s); return; } memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { LOG(0, 0, 0, "Error getting eth0 interface index for GARP: %s\n", strerror(errno)); close(s); return; } close(s); sendarp(ifr.ifr_ifindex, mac, ip); } static sessiont *sessiontbysessionidt(sessionidt s) { if (!s || s >= MAXSESSION) return NULL; return &session[s]; } static sessionidt sessionidtbysessiont(sessiont *s) { sessionidt val = s-session; if (s < session || val >= MAXSESSION) return 0; return val; } // actually send a control message for a specific tunnel void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t) { struct sockaddr_in addr; CSTAT(tunnelsend); if (!t) { LOG(0, 0, t, "tunnelsend called with 0 as tunnel id\n"); STAT(tunnel_tx_errors); return; } if (t == TUNNEL_ID_PPPOE) { pppoe_sess_send(buf, l, t); return; } if (!tunnel[t].ip) { LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); STAT(tunnel_tx_errors); return; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; *(uint32_t *) & addr.sin_addr = htonl(tunnel[t].ip); addr.sin_port = htons(tunnel[t].port); // sequence expected, if sequence in message if (*buf & 0x08) *(uint16_t *) (buf + ((*buf & 0x40) ? 10 : 8)) = htons(tunnel[t].nr); // If this is a control message, deal with retries if (*buf & 0x80) { tunnel[t].last = time_now; // control message sent tunnel[t].retry = backoff(tunnel[t].try); // when to resend if (tunnel[t].try) { STAT(tunnel_retries); LOG(3, 0, t, "Control message resend try %d\n", tunnel[t].try); } } if (sendto(udpfd[tunnel[t].indexudp], buf, l, 0, (void *) &addr, sizeof(addr)) < 0) { LOG(0, ntohs((*(uint16_t *) (buf + 6))), t, "Error sending data out tunnel: %s (udpfd=%d, buf=%p, len=%d, dest=%s)\n", strerror(errno), udpfd[tunnel[t].indexudp], buf, l, inet_ntoa(addr.sin_addr)); STAT(tunnel_tx_errors); return; } LOG_HEX(5, "Send Tunnel Data", buf, l); STAT(tunnel_tx_packets); INC_STAT(tunnel_tx_bytes, l); } // // Tiny helper function to write data to // the 'tun' device. // int tun_write(uint8_t * data, int size) { return write(tunfd, data, size); } // adjust tcp mss to avoid fragmentation (called only for tcp packets with syn set) void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp) { int d = (tcp[12] >> 4) * 4; uint8_t *mss = 0; uint8_t *opts; uint8_t *data; uint16_t orig; uint32_t sum; if ((tcp[13] & 0x3f) & ~(TCP_FLAG_SYN|TCP_FLAG_ACK)) // only want SYN and SYN,ACK return; if (tcp + d > buf + len) // short? return; opts = tcp + 20; data = tcp + d; while (opts < data) { if (*opts == 2 && opts[1] == 4) // mss option (2), length 4 { mss = opts + 2; if (mss + 2 > data) return; // short? break; } if (*opts == 0) return; // end of options if (*opts == 1 || !opts[1]) // no op (one byte), or no length (prevent loop) opts++; else opts += opts[1]; // skip over option } if (!mss) return; // not found orig = ntohs(*(uint16_t *) mss); if (orig <= MSS) return; // mss OK LOG(5, s, t, "TCP: %s:%u -> %s:%u SYN%s: adjusted mss from %u to %u\n", fmtaddr(*(in_addr_t *) (buf + 12), 0), ntohs(*(uint16_t *) tcp), fmtaddr(*(in_addr_t *) (buf + 16), 1), ntohs(*(uint16_t *) (tcp + 2)), (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, MSS); // set mss *(int16_t *) mss = htons(MSS); // adjust checksum (see rfc1141) sum = orig + (~MSS & 0xffff); sum += ntohs(*(uint16_t *) (tcp + 16)); sum = (sum & 0xffff) + (sum >> 16); *(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16)); } void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra) { uint16_t proto; if (extra) { // Skip the four extra bytes p += 4; l -= 4; } if (*p & 1) { proto = *p++; l--; } else { proto = ntohs(*(uint16_t *) p); p += 2; l -= 2; } if (proto == PPPIP) { if (session[s].die) { LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } session[s].last_packet = session[s].last_data = time_now; processipin(s, t, p, l); } else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) { if (session[s].die) { LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } session[s].last_packet = session[s].last_data = time_now; processipv6in(s, t, p, l); } else if (proto == PPPIPCP) { session[s].last_packet = session[s].last_data = time_now; processipcp(s, t, p, l); } else if (proto == PPPCCP) { session[s].last_packet = session[s].last_data = time_now; processccp(s, t, p, l); } else { LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto); } } static void update_session_out_stat(sessionidt s, sessiont *sp, int len) { increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count sp->cout_delta += len; sp->pout++; sp->last_data = time_now; sess_local[s].cout += len; // To send to master.. sess_local[s].pout++; } // process outgoing (to tunnel) IP // // (i.e. this routine writes to data[-8]). void processipout(uint8_t *buf, int len) { sessionidt s; sessiont *sp; tunnelidt t; in_addr_t ip, ip_src; uint8_t *data = buf; // Keep a copy of the originals. int size = len; uint8_t fragbuf[MAXETHER + 20]; CSTAT(processipout); if (len < MIN_IP_SIZE) { LOG(1, 0, 0, "Short IP, %d bytes\n", len); STAT(tun_rx_errors); return; } if (len >= MAXETHER) { LOG(1, 0, 0, "Oversize IP packet %d bytes\n", len); STAT(tun_rx_errors); return; } // Skip the tun header buf += 4; len -= 4; // Got an IP header now if (*(uint8_t *)(buf) >> 4 != 4) { LOG(1, 0, 0, "IP: Don't understand anything except IPv4\n"); return; } ip_src = *(uint32_t *)(buf + 12); ip = *(uint32_t *)(buf + 16); if (!(s = sessionbyip(ip))) { // Is this a packet for a session that doesn't exist? static int rate = 0; // Number of ICMP packets we've sent this second. static int last = 0; // Last time we reset the ICMP packet counter 'rate'. if (last != time_now) { last = time_now; rate = 0; } if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second. { LOG(4, 0, 0, "IP: Sending ICMP host unreachable to %s\n", fmtaddr(*(in_addr_t *)(buf + 12), 0)); host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4), config->bind_address ? config->bind_address : my_address, buf, len); } return; } t = session[s].tunnel; if (len > session[s].mru || (session[s].mrru && len > session[s].mrru)) { LOG(3, s, t, "Packet size more than session MRU\n"); return; } sp = &session[s]; // DoS prevention: enforce a maximum number of packets per 0.1s for a session if (config->max_packets > 0) { if (sess_local[s].last_packet_out == TIME) { int max = config->max_packets; // All packets for throttled sessions are handled by the // master, so further limit by using the throttle rate. // A bit of a kludge, since throttle rate is in kbps, // but should still be generous given our average DSL // packet size is 200 bytes: a limit of 28kbps equates // to around 180 packets per second. if (!config->cluster_iam_master && sp->throttle_out && sp->throttle_out < max) max = sp->throttle_out; if (++sess_local[s].packets_out > max) { sess_local[s].packets_dropped++; return; } } else { if (sess_local[s].packets_dropped) { INC_STAT(tun_rx_dropped, sess_local[s].packets_dropped); LOG(3, s, t, "Dropped %u/%u packets to %s for %suser %s\n", sess_local[s].packets_dropped, sess_local[s].packets_out, fmtaddr(ip, 0), sp->throttle_out ? "throttled " : "", sp->user); } sess_local[s].last_packet_out = TIME; sess_local[s].packets_out = 1; sess_local[s].packets_dropped = 0; } } // run access-list if any if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1)) return; // adjust MSS on SYN and SYN,ACK packets with options if ((ntohs(*(uint16_t *) (buf + 6)) & 0x1fff) == 0 && buf[9] == IPPROTO_TCP) // first tcp fragment { int ihl = (buf[0] & 0xf) * 4; // length of IP header if (len >= ihl + 20 && (buf[ihl + 13] & TCP_FLAG_SYN) && ((buf[ihl + 12] >> 4) > 5)) adjust_tcp_mss(s, t, buf, len, buf + ihl); } if (sp->tbf_out) { if (!config->no_throttle_local_IP || !sessionbyip(ip_src)) { // Are we throttling this session? if (config->cluster_iam_master) tbf_queue_packet(sp->tbf_out, data, size); else master_throttle_packet(sp->tbf_out, data, size); return; } } if (sp->walled_garden && !config->cluster_iam_master) { // We are walled-gardening this master_garden_packet(s, data, size); return; } if(session[s].bundle != 0 && bundle[session[s].bundle].num_of_links > 1) { if (!config->cluster_iam_master) { // The MPPP packets must be managed by the Master. master_forward_mppp_packet(s, data, size); return; } // Add on L2TP header sessionidt members[MAXBUNDLESES]; bundleidt bid = session[s].bundle; bundlet *b = &bundle[bid]; uint32_t num_of_links, nb_opened; int i; num_of_links = b->num_of_links; nb_opened = 0; for (i = 0;i < num_of_links;i++) { s = b->members[i]; if (session[s].ppp.lcp == Opened) { members[nb_opened] = s; nb_opened++; } } if (nb_opened < 1) { LOG(3, s, t, "MPPP: PROCESSIPOUT ERROR, no session opened in bundle:%d\n", bid); return; } num_of_links = nb_opened; b->current_ses = (b->current_ses + 1) % num_of_links; s = members[b->current_ses]; t = session[s].tunnel; sp = &session[s]; LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s); if (num_of_links > 1) { if(len > MINFRAGLEN) { //for rotate traffic among the member links uint32_t divisor = num_of_links; if (divisor > 2) divisor = divisor/2 + (divisor & 1); // Partition the packet to "num_of_links" fragments uint32_t fraglen = len / divisor; uint32_t last_fraglen = fraglen + len % divisor; uint32_t remain = len; // send the first packet uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, fraglen, s, t, PPPIP, 0, bid, MP_BEGIN); if (!p) return; tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it... // statistics update_session_out_stat(s, sp, fraglen); remain -= fraglen; while (remain > last_fraglen) { b->current_ses = (b->current_ses + 1) % num_of_links; s = members[b->current_ses]; t = session[s].tunnel; sp = &session[s]; LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), fraglen, s, t, PPPIP, 0, bid, 0); if (!p) return; tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it... update_session_out_stat(s, sp, fraglen); remain -= fraglen; } // send the last fragment b->current_ses = (b->current_ses + 1) % num_of_links; s = members[b->current_ses]; t = session[s].tunnel; sp = &session[s]; LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), remain, s, t, PPPIP, 0, bid, MP_END); if (!p) return; tunnelsend(fragbuf, remain + (p-fragbuf), t); // send it... update_session_out_stat(s, sp, remain); if (remain != last_fraglen) LOG(3, s, t, "PROCESSIPOUT ERROR REMAIN != LAST_FRAGLEN, %d != %d\n", remain, last_fraglen); } else { // Send it as one frame uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS); if (!p) return; tunnelsend(fragbuf, len + (p-fragbuf), t); // send it... LOG(4, s, t, "MPPP: packet sent as one frame\n"); update_session_out_stat(s, sp, len); } } else { // Send it as one frame (NO MPPP Frame) uint8_t *p = opt_makeppp(buf, len, s, t, PPPIP, 0, 0, 0); tunnelsend(p, len + (buf-p), t); // send it... update_session_out_stat(s, sp, len); } } else { uint8_t *p = opt_makeppp(buf, len, s, t, PPPIP, 0, 0, 0); tunnelsend(p, len + (buf-p), t); // send it... update_session_out_stat(s, sp, len); } // Snooping this session, send it to intercept box if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); udp_tx += len; } // process outgoing (to tunnel) IPv6 // static void processipv6out(uint8_t * buf, int len) { sessionidt s; sessiont *sp; tunnelidt t; struct in6_addr ip6; uint8_t *data = buf; // Keep a copy of the originals. int size = len; uint8_t b[MAXETHER + 20]; CSTAT(processipv6out); if (len < MIN_IP_SIZE) { LOG(1, 0, 0, "Short IPv6, %d bytes\n", len); STAT(tunnel_tx_errors); return; } if (len >= MAXETHER) { LOG(1, 0, 0, "Oversize IPv6 packet %d bytes\n", len); STAT(tunnel_tx_errors); return; } // Skip the tun header buf += 4; len -= 4; // Got an IP header now if (*(uint8_t *)(buf) >> 4 != 6) { LOG(1, 0, 0, "IP: Don't understand anything except IPv6\n"); return; } ip6 = *(struct in6_addr *)(buf+24); s = sessionbyipv6(ip6); if (s == 0) { s = sessionbyipv6new(ip6); } if (s == 0) { // Is this a packet for a session that doesn't exist? static int rate = 0; // Number of ICMP packets we've sent this second. static int last = 0; // Last time we reset the ICMP packet counter 'rate'. if (last != time_now) { last = time_now; rate = 0; } if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second. { // FIXME: Should send icmp6 host unreachable } return; } if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) { bundleidt bid = session[s].bundle; bundlet *b = &bundle[bid]; b->current_ses = (b->current_ses + 1) % b->num_of_links; s = b->members[b->current_ses]; LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %u\n", s); } t = session[s].tunnel; sp = &session[s]; sp->last_data = time_now; // FIXME: add DoS prevention/filters? if (sp->tbf_out) { // Are we throttling this session? if (config->cluster_iam_master) tbf_queue_packet(sp->tbf_out, data, size); else master_throttle_packet(sp->tbf_out, data, size); return; } else if (sp->walled_garden && !config->cluster_iam_master) { // We are walled-gardening this master_garden_packet(s, data, size); return; } LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); // Add on L2TP header { uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0); if (!p) return; tunnelsend(b, len + (p-b), t); // send it... } // Snooping this session, send it to intercept box if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count sp->cout_delta += len; sp->pout++; udp_tx += len; sess_local[s].cout += len; // To send to master.. sess_local[s].pout++; } // // Helper routine for the TBF filters. // Used to send queued data in to the user! // static void send_ipout(sessionidt s, uint8_t *buf, int len) { sessiont *sp; tunnelidt t; uint8_t *p; uint8_t *data = buf; // Keep a copy of the originals. uint8_t b[MAXETHER + 20]; if (len < 0 || len > MAXETHER) { LOG(1, 0, 0, "Odd size IP packet: %d bytes\n", len); return; } // Skip the tun header buf += 4; len -= 4; if (!session[s].ip) return; t = session[s].tunnel; sp = &session[s]; LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); // Add on L2TP header if (*(uint16_t *) (data + 2) == htons(PKTIPV6)) p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0); // IPV6 else p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0); // IPV4 if (!p) return; tunnelsend(b, len + (p-b), t); // send it... // Snooping this session. if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count sp->cout_delta += len; sp->pout++; udp_tx += len; sess_local[s].cout += len; // To send to master.. sess_local[s].pout++; } // add an AVP (16 bit) static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m) { uint16_t l = (m ? 0x8008 : 0x0008); uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0); pint16[0] = htons(l); pint16[1] = htons(0); pint16[2] = htons(avp); pint16[3] = htons(val); c->length += 8; } // add an AVP (32 bit) static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m) { uint16_t l = (m ? 0x800A : 0x000A); uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0); uint32_t *pint32 = (uint32_t *) (c->buf + c->length + 6); pint16[0] = htons(l); pint16[1] = htons(0); pint16[2] = htons(avp); pint32[0] = htonl(val); c->length += 10; } // add an AVP (string) static void controls(controlt * c, uint16_t avp, char *val, uint8_t m) { uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6); uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0); pint16[0] = htons(l); pint16[1] = htons(0); pint16[2] = htons(avp); memcpy(c->buf + c->length + 6, val, strlen(val)); c->length += 6 + strlen(val); } // add a binary AVP static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m) { uint16_t l = ((m ? 0x8000 : 0) + len + 6); uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0); pint16[0] = htons(l); pint16[1] = htons(0); pint16[2] = htons(avp); memcpy(c->buf + c->length + 6, val, len); c->length += 6 + len; } // new control connection static controlt *controlnew(uint16_t mtype) { controlt *c; if (!controlfree) c = malloc(sizeof(controlt)); else { c = controlfree; controlfree = c->next; } assert(c); c->next = 0; c->buf[0] = 0xC8; // flags c->buf[1] = 0x02; // ver c->length = 12; control16(c, 0, mtype, 1); return c; } // send zero block if nothing is waiting // (ZLB send). static void controlnull(tunnelidt t) { uint16_t buf[6]; if (tunnel[t].controlc) // Messages queued; They will carry the ack. return; buf[0] = htons(0xC802); // flags/ver buf[1] = htons(12); // length buf[2] = htons(tunnel[t].far); // tunnel buf[3] = htons(0); // session buf[4] = htons(tunnel[t].ns); // sequence buf[5] = htons(tunnel[t].nr); // sequence tunnelsend((uint8_t *)buf, 12, t); } // add a control message to a tunnel, and send if within window static void controladd(controlt *c, sessionidt far, tunnelidt t) { uint16_t *pint16 = (uint16_t *) (c->buf + 2); pint16[0] = htons(c->length); // length pint16[1] = htons(tunnel[t].far); // tunnel pint16[2] = htons(far); // session pint16[3] = htons(tunnel[t].ns); // sequence tunnel[t].ns++; // advance sequence // link in message in to queue if (tunnel[t].controlc) tunnel[t].controle->next = c; else tunnel[t].controls = c; tunnel[t].controle = c; tunnel[t].controlc++; // send now if space in window if (tunnel[t].controlc <= tunnel[t].window) { tunnel[t].try = 0; // first send tunnelsend(c->buf, c->length, t); } } // // Throttle or Unthrottle a session // // Throttle the data from/to through a session to no more than // 'rate_in' kbit/sec in (from user) or 'rate_out' kbit/sec out (to // user). // // If either value is -1, the current value is retained for that // direction. // void throttle_session(sessionidt s, int rate_in, int rate_out) { if (!session[s].opened) return; // No-one home. if (!*session[s].user) return; // User not logged in if (rate_in >= 0) { int bytes = rate_in * 1024 / 8; // kbits to bytes if (session[s].tbf_in) free_tbf(session[s].tbf_in); if (rate_in > 0) session[s].tbf_in = new_tbf(s, bytes * 2, bytes, send_ipin); else session[s].tbf_in = 0; session[s].throttle_in = rate_in; } if (rate_out >= 0) { int bytes = rate_out * 1024 / 8; if (session[s].tbf_out) free_tbf(session[s].tbf_out); if (rate_out > 0) session[s].tbf_out = new_tbf(s, bytes * 2, bytes, send_ipout); else session[s].tbf_out = 0; session[s].throttle_out = rate_out; } } // add/remove filters from session (-1 = no change) void filter_session(sessionidt s, int filter_in, int filter_out) { if (!session[s].opened) return; // No-one home. if (!*session[s].user) return; // User not logged in // paranoia if (filter_in > MAXFILTER) filter_in = -1; if (filter_out > MAXFILTER) filter_out = -1; if (session[s].filter_in > MAXFILTER) session[s].filter_in = 0; if (session[s].filter_out > MAXFILTER) session[s].filter_out = 0; if (filter_in >= 0) { if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; if (filter_in > 0) ip_filters[filter_in - 1].used++; session[s].filter_in = filter_in; } if (filter_out >= 0) { if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--; if (filter_out > 0) ip_filters[filter_out - 1].used++; session[s].filter_out = filter_out; } } // start tidy shutdown of session void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause) { int walled_garden = session[s].walled_garden; bundleidt b = session[s].bundle; //delete routes only for last session in bundle (in case of MPPP) int del_routes = !b || (bundle[b].num_of_links == 1); CSTAT(sessionshutdown); if (!session[s].opened) { LOG(3, s, session[s].tunnel, "Called sessionshutdown on an unopened session.\n"); return; // not a live session } if (!session[s].die) { struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] }; LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason); run_plugins(PLUGIN_KILL_SESSION, &data); } if (session[s].ip && !walled_garden && !session[s].die) { // RADIUS Stop message uint16_t r = radiusnew(s); if (r) { // stop, if not already trying if (radius[r].state != RADIUSSTOP) { radius[r].term_cause = term_cause; radius[r].term_msg = reason; radiussend(r, RADIUSSTOP); } } else LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); // Save counters to dump to accounting file if (*config->accounting_dir && shut_acct_n < sizeof(shut_acct) / sizeof(*shut_acct)) memcpy(&shut_acct[shut_acct_n++], &session[s], sizeof(session[s])); } if (!session[s].die) session[s].die = TIME + 150; // Clean up in 15 seconds if (session[s].ip) { // IP allocated, clear and unroute int r; int routed = 0; for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) { if ((session[s].ip >> (32-session[s].route[r].prefixlen)) == (session[s].route[r].ip >> (32-session[s].route[r].prefixlen))) routed++; if (del_routes) routeset(s, session[s].route[r].ip, session[s].route[r].prefixlen, 0, 0); session[s].route[r].ip = 0; } if (session[s].ip_pool_index == -1) // static ip { if (!routed && del_routes) routeset(s, session[s].ip, 0, 0, 0); session[s].ip = 0; } else free_ip_address(s); // unroute IPv6, if setup for (r = 0; r < MAXROUTE6 && session[s].route6[r].ipv6route.s6_addr[0] && session[s].route6[r].ipv6prefixlen; r++) { if (del_routes) route6set(s, session[s].route6[r].ipv6route, session[s].route6[r].ipv6prefixlen, 0); memset(&session[s].route6[r], 0, sizeof(session[s].route6[r])); } if (session[s].ipv6address.s6_addr[0] && del_routes) { route6set(s, session[s].ipv6address, 128, 0); } if (b) { // This session was part of a bundle bundle[b].num_of_links--; LOG(3, s, session[s].tunnel, "MPPP: Dropping member link: %d from bundle %d\n",s,b); if(bundle[b].num_of_links == 0) { bundleclear(b); LOG(3, s, session[s].tunnel, "MPPP: Kill bundle: %d (No remaining member links)\n",b); } else { // Adjust the members array to accomodate the new change uint8_t mem_num = 0; // It should be here num_of_links instead of num_of_links-1 (previous instruction "num_of_links--") if(bundle[b].members[bundle[b].num_of_links] != s) { uint8_t ml; for(ml = 0; mlpeer_address ? config->peer_address : config->iftun_n_address[tunnel[t].indexudp] ? config->iftun_n_address[tunnel[t].indexudp] : my_address; // send my IP tunnelsend(buf, 10 + (q - buf), t); // send it restart_timer(s, ipcp); } void sendipv6cp(sessionidt s, tunnelidt t) { uint8_t buf[MAXETHER]; uint8_t *q; CSTAT(sendipv6cp); LOG(3, s, t, "IPV6CP: send ConfigReq\n"); q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP, 0, 0, 0); if (!q) return; *q = ConfigReq; q[1] = session[s].unique_id & 0xf; // ID, don't care, we // only send one type // of request *(uint16_t *) (q + 2) = htons(14); q[4] = 1; // interface identifier option q[5] = 10; // option length *(uint32_t *) (q + 6) = 0; // We'll be prefix::1 *(uint32_t *) (q + 10) = 0; q[13] = 1; tunnelsend(buf, 14 + (q - buf), t); // send it restart_timer(s, ipv6cp); } static void sessionclear(sessionidt s) { memset(&session[s], 0, sizeof(session[s])); memset(&sess_local[s], 0, sizeof(sess_local[s])); memset(&cli_session_actions[s], 0, sizeof(cli_session_actions[s])); session[s].tunnel = T_FREE; // Mark it as free. session[s].next = sessionfree; sessionfree = s; } // kill a session now void sessionkill(sessionidt s, char *reason) { CSTAT(sessionkill); if (!session[s].opened) // not alive return; if (session[s].next) { LOG(0, s, session[s].tunnel, "Tried to kill a session with next pointer set (%u)\n", session[s].next); return; } if (!session[s].die) sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc. if (sess_local[s].radius) radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed if (session[s].forwardtosession) { sessionidt sess = session[s].forwardtosession; if (session[sess].forwardtosession == s) { // Shutdown the linked session also. sessionshutdown(sess, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET); } } LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason); sessionclear(s); cluster_send_session(s); } static void tunnelclear(tunnelidt t) { if (!t) return; memset(&tunnel[t], 0, sizeof(tunnel[t])); tunnel[t].state = TUNNELFREE; } static void bundleclear(bundleidt b) { if (!b) return; memset(&bundle[b], 0, sizeof(bundle[b])); bundle[b].state = BUNDLEFREE; } // kill a tunnel now static void tunnelkill(tunnelidt t, char *reason) { sessionidt s; controlt *c; CSTAT(tunnelkill); tunnel[t].state = TUNNELDIE; // free control messages while ((c = tunnel[t].controls)) { controlt * n = c->next; tunnel[t].controls = n; tunnel[t].controlc--; c->next = controlfree; controlfree = c; } // kill sessions for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) sessionkill(s, reason); // free tunnel tunnelclear(t); LOG(1, 0, t, "Kill tunnel %u: %s\n", t, reason); cli_tunnel_actions[t].action = 0; cluster_send_tunnel(t); } // shut down a tunnel cleanly static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg) { sessionidt s; CSTAT(tunnelshutdown); if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE) { // never set up, can immediately kill tunnelkill(t, reason); return; } LOG(1, 0, t, "Shutting down tunnel %u (%s)\n", t, reason); // close session for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) sessionshutdown(s, reason, CDN_NONE, TERM_ADMIN_RESET); tunnel[t].state = TUNNELDIE; tunnel[t].die = TIME + 700; // Clean up in 70 seconds cluster_send_tunnel(t); // TBA - should we wait for sessions to stop? if (result) { controlt *c = controlnew(4); // sending StopCCN if (error) { uint16_t buf[32]; int l = 4; buf[0] = htons(result); buf[1] = htons(error); if (msg) { int m = strlen(msg); if (m + 4 > sizeof(buf)) m = sizeof(buf) - 4; memcpy(buf+2, msg, m); l += m; } controlb(c, 1, (uint8_t *)buf, l, 1); } else control16(c, 1, result, 1); control16(c, 9, t, 1); // assigned tunnel (our end) controladd(c, 0, t); // send the message } } // read and process packet on tunnel (UDP) void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexudpfd) { uint8_t *sendchalresponse = NULL; uint8_t *recvchalresponse = NULL; uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0; uint8_t *p = buf + 2; CSTAT(processudp); udp_rx += len; udp_rx_pkt++; LOG_HEX(5, "UDP Data", buf, len); STAT(tunnel_rx_packets); INC_STAT(tunnel_rx_bytes, len); if (len < 6) { LOG(1, 0, 0, "Short UDP, %d bytes\n", len); STAT(tunnel_rx_errors); return; } if ((buf[1] & 0x0F) != 2) { LOG(1, 0, 0, "Bad L2TP ver %d\n", buf[1] & 0x0F); STAT(tunnel_rx_errors); return; } if (*buf & 0x40) { // length l = ntohs(*(uint16_t *) p); p += 2; } t = ntohs(*(uint16_t *) p); p += 2; s = ntohs(*(uint16_t *) p); p += 2; if (s >= MAXSESSION) { LOG(1, s, t, "Received UDP packet with invalid session ID\n"); STAT(tunnel_rx_errors); return; } if (t >= MAXTUNNEL) { LOG(1, s, t, "Received UDP packet with invalid tunnel ID\n"); STAT(tunnel_rx_errors); return; } if (t == TUNNEL_ID_PPPOE) { LOG(1, s, t, "Received UDP packet with tunnel ID reserved for pppoe\n"); STAT(tunnel_rx_errors); return; } if (*buf & 0x08) { // ns/nr ns = ntohs(*(uint16_t *) p); p += 2; nr = ntohs(*(uint16_t *) p); p += 2; } if (*buf & 0x02) { // offset uint16_t o = ntohs(*(uint16_t *) p); p += o + 2; } if ((p - buf) > l) { LOG(1, s, t, "Bad length %d>%d\n", (int) (p - buf), l); STAT(tunnel_rx_errors); return; } l -= (p - buf); // used to time out old tunnels if (t && tunnel[t].state == TUNNELOPEN) tunnel[t].lastrec = time_now; if (*buf & 0x80) { // control uint16_t message = 0xFFFF; // message type uint8_t fatal = 0; uint8_t mandatory = 0; uint16_t asession = 0; // assigned session uint32_t amagic = 0; // magic number uint8_t aflags = 0; // flags from last LCF uint16_t version = 0x0100; // protocol version (we handle 0.0 as well and send that back just in case) char called[MAXTEL] = ""; // called number char calling[MAXTEL] = ""; // calling number if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } // control messages must have bits 0x80|0x40|0x08 // (type, length and sequence) set, and bits 0x02|0x01 // (offset and priority) clear if ((*buf & 0xCB) != 0xC8) { LOG(1, s, t, "Bad control header %02X\n", *buf); STAT(tunnel_rx_errors); return; } // check for duplicate tunnel open message if (!t && ns == 0) { int i; // // Is this a duplicate of the first packet? (SCCRQ) // for (i = 1; i <= config->cluster_highest_tunnelid ; ++i) { if (tunnel[i].state != TUNNELOPENING || tunnel[i].ip != ntohl(*(in_addr_t *) & addr->sin_addr) || tunnel[i].port != ntohs(addr->sin_port) ) continue; t = i; LOG(3, s, t, "Duplicate SCCRQ?\n"); break; } } LOG(3, s, t, "Control message (%d bytes): (unacked %d) l-ns %u l-nr %u r-ns %u r-nr %u\n", l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); // if no tunnel specified, assign one if (!t) { if (!(t = new_tunnel())) { LOG(1, 0, 0, "No more tunnels\n"); STAT(tunnel_overflow); return; } tunnelclear(t); tunnel[t].ip = ntohl(*(in_addr_t *) & addr->sin_addr); tunnel[t].port = ntohs(addr->sin_port); tunnel[t].window = 4; // default window tunnel[t].indexudp = indexudpfd; STAT(tunnel_created); LOG(1, 0, t, " New tunnel from %s:%u ID %u\n", fmtaddr(htonl(tunnel[t].ip), 0), tunnel[t].port, t); } // If the 'ns' just received is not the 'nr' we're // expecting, just send an ack and drop it. // // if 'ns' is less, then we got a retransmitted packet. // if 'ns' is greater than missed a packet. Either way // we should ignore it. if (ns != tunnel[t].nr) { // is this the sequence we were expecting? STAT(tunnel_rx_errors); LOG(1, 0, t, " Out of sequence tunnel %u, (%u is not the expected %u)\n", t, ns, tunnel[t].nr); if (l) // Is this not a ZLB? controlnull(t); return; } // check sequence of this message { int skip = tunnel[t].window; // track how many in-window packets are still in queue // some to clear maybe? while (tunnel[t].controlc > 0 && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) { controlt *c = tunnel[t].controls; tunnel[t].controls = c->next; tunnel[t].controlc--; c->next = controlfree; controlfree = c; skip--; tunnel[t].try = 0; // we have progress } // receiver advance (do here so quoted correctly in any sends below) if (l) tunnel[t].nr = (ns + 1); if (skip < 0) skip = 0; if (skip < tunnel[t].controlc) { // some control packets can now be sent that were previous stuck out of window int tosend = tunnel[t].window - skip; controlt *c = tunnel[t].controls; while (c && skip) { c = c->next; skip--; } while (c && tosend) { tunnel[t].try = 0; // first send tunnelsend(c->buf, c->length, t); c = c->next; tosend--; } } if (!tunnel[t].controlc) tunnel[t].retry = 0; // caught up } if (l) { // if not a null message int result = 0; int error = 0; char *msg = 0; // Default disconnect cause/message on receipt of CDN. Set to // more specific value from attribute 1 (result code) or 46 // (disconnect cause) if present below. int disc_cause_set = 0; int disc_cause = TERM_NAS_REQUEST; char const *disc_reason = "Closed (Received CDN)."; // process AVPs while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP { uint16_t n = (ntohs(*(uint16_t *) p) & 0x3FF); uint8_t *b = p; uint8_t flags = *p; uint16_t mtype; if ((n > l) || (n < 6)) { LOG(1, s, t, "Invalid length in AVP\n"); STAT(tunnel_rx_errors); return; } p += n; // next l -= n; if (flags & 0x3C) // reserved bits, should be clear { LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b); fatal = flags; result = 2; // general error error = 3; // reserved field non-zero msg = 0; continue; // next } b += 2; if (*(uint16_t *) (b)) { LOG(2, s, t, "Unknown AVP vendor %u\n", ntohs(*(uint16_t *) (b))); fatal = flags; result = 2; // general error error = 6; // generic vendor-specific error msg = "unsupported vendor-specific"; continue; // next } b += 2; mtype = ntohs(*(uint16_t *) (b)); b += 2; n -= 6; if (flags & 0x40) { uint16_t orig_len; // handle hidden AVPs if (!*config->l2tp_secret) { LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n"); fatal = flags; result = 2; // general error error = 6; // generic vendor-specific error msg = "secret not specified"; continue; } if (!session[s].random_vector_length) { LOG(1, s, t, "Hidden AVP requested, but no random vector.\n"); fatal = flags; result = 2; // general error error = 6; // generic msg = "no random vector"; continue; } if (n < 8) { LOG(2, s, t, "Short hidden AVP.\n"); fatal = flags; result = 2; // general error error = 2; // length is wrong msg = 0; continue; } // Unhide the AVP unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length); orig_len = ntohs(*(uint16_t *) b); if (orig_len > n + 2) { LOG(1, s, t, "Original length %d too long in hidden AVP of length %d; wrong secret?\n", orig_len, n); fatal = flags; result = 2; // general error error = 2; // length is wrong msg = 0; continue; } b += 2; n = orig_len; } LOG(4, s, t, " AVP %u (%s) len %d%s%s\n", mtype, l2tp_avp_name(mtype), n, flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : ""); switch (mtype) { case 0: // message type message = ntohs(*(uint16_t *) b); mandatory = flags & 0x80; LOG(4, s, t, " Message type = %u (%s)\n", message, l2tp_code(message)); break; case 1: // result code { uint16_t rescode = ntohs(*(uint16_t *) b); char const *resdesc = "(unknown)"; char const *errdesc = NULL; int cause = 0; if (message == 4) { /* StopCCN */ resdesc = l2tp_stopccn_result_code(rescode); cause = TERM_LOST_SERVICE; } else if (message == 14) { /* CDN */ resdesc = l2tp_cdn_result_code(rescode); if (rescode == 1) cause = TERM_LOST_CARRIER; else cause = TERM_ADMIN_RESET; } LOG(4, s, t, " Result Code %u: %s\n", rescode, resdesc); if (n >= 4) { uint16_t errcode = ntohs(*(uint16_t *)(b + 2)); errdesc = l2tp_error_code(errcode); LOG(4, s, t, " Error Code %u: %s\n", errcode, errdesc); } if (n > 4) LOG(4, s, t, " Error String: %.*s\n", n-4, b+4); if (cause && disc_cause_set < mtype) // take cause from attrib 46 in preference { disc_cause_set = mtype; disc_reason = errdesc ? errdesc : resdesc; disc_cause = cause; } break; } break; case 2: // protocol version { version = ntohs(*(uint16_t *) (b)); LOG(4, s, t, " Protocol version = %u\n", version); if (version && version != 0x0100) { // allow 0.0 and 1.0 LOG(1, s, t, " Bad protocol version %04X\n", version); fatal = flags; result = 5; // unspported protocol version error = 0x0100; // supported version msg = 0; continue; // next } } break; case 3: // framing capabilities break; case 4: // bearer capabilities break; case 5: // tie breaker // We never open tunnels, so we don't care about tie breakers continue; case 6: // firmware revision break; case 7: // host name memset(tunnel[t].hostname, 0, sizeof(tunnel[t].hostname)); memcpy(tunnel[t].hostname, b, (n < sizeof(tunnel[t].hostname)) ? n : sizeof(tunnel[t].hostname) - 1); LOG(4, s, t, " Tunnel hostname = \"%s\"\n", tunnel[t].hostname); // TBA - to send to RADIUS break; case 8: // vendor name memset(tunnel[t].vendor, 0, sizeof(tunnel[t].vendor)); memcpy(tunnel[t].vendor, b, (n < sizeof(tunnel[t].vendor)) ? n : sizeof(tunnel[t].vendor) - 1); LOG(4, s, t, " Vendor name = \"%s\"\n", tunnel[t].vendor); break; case 9: // assigned tunnel tunnel[t].far = ntohs(*(uint16_t *) (b)); LOG(4, s, t, " Remote tunnel id = %u\n", tunnel[t].far); break; case 10: // rx window tunnel[t].window = ntohs(*(uint16_t *) (b)); if (!tunnel[t].window) tunnel[t].window = 1; // window of 0 is silly LOG(4, s, t, " rx window = %u\n", tunnel[t].window); break; case 11: // Request Challenge { LOG(4, s, t, " LAC requested CHAP authentication for tunnel\n"); if (message == 1) build_chap_response(b, 2, n, &sendchalresponse); else if (message == 2) build_chap_response(b, 3, n, &sendchalresponse); } break; case 13: // receive challenge Response if (tunnel[t].isremotelns) { recvchalresponse = calloc(17, 1); memcpy(recvchalresponse, b, (n < 17) ? n : 16); LOG(3, s, t, "received challenge response from REMOTE LNS\n"); } else // Why did they send a response? We never challenge. LOG(2, s, t, " received unexpected challenge response\n"); break; case 14: // assigned session asession = session[s].far = ntohs(*(uint16_t *) (b)); LOG(4, s, t, " assigned session = %u\n", asession); break; case 15: // call serial number LOG(4, s, t, " call serial number = %u\n", ntohl(*(uint32_t *)b)); break; case 18: // bearer type LOG(4, s, t, " bearer type = %u\n", ntohl(*(uint32_t *)b)); // TBA - for RADIUS break; case 19: // framing type LOG(4, s, t, " framing type = %u\n", ntohl(*(uint32_t *)b)); // TBA break; case 21: // called number memset(called, 0, sizeof(called)); memcpy(called, b, (n < sizeof(called)) ? n : sizeof(called) - 1); LOG(4, s, t, " Called <%s>\n", called); break; case 22: // calling number memset(calling, 0, sizeof(calling)); memcpy(calling, b, (n < sizeof(calling)) ? n : sizeof(calling) - 1); LOG(4, s, t, " Calling <%s>\n", calling); break; case 23: // subtype break; case 24: // tx connect speed if (n == 4) { session[s].tx_connect_speed = ntohl(*(uint32_t *)b); } else { // AS5300s send connect speed as a string char tmp[30]; memset(tmp, 0, sizeof(tmp)); memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1); session[s].tx_connect_speed = atol(tmp); } LOG(4, s, t, " TX connect speed <%u>\n", session[s].tx_connect_speed); break; case 38: // rx connect speed if (n == 4) { session[s].rx_connect_speed = ntohl(*(uint32_t *)b); } else { // AS5300s send connect speed as a string char tmp[30]; memset(tmp, 0, sizeof(tmp)); memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1); session[s].rx_connect_speed = atol(tmp); } LOG(4, s, t, " RX connect speed <%u>\n", session[s].rx_connect_speed); break; case 25: // Physical Channel ID { uint32_t tmp = ntohl(*(uint32_t *) b); LOG(4, s, t, " Physical Channel ID <%X>\n", tmp); break; } case 29: // Proxy Authentication Type { uint16_t atype = ntohs(*(uint16_t *)b); LOG(4, s, t, " Proxy Auth Type %u (%s)\n", atype, ppp_auth_type(atype)); break; } case 30: // Proxy Authentication Name { char authname[64]; memset(authname, 0, sizeof(authname)); memcpy(authname, b, (n < sizeof(authname)) ? n : sizeof(authname) - 1); LOG(4, s, t, " Proxy Auth Name (%s)\n", authname); break; } case 31: // Proxy Authentication Challenge { LOG(4, s, t, " Proxy Auth Challenge\n"); break; } case 32: // Proxy Authentication ID { uint16_t authid = ntohs(*(uint16_t *)(b)); LOG(4, s, t, " Proxy Auth ID (%u)\n", authid); break; } case 33: // Proxy Authentication Response LOG(4, s, t, " Proxy Auth Response\n"); break; case 27: // last sent lcp { // find magic number uint8_t *p = b, *e = p + n; while (p + 1 < e && p[1] && p + p[1] <= e) { if (*p == 5 && p[1] == 6) // Magic-Number amagic = ntohl(*(uint32_t *) (p + 2)); else if (*p == 7) // Protocol-Field-Compression aflags |= SESSION_PFC; else if (*p == 8) // Address-and-Control-Field-Compression aflags |= SESSION_ACFC; p += p[1]; } } break; case 28: // last recv lcp confreq break; case 26: // Initial Received LCP CONFREQ break; case 39: // seq required - we control it as an LNS anyway... break; case 36: // Random Vector LOG(4, s, t, " Random Vector received. Enabled AVP Hiding.\n"); memset(session[s].random_vector, 0, sizeof(session[s].random_vector)); if (n > sizeof(session[s].random_vector)) n = sizeof(session[s].random_vector); memcpy(session[s].random_vector, b, n); session[s].random_vector_length = n; break; case 46: // ppp disconnect cause if (n >= 5) { uint16_t code = ntohs(*(uint16_t *) b); uint16_t proto = ntohs(*(uint16_t *) (b + 2)); uint8_t dir = *(b + 4); LOG(4, s, t, " PPP disconnect cause " "(code=%u, proto=%04X, dir=%u, msg=\"%.*s\")\n", code, proto, dir, n - 5, b + 5); disc_cause_set = mtype; switch (code) { case 1: // admin disconnect disc_cause = TERM_ADMIN_RESET; disc_reason = "Administrative disconnect"; break; case 3: // lcp terminate if (dir != 2) break; // 1=peer (LNS), 2=local (LAC) disc_cause = TERM_USER_REQUEST; disc_reason = "Normal disconnection"; break; case 4: // compulsory encryption unavailable if (dir != 1) break; // 1=refused by peer, 2=local disc_cause = TERM_USER_ERROR; disc_reason = "Compulsory encryption refused"; break; case 5: // lcp: fsm timeout disc_cause = TERM_PORT_ERROR; disc_reason = "LCP: FSM timeout"; break; case 6: // lcp: no recognisable lcp packets received disc_cause = TERM_PORT_ERROR; disc_reason = "LCP: no recognisable LCP packets"; break; case 7: // lcp: magic-no error (possibly looped back) disc_cause = TERM_PORT_ERROR; disc_reason = "LCP: magic-no error (possible loop)"; break; case 8: // lcp: echo request timeout disc_cause = TERM_PORT_ERROR; disc_reason = "LCP: echo request timeout"; break; case 13: // auth: fsm timeout disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = "Authentication: FSM timeout"; break; case 15: // auth: unacceptable auth protocol disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = "Unacceptable authentication protocol"; break; case 16: // auth: authentication failed disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = "Authentication failed"; break; case 17: // ncp: fsm timeout disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = "NCP: FSM timeout"; break; case 18: // ncp: no ncps available disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = "NCP: no NCPs available"; break; case 19: // ncp: failure to converge on acceptable address disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = (dir == 1) ? "NCP: too many Configure-Naks received from peer" : "NCP: too many Configure-Naks sent to peer"; break; case 20: // ncp: user not permitted to use any address disc_cause = TERM_SERVICE_UNAVAILABLE; disc_reason = (dir == 1) ? "NCP: local link address not acceptable to peer" : "NCP: remote link address not acceptable"; break; } } break; default: { static char e[] = "unknown AVP 0xXXXX"; LOG(2, s, t, " Unknown AVP type %u\n", mtype); fatal = flags; result = 2; // general error error = 8; // unknown mandatory AVP sprintf((msg = e) + 14, "%04x", mtype); continue; // next } } } // process message if (fatal & 0x80) tunnelshutdown(t, "Invalid mandatory AVP", result, error, msg); else switch (message) { case 1: // SCCRQ - Start Control Connection Request tunnel[t].state = TUNNELOPENING; LOG(3, s, t, "Received SCCRQ\n"); if (main_quit != QUIT_SHUTDOWN) { LOG(3, s, t, "sending SCCRP\n"); controlt *c = controlnew(2); // sending SCCRP control16(c, 2, version, 1); // protocol version control32(c, 3, 3, 1); // framing controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Send Challenge response control16(c, 9, t, 1); // assigned tunnel controladd(c, 0, t); // send the resply } else { tunnelshutdown(t, "Shutting down", 6, 0, 0); } break; case 2: // SCCRP tunnel[t].state = TUNNELOPEN; tunnel[t].lastrec = time_now; LOG(3, s, t, "Received SCCRP\n"); if (main_quit != QUIT_SHUTDOWN) { if (tunnel[t].isremotelns && recvchalresponse) { hasht hash; lac_calc_rlns_auth(t, 2, hash); // id = 2 (SCCRP) // check authenticator if (memcmp(hash, recvchalresponse, 16) == 0) { LOG(3, s, t, "sending SCCCN to REMOTE LNS\n"); controlt *c = controlnew(3); // sending SCCCN controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name controls(c, 8, Vendor_name, 1); // Vendor name control16(c, 2, version, 1); // protocol version control32(c, 3, 3, 1); // framing Capabilities if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Challenge response control16(c, 9, t, 1); // assigned tunnel controladd(c, 0, t); // send } else { tunnelshutdown(t, "Bad chap response from REMOTE LNS", 4, 0, 0); } } } else { tunnelshutdown(t, "Shutting down", 6, 0, 0); } break; case 3: // SCCN LOG(3, s, t, "Received SCCN\n"); tunnel[t].state = TUNNELOPEN; tunnel[t].lastrec = time_now; controlnull(t); // ack break; case 4: // StopCCN LOG(3, s, t, "Received StopCCN\n"); controlnull(t); // ack tunnelshutdown(t, "Stopped", 0, 0, 0); // Shut down cleanly break; case 6: // HELLO LOG(3, s, t, "Received HELLO\n"); controlnull(t); // simply ACK break; case 7: // OCRQ // TBA LOG(3, s, t, "Received OCRQ\n"); break; case 8: // OCRO // TBA LOG(3, s, t, "Received OCRO\n"); break; case 9: // OCCN // TBA LOG(3, s, t, "Received OCCN\n"); break; case 10: // ICRQ LOG(3, s, t, "Received ICRQ\n"); if (sessionfree && main_quit != QUIT_SHUTDOWN) { controlt *c = controlnew(11); // ICRP LOG(3, s, t, "Sending ICRP\n"); s = sessionfree; sessionfree = session[s].next; memset(&session[s], 0, sizeof(session[s])); if (s > config->cluster_highest_sessionid) config->cluster_highest_sessionid = s; session[s].opened = time_now; session[s].tunnel = t; session[s].far = asession; session[s].last_packet = session[s].last_data = time_now; LOG(3, s, t, "New session (%u/%u)\n", tunnel[t].far, session[s].far); control16(c, 14, s, 1); // assigned session controladd(c, asession, t); // send the reply strncpy(session[s].called, called, sizeof(session[s].called) - 1); strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1); session[s].ppp.phase = Establish; session[s].ppp.lcp = Starting; STAT(session_created); break; } { controlt *c = controlnew(14); // CDN LOG(3, s, t, "Sending CDN\n"); if (!sessionfree) { STAT(session_overflow); LOG(1, 0, t, "No free sessions\n"); control16(c, 1, 4, 0); // temporary lack of resources } else control16(c, 1, 2, 7); // shutting down, try another controladd(c, asession, t); // send the message } return; case 11: // ICRP LOG(3, s, t, "Received ICRP\n"); if (session[s].forwardtosession) { controlt *c = controlnew(12); // ICCN session[s].opened = time_now; session[s].tunnel = t; session[s].far = asession; session[s].last_packet = session[s].last_data = time_now; control32(c, 19, 1, 1); // Framing Type control32(c, 24, 10000000, 1); // Tx Connect Speed controladd(c, asession, t); // send the message LOG(3, s, t, "Sending ICCN\n"); } break; case 12: // ICCN LOG(3, s, t, "Received ICCN\n"); if (amagic == 0) amagic = time_now; session[s].magic = amagic; // set magic number session[s].flags = aflags; // set flags received session[s].mru = PPPoE_MRU; // default controlnull(t); // ack // start LCP sess_local[s].lcp_authtype = config->radius_authprefer; sess_local[s].ppp_mru = MRU; // Set multilink options before sending initial LCP packet sess_local[s].mp_mrru = 1614; sess_local[s].mp_epdis = ntohl(config->iftun_address ? config->iftun_address : my_address); sendlcp(s, t); change_state(s, lcp, RequestSent); break; case 14: // CDN LOG(3, s, t, "Received CDN\n"); controlnull(t); // ack sessionshutdown(s, disc_reason, CDN_NONE, disc_cause); break; case 0xFFFF: LOG(1, s, t, "Missing message type\n"); break; default: STAT(tunnel_rx_errors); if (mandatory) tunnelshutdown(t, "Unknown message type", 2, 6, "unknown message type"); else LOG(1, s, t, "Unknown message type %u\n", message); break; } if (sendchalresponse) free(sendchalresponse); if (recvchalresponse) free(recvchalresponse); cluster_send_tunnel(t); } else { LOG(4, s, t, " Got a ZLB ack\n"); } } else { // data uint16_t proto; LOG_HEX(5, "Receive Tunnel Data", p, l); if (l > 2 && p[0] == 0xFF && p[1] == 0x03) { // HDLC address header, discard p += 2; l -= 2; } if (l < 2) { LOG(1, s, t, "Short ppp length %d\n", l); STAT(tunnel_rx_errors); return; } if (*p & 1) { proto = *p++; l--; } else { proto = ntohs(*(uint16_t *) p); p += 2; l -= 2; } if (session[s].forwardtosession) { LOG(5, s, t, "Forwarding data session to session %u\n", session[s].forwardtosession); // Forward to LAC/BAS or Remote LNS session lac_session_forward(buf, len, s, proto, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } else if (config->auth_tunnel_change_addr_src) { if (tunnel[t].ip != ntohl(addr->sin_addr.s_addr) && tunnel[t].port == ntohs(addr->sin_port)) { // The remotes BAS are a clustered l2tpns server and the source IP has changed LOG(5, s, t, "The tunnel IP source (%s) has changed by new IP (%s)\n", fmtaddr(htonl(tunnel[t].ip), 0), fmtaddr(addr->sin_addr.s_addr, 0)); tunnel[t].ip = ntohl(addr->sin_addr.s_addr); } } if (s && !session[s].opened) // Is something wrong?? { if (!config->cluster_iam_master) { // Pass it off to the master to deal with.. master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } LOG(1, s, t, "UDP packet contains session which is not opened. Dropping packet.\n"); STAT(tunnel_rx_errors); return; } if (proto == PPPPAP) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processpap(s, t, p, l); } else if (proto == PPPCHAP) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processchap(s, t, p, l); } else if (proto == PPPLCP) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processlcp(s, t, p, l); } else if (proto == PPPIPCP) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processipcp(s, t, p, l); } else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0]) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processipv6cp(s, t, p, l); } else if (proto == PPPCCP) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processccp(s, t, p, l); } else if (proto == PPPIP) { if (session[s].die) { LOG(4, s, t, "Session %u is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processipin(s, t, p, l); } else if (proto == PPPMP) { if (session[s].die) { LOG(4, s, t, "Session %u is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } session[s].last_packet = session[s].last_data = time_now; if (!config->cluster_iam_master) { // The fragments reconstruction is managed by the Master. master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } processmpin(s, t, p, l); } else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) { if (session[s].die) { LOG(4, s, t, "Session %u is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } if (!config->cluster_iam_master) { // Check if DhcpV6, IP dst: FF02::1:2, Src Port 0x0222 (546), Dst Port 0x0223 (547) if (*(p + 6) == 17 && *(p + 24) == 0xFF && *(p + 25) == 2 && *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 && *(uint16_t *)(p + 34) == 0 && *(p + 36) == 0 && *(p + 37) == 1 && *(p + 38) == 0 && *(p + 39) == 2 && *(p + 40) == 2 && *(p + 41) == 0x22 && *(p + 42) == 2 && *(p + 43) == 0x23) { // DHCPV6 must be managed by the Master. master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } } processipv6in(s, t, p, l); } else if (session[s].ppp.lcp == Opened) { session[s].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; } protoreject(s, t, p, l, proto); } else { LOG(2, s, t, "Unknown PPP protocol 0x%04X received in LCP %s state\n", proto, ppp_state(session[s].ppp.lcp)); } } } // read and process packet on tun // (i.e. this routine writes to buf[-8]). static void processtun(uint8_t * buf, int len) { LOG_HEX(5, "Receive TUN Data", buf, len); STAT(tun_rx_packets); INC_STAT(tun_rx_bytes, len); CSTAT(processtun); eth_rx_pkt++; eth_rx += len; if (len < 22) { LOG(1, 0, 0, "Short tun packet %d bytes\n", len); STAT(tun_rx_errors); return; } if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IPv4 processipout(buf, len); else if (*(uint16_t *) (buf + 2) == htons(PKTIPV6) // IPV6 && config->ipv6_prefix.s6_addr[0]) processipv6out(buf, len); // Else discard. } // Handle retries, timeouts. Runs every 1/10th sec, want to ensure // that we look at the whole of the tunnel, radius and session tables // every second static void regular_cleanups(double period) { // Next tunnel, radius and session to check for actions on. static tunnelidt t = 0; static int r = 0; static sessionidt s = 0; int t_actions = 0; int r_actions = 0; int s_actions = 0; int t_slice; int r_slice; int s_slice; int i; int a; // divide up tables into slices based on the last run t_slice = config->cluster_highest_tunnelid * period; r_slice = (MAXRADIUS - 1) * period; s_slice = config->cluster_highest_sessionid * period; if (t_slice < 1) t_slice = 1; else if (t_slice > config->cluster_highest_tunnelid) t_slice = config->cluster_highest_tunnelid; if (r_slice < 1) r_slice = 1; else if (r_slice > (MAXRADIUS - 1)) r_slice = MAXRADIUS - 1; if (s_slice < 1) s_slice = 1; else if (s_slice > config->cluster_highest_sessionid) s_slice = config->cluster_highest_sessionid; LOG(4, 0, 0, "Begin regular cleanup (last %f seconds ago)\n", period); for (i = 0; i < t_slice; i++) { t++; if (t > config->cluster_highest_tunnelid) t = 1; if (t == TUNNEL_ID_PPPOE) continue; // check for expired tunnels if (tunnel[t].die && tunnel[t].die <= TIME) { STAT(tunnel_timeout); tunnelkill(t, "Expired"); t_actions++; continue; } // check for message resend if (tunnel[t].retry && tunnel[t].controlc) { // resend pending messages as timeout on reply if (tunnel[t].retry <= TIME) { controlt *c = tunnel[t].controls; uint16_t w = tunnel[t].window; tunnel[t].try++; // another try if (tunnel[t].try > 5) tunnelkill(t, "Timeout on control message"); // game over else while (c && w--) { tunnelsend(c->buf, c->length, t); c = c->next; } t_actions++; } } // Send hello if (tunnel[t].state == TUNNELOPEN && !tunnel[t].controlc && (time_now - tunnel[t].lastrec) > 60) { if (!config->disable_sending_hello) { controlt *c = controlnew(6); // sending HELLO controladd(c, 0, t); // send the message LOG(3, 0, t, "Sending HELLO message\n"); t_actions++; } } // Check for tunnel changes requested from the CLI if ((a = cli_tunnel_actions[t].action)) { cli_tunnel_actions[t].action = 0; if (a & CLI_TUN_KILL) { LOG(2, 0, t, "Dropping tunnel by CLI\n"); tunnelshutdown(t, "Requested by administrator", 1, 0, 0); t_actions++; } } } for (i = 0; i < r_slice; i++) { r++; if (r >= MAXRADIUS) r = 1; if (!radius[r].state) continue; if (radius[r].retry <= TIME) { radiusretry(r); r_actions++; } } for (i = 0; i < s_slice; i++) { s++; if (s > config->cluster_highest_sessionid) s = 1; if (!session[s].opened) // Session isn't in use continue; // check for expired sessions if (session[s].die) { if (session[s].die <= TIME) { sessionkill(s, "Expired"); s_actions++; } continue; } // PPP timeouts if (sess_local[s].lcp.restart <= time_now) { int next_state = session[s].ppp.lcp; switch (session[s].ppp.lcp) { case RequestSent: case AckReceived: next_state = RequestSent; case AckSent: if (sess_local[s].lcp.conf_sent < config->ppp_max_configure) { LOG(3, s, session[s].tunnel, "No ACK for LCP ConfigReq... resending\n"); sendlcp(s, session[s].tunnel); change_state(s, lcp, next_state); } else { sessionshutdown(s, "No response to LCP ConfigReq.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); STAT(session_timeout); } s_actions++; } if (session[s].die) continue; } if (sess_local[s].ipcp.restart <= time_now) { int next_state = session[s].ppp.ipcp; switch (session[s].ppp.ipcp) { case RequestSent: case AckReceived: next_state = RequestSent; case AckSent: if (sess_local[s].ipcp.conf_sent < config->ppp_max_configure) { LOG(3, s, session[s].tunnel, "No ACK for IPCP ConfigReq... resending\n"); sendipcp(s, session[s].tunnel); change_state(s, ipcp, next_state); } else { sessionshutdown(s, "No response to IPCP ConfigReq.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); STAT(session_timeout); } s_actions++; } if (session[s].die) continue; } if (sess_local[s].ipv6cp.restart <= time_now) { int next_state = session[s].ppp.ipv6cp; switch (session[s].ppp.ipv6cp) { case RequestSent: case AckReceived: next_state = RequestSent; case AckSent: if (sess_local[s].ipv6cp.conf_sent < config->ppp_max_configure) { LOG(3, s, session[s].tunnel, "No ACK for IPV6CP ConfigReq... resending\n"); sendipv6cp(s, session[s].tunnel); change_state(s, ipv6cp, next_state); } else { LOG(3, s, session[s].tunnel, "No ACK for IPV6CP ConfigReq\n"); change_state(s, ipv6cp, Stopped); } s_actions++; } } if (sess_local[s].ccp.restart <= time_now) { int next_state = session[s].ppp.ccp; switch (session[s].ppp.ccp) { case RequestSent: case AckReceived: next_state = RequestSent; case AckSent: if (sess_local[s].ccp.conf_sent < config->ppp_max_configure) { LOG(3, s, session[s].tunnel, "No ACK for CCP ConfigReq... resending\n"); sendccp(s, session[s].tunnel); change_state(s, ccp, next_state); } else { LOG(3, s, session[s].tunnel, "No ACK for CCP ConfigReq\n"); change_state(s, ccp, Stopped); } s_actions++; } } // Drop sessions who have not responded within IDLE_ECHO_TIMEOUT seconds if (session[s].last_packet && (time_now - session[s].last_packet >= config->idle_echo_timeout)) { sessionshutdown(s, "No response to LCP ECHO requests.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); STAT(session_timeout); s_actions++; continue; } // No data in ECHO_TIMEOUT seconds, send LCP ECHO if (session[s].ppp.phase >= Establish && ((!config->ppp_keepalive) || (time_now - session[s].last_packet >= config->echo_timeout)) && (time_now - sess_local[s].last_echo >= ECHO_TIMEOUT)) { uint8_t b[MAXETHER]; uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP, 1, 0, 0); if (!q) continue; *q = EchoReq; *(uint8_t *)(q + 1) = (time_now % 255); // ID *(uint16_t *)(q + 2) = htons(8); // Length *(uint32_t *)(q + 4) = session[s].ppp.lcp == Opened ? htonl(session[s].magic) : 0; // Magic Number LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", (int)(time_now - session[s].last_packet)); tunnelsend(b, (q - b) + 8, session[s].tunnel); // send it sess_local[s].last_echo = time_now; s_actions++; } // Drop sessions who have reached session_timeout seconds if (session[s].session_timeout) { bundleidt bid = session[s].bundle; if (bid) { if (time_now - bundle[bid].last_check >= 1) { bundle[bid].online_time += (time_now - bundle[bid].last_check) * bundle[bid].num_of_links; bundle[bid].last_check = time_now; if (bundle[bid].online_time >= session[s].session_timeout) { int ses; for (ses = bundle[bid].num_of_links - 1; ses >= 0; ses--) { sessionshutdown(bundle[bid].members[ses], "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); s_actions++; continue; } } } } else if (time_now - session[s].opened >= session[s].session_timeout) { sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); s_actions++; continue; } } // Drop sessions who have reached idle_timeout seconds if (session[s].last_data && session[s].idle_timeout && (time_now - session[s].last_data >= session[s].idle_timeout)) { sessionshutdown(s, "Idle Timeout Reached", CDN_ADMIN_DISC, TERM_IDLE_TIMEOUT); STAT(session_timeout); s_actions++; continue; } // Check for actions requested from the CLI if ((a = cli_session_actions[s].action)) { int send = 0; cli_session_actions[s].action = 0; if (a & CLI_SESS_KILL) { LOG(2, s, session[s].tunnel, "Dropping session by CLI\n"); sessionshutdown(s, "Requested by administrator.", CDN_ADMIN_DISC, TERM_ADMIN_RESET); a = 0; // dead, no need to check for other actions s_actions++; } if (a & CLI_SESS_NOSNOOP) { LOG(2, s, session[s].tunnel, "Unsnooping session by CLI\n"); session[s].snoop_ip = 0; session[s].snoop_port = 0; s_actions++; send++; } else if (a & CLI_SESS_SNOOP) { LOG(2, s, session[s].tunnel, "Snooping session by CLI (to %s:%u)\n", fmtaddr(cli_session_actions[s].snoop_ip, 0), cli_session_actions[s].snoop_port); session[s].snoop_ip = cli_session_actions[s].snoop_ip; session[s].snoop_port = cli_session_actions[s].snoop_port; s_actions++; send++; } if (a & CLI_SESS_NOTHROTTLE) { LOG(2, s, session[s].tunnel, "Un-throttling session by CLI\n"); throttle_session(s, 0, 0); s_actions++; send++; } else if (a & CLI_SESS_THROTTLE) { LOG(2, s, session[s].tunnel, "Throttling session by CLI (to %dkb/s up and %dkb/s down)\n", cli_session_actions[s].throttle_in, cli_session_actions[s].throttle_out); throttle_session(s, cli_session_actions[s].throttle_in, cli_session_actions[s].throttle_out); s_actions++; send++; } if (a & CLI_SESS_NOFILTER) { LOG(2, s, session[s].tunnel, "Un-filtering session by CLI\n"); filter_session(s, 0, 0); s_actions++; send++; } else if (a & CLI_SESS_FILTER) { LOG(2, s, session[s].tunnel, "Filtering session by CLI (in=%d, out=%d)\n", cli_session_actions[s].filter_in, cli_session_actions[s].filter_out); filter_session(s, cli_session_actions[s].filter_in, cli_session_actions[s].filter_out); s_actions++; send++; } if (send) cluster_send_session(s); } // RADIUS interim accounting if (config->radius_accounting && config->radius_interim > 0 && session[s].ip && !session[s].walled_garden && !sess_local[s].radius // RADIUS already in progress && time_now - sess_local[s].last_interim >= config->radius_interim && session[s].flags & SESSION_STARTED) { int rad = radiusnew(s); if (!rad) { LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n"); STAT(radius_overflow); continue; } LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n", session[s].user, session[s].unique_id); radiussend(rad, RADIUSINTERIM); sess_local[s].last_interim = time_now; s_actions++; } } LOG(4, 0, 0, "End regular cleanup: checked %d/%d/%d tunnels/radius/sessions; %d/%d/%d actions\n", t_slice, r_slice, s_slice, t_actions, r_actions, s_actions); } // // Are we in the middle of a tunnel update, or radius // requests?? // static int still_busy(void) { int i; static clockt last_talked = 0; static clockt start_busy_wait = 0; #ifdef BGP static time_t stopped_bgp = 0; if (bgp_configured) { if (!stopped_bgp) { LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY); for (i = 0; i < BGP_NUM_PEERS; i++) if (bgp_peers[i].state == Established) bgp_stop(&bgp_peers[i]); stopped_bgp = time_now; if (!config->cluster_iam_master) { // we don't want to become master cluster_send_ping(0); return 1; } } if (!config->cluster_iam_master && time_now < (stopped_bgp + QUIT_DELAY)) return 1; } #endif /* BGP */ if (!config->cluster_iam_master) return 0; if (main_quit == QUIT_SHUTDOWN) { static int dropped = 0; if (!dropped) { int i; LOG(1, 0, 0, "Dropping sessions and tunnels\n"); for (i = 1; i < MAXTUNNEL; i++) if (tunnel[i].ip || tunnel[i].state) tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0); dropped = 1; } } if (start_busy_wait == 0) start_busy_wait = TIME; for (i = config->cluster_highest_tunnelid ; i > 0 ; --i) { if (!tunnel[i].controlc) continue; if (last_talked != TIME) { LOG(2, 0, 0, "Tunnel %u still has un-acked control messages.\n", i); last_talked = TIME; } return 1; } // We stop waiting for radius after BUSY_WAIT_TIME 1/10th seconds if (abs(TIME - start_busy_wait) > BUSY_WAIT_TIME) { LOG(1, 0, 0, "Giving up waiting for RADIUS to be empty. Shutting down anyway.\n"); return 0; } for (i = 1; i < MAXRADIUS; i++) { if (radius[i].state == RADIUSNULL) continue; if (radius[i].state == RADIUSWAIT) continue; if (last_talked != TIME) { LOG(2, 0, 0, "Radius session %u is still busy (sid %u)\n", i, radius[i].session); last_talked = TIME; } return 1; } return 0; } #ifdef HAVE_EPOLL # include #else # define FAKE_EPOLL_IMPLEMENTATION /* include the functions */ # include "fake_epoll.h" #endif // the base set of fds polled: cli, cluster, tun, udp (MAX_UDPFD), control, dae, netlink, udplac, pppoedisc, pppoesess #define BASE_FDS (9 + MAX_UDPFD) // additional polled fds #ifdef BGP # define EXTRA_FDS BGP_NUM_PEERS #else # define EXTRA_FDS 0 #endif // main loop - gets packets on tun or udp and processes them static void mainloop(void) { int i, j; uint8_t buf[65536]; uint8_t *p = buf + 32; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD) // and the forwarded pppoe session int size_bufp = sizeof(buf) - 32; clockt next_cluster_ping = 0; // send initial ping immediately struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS]; int maxevent = sizeof(events)/sizeof(*events); if ((epollfd = epoll_create(maxevent)) < 0) { LOG(0, 0, 0, "epoll_create failed: %s\n", strerror(errno)); exit(1); } LOG(4, 0, 0, "Beginning of main loop. clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d , udplacfd=%d, pppoefd=%d, pppoesessfd=%d\n", clifd, cluster_sockfd, tunfd, udpfd[0], controlfd, daefd, nlfd, udplacfd, pppoediscfd, pppoesessfd); /* setup our fds to poll for input */ { static struct event_data d[BASE_FDS]; struct epoll_event e; e.events = EPOLLIN; i = 0; if (clifd >= 0) { d[i].type = FD_TYPE_CLI; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e); } d[i].type = FD_TYPE_CLUSTER; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e); d[i].type = FD_TYPE_TUN; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e); d[i].type = FD_TYPE_CONTROL; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e); d[i].type = FD_TYPE_DAE; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, daefd, &e); d[i].type = FD_TYPE_NETLINK; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, nlfd, &e); d[i].type = FD_TYPE_PPPOEDISC; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoediscfd, &e); d[i].type = FD_TYPE_PPPOESESS; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoesessfd, &e); for (j = 0; j < config->nbudpfd; j++) { d[i].type = FD_TYPE_UDP; d[i].index = j; e.data.ptr = &d[i++]; epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd[j], &e); } } #ifdef BGP signal(SIGPIPE, SIG_IGN); bgp_setup(config->as_number); if (config->bind_address) bgp_add_route(config->bind_address, 0xffffffff); for (i = 0; i < BGP_NUM_PEERS; i++) { if (config->neighbour[i].name[0]) bgp_start(&bgp_peers[i], config->neighbour[i].name, config->neighbour[i].as, config->neighbour[i].keepalive, config->neighbour[i].hold, config->neighbour[i].update_source, 0); /* 0 = routing disabled */ } #endif /* BGP */ while (!main_quit || still_busy()) { int more = 0; int n; if (main_reload) { main_reload = 0; read_config_file(); config->reload_config++; } if (config->reload_config) { config->reload_config = 0; update_config(); } #ifdef BGP bgp_set_poll(); #endif /* BGP */ n = epoll_wait(epollfd, events, maxevent, 100); // timeout 100ms (1/10th sec) STAT(select_called); TIME = now(NULL); if (n < 0) { if (errno == EINTR || errno == ECHILD) // EINTR was clobbered by sigchild_handler() continue; LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno)); break; // exit } if (n) { struct sockaddr_in addr; struct in_addr local; socklen_t alen; int c, s; int udp_ready[MAX_UDPFD + 1] = INIT_TABUDPVAR; int pppoesess_ready = 0; int pppoesess_pkts = 0; int tun_ready = 0; int cluster_ready = 0; int udp_pkts[MAX_UDPFD + 1] = INIT_TABUDPVAR; int tun_pkts = 0; int cluster_pkts = 0; #ifdef BGP uint32_t bgp_events[BGP_NUM_PEERS]; memset(bgp_events, 0, sizeof(bgp_events)); #endif /* BGP */ for (c = n, i = 0; i < c; i++) { struct event_data *d = events[i].data.ptr; switch (d->type) { case FD_TYPE_CLI: // CLI connections { int cli; alen = sizeof(addr); if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0) { cli_do(cli); close(cli); } else LOG(0, 0, 0, "accept error: %s\n", strerror(errno)); n--; break; } // these are handled below, with multiple interleaved reads case FD_TYPE_CLUSTER: cluster_ready++; break; case FD_TYPE_TUN: tun_ready++; break; case FD_TYPE_UDP: udp_ready[d->index]++; break; case FD_TYPE_PPPOESESS: pppoesess_ready++; break; case FD_TYPE_PPPOEDISC: // pppoe discovery s = read(pppoediscfd, p, size_bufp); if (s > 0) process_pppoe_disc(p, s); n--; break; case FD_TYPE_CONTROL: // nsctl commands alen = sizeof(addr); s = recvfromto(controlfd, p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local); if (s > 0) processcontrol(p, s, &addr, alen, &local); n--; break; case FD_TYPE_DAE: // DAE requests alen = sizeof(addr); s = recvfromto(daefd, p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local); if (s > 0) processdae(p, s, &addr, alen, &local); n--; break; case FD_TYPE_RADIUS: // RADIUS response alen = sizeof(addr); s = recvfrom(radfds[d->index], p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen); if (s >= 0 && config->cluster_iam_master) { if (addr.sin_addr.s_addr == config->radiusserver[0] || addr.sin_addr.s_addr == config->radiusserver[1]) processrad(p, s, d->index); else LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n", fmtaddr(addr.sin_addr.s_addr, 0)); } n--; break; #ifdef BGP case FD_TYPE_BGP: bgp_events[d->index] = events[i].events; n--; break; #endif /* BGP */ case FD_TYPE_NETLINK: { struct nlmsghdr *nh = (struct nlmsghdr *)p; s = netlink_recv(p, size_bufp); if (nh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *errmsg = NLMSG_DATA(nh); if (errmsg->error) { if (errmsg->msg.nlmsg_seq < min_initok_nlseqnum) { LOG(0, 0, 0, "Got a fatal netlink error (while %s): %s\n", tun_nl_phase_msg[nh->nlmsg_seq], strerror(-errmsg->error)); exit(1); } else LOG(0, 0, 0, "Got a netlink error: %s\n", strerror(-errmsg->error)); } // else it's a ack } else LOG(1, 0, 0, "Got a unknown netlink message: type %d seq %d flags %d\n", nh->nlmsg_type, nh->nlmsg_seq, nh->nlmsg_flags); n--; break; } default: LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type); } } #ifdef BGP bgp_process(bgp_events); #endif /* BGP */ for (c = 0; n && c < config->multi_read_count; c++) { for (j = 0; j < config->nbudpfd; j++) { // L2TP and L2TP REMOTE LNS if (udp_ready[j]) { alen = sizeof(addr); if ((s = recvfrom(udpfd[j], p, size_bufp, 0, (void *) &addr, &alen)) > 0) { processudp(p, s, &addr, j); udp_pkts[j]++; } else { udp_ready[j] = 0; n--; } } } // incoming IP if (tun_ready) { if ((s = read(tunfd, p, size_bufp)) > 0) { processtun(p, s); tun_pkts++; } else { tun_ready = 0; n--; } } // pppoe session if (pppoesess_ready) { if ((s = read(pppoesessfd, p, size_bufp)) > 0) { process_pppoe_sess(p, s); pppoesess_pkts++; } else { pppoesess_ready = 0; n--; } } // cluster if (cluster_ready) { alen = sizeof(addr); if ((s = recvfrom(cluster_sockfd, p, size_bufp, MSG_WAITALL, (void *) &addr, &alen)) > 0) { processcluster(p, s, addr.sin_addr.s_addr); cluster_pkts++; } else { cluster_ready = 0; n--; } } } if (udp_pkts[0] > 1 || tun_pkts > 1 || cluster_pkts > 1) STAT(multi_read_used); if (c >= config->multi_read_count) { LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster and %d pppoe packets\n", config->multi_read_count, udp_pkts[0], tun_pkts, cluster_pkts, pppoesess_pkts); STAT(multi_read_exceeded); more++; } } #ifdef BGP else /* no event received, but timers could still have expired */ bgp_process_peers_timers(); #endif /* BGP */ if (time_changed) { double Mbps = 1024.0 * 1024.0 / 8 * time_changed; // Log current traffic stats snprintf(config->bandwidth, sizeof(config->bandwidth), "UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%u OUT:%u", (udp_rx / Mbps), (eth_tx / Mbps), (eth_rx / Mbps), (udp_tx / Mbps), ((udp_tx + udp_rx + eth_tx + eth_rx) / Mbps), udp_rx_pkt / time_changed, eth_rx_pkt / time_changed); udp_tx = udp_rx = 0; udp_rx_pkt = eth_rx_pkt = 0; eth_tx = eth_rx = 0; time_changed = 0; if (config->dump_speed) printf("%s\n", config->bandwidth); // Update the internal time counter strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); { // Run timer hooks struct param_timer p = { time_now }; run_plugins(PLUGIN_TIMER, &p); } } // Runs on every machine (master and slaves). if (next_cluster_ping <= TIME) { // Check to see which of the cluster is still alive.. cluster_send_ping(basetime); // Only does anything if we're a slave cluster_check_master(); // ditto. cluster_heartbeat(); // Only does anything if we're a master. cluster_check_slaves(); // ditto. master_update_counts(); // If we're a slave, send our byte counters to our master. if (config->cluster_iam_master && !config->cluster_iam_uptodate) next_cluster_ping = TIME + 1; // out-of-date slaves, do fast updates else next_cluster_ping = TIME + config->cluster_hb_interval; } if (!config->cluster_iam_master) continue; // Run token bucket filtering queue.. // Only run it every 1/10th of a second. { static clockt last_run = 0; if (last_run != TIME) { last_run = TIME; tbf_run_timer(); } } // Handle timeouts, retries etc. { static double last_clean = 0; double this_clean; double diff; TIME = now(&this_clean); diff = this_clean - last_clean; // Run during idle time (after we've handled // all incoming packets) or every 1/10th sec if (!more || diff > 0.1) { regular_cleanups(diff); last_clean = this_clean; } } if (*config->accounting_dir) { static clockt next_acct = 0; static clockt next_shut_acct = 0; if (next_acct <= TIME) { // Dump accounting data next_acct = TIME + ACCT_TIME; next_shut_acct = TIME + ACCT_SHUT_TIME; dump_acct_info(1); } else if (next_shut_acct <= TIME) { // Dump accounting data for shutdown sessions next_shut_acct = TIME + ACCT_SHUT_TIME; if (shut_acct_n) dump_acct_info(0); } } } // Are we the master and shutting down?? if (config->cluster_iam_master) cluster_heartbeat(); // Flush any queued changes.. // Ok. Notify everyone we're shutting down. If we're // the master, this will force an election. cluster_send_ping(0); // // Important!!! We MUST not process any packets past this point! LOG(1, 0, 0, "Shutdown complete\n"); } static void stripdomain(char *host) { char *p; if ((p = strchr(host, '.'))) { char *domain = 0; char _domain[1024]; // strip off domain FILE *resolv = fopen("/etc/resolv.conf", "r"); if (resolv) { char buf[1024]; char *b; while (fgets(buf, sizeof(buf), resolv)) { if (strncmp(buf, "domain", 6) && strncmp(buf, "search", 6)) continue; if (!isspace(buf[6])) continue; b = buf + 7; while (isspace(*b)) b++; if (*b) { char *d = b; while (*b && !isspace(*b)) b++; *b = 0; if (buf[0] == 'd') // domain is canonical { domain = d; break; } // first search line if (!domain) { // hold, may be subsequent domain line strncpy(_domain, d, sizeof(_domain))[sizeof(_domain)-1] = 0; domain = _domain; } } } fclose(resolv); } if (domain) { int hl = strlen(host); int dl = strlen(domain); if (dl < hl && host[hl - dl - 1] == '.' && !strcmp(host + hl - dl, domain)) host[hl -dl - 1] = 0; } else { *p = 0; // everything after first dot } } } // Init data structures static void initdata(int optdebug, char *optconfig) { int i; if (!(config = shared_malloc(sizeof(configt)))) { fprintf(stderr, "Error doing malloc for configuration: %s\n", strerror(errno)); exit(1); } memset(config, 0, sizeof(configt)); time(&config->start_time); strncpy(config->config_file, optconfig, strlen(optconfig)); config->debug = optdebug; config->num_tbfs = MAXTBFS; config->rl_rate = 28; // 28kbps config->cluster_mcast_ttl = 1; config->cluster_master_min_adv = 1; config->ppp_restart_time = 3; config->ppp_max_configure = 10; config->ppp_max_failure = 5; config->kill_timedout_sessions = 1; strcpy(config->random_device, RANDOMDEVICE); // Set default value echo_timeout and idle_echo_timeout config->echo_timeout = ECHO_TIMEOUT; config->idle_echo_timeout = IDLE_ECHO_TIMEOUT; config->ppp_keepalive = 1; // Set default RDNSS lifetime config->dns6_lifetime = 1200; log_stream = stderr; #ifdef RINGBUFFER if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer)))) { LOG(0, 0, 0, "Error doing malloc for ringbuffer: %s\n", strerror(errno)); exit(1); } memset(ringbuffer, 0, sizeof(struct Tringbuffer)); #endif if (!(_statistics = shared_malloc(sizeof(struct Tstats)))) { LOG(0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno)); exit(1); } if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL))) { LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno)); exit(1); } if (!(bundle = shared_malloc(sizeof(bundlet) * MAXBUNDLE))) { LOG(0, 0, 0, "Error doing malloc for bundles: %s\n", strerror(errno)); exit(1); } if (!(frag = shared_malloc(sizeof(fragmentationt) * MAXBUNDLE))) { LOG(0, 0, 0, "Error doing malloc for fragmentations: %s\n", strerror(errno)); exit(1); } if (!(session = shared_malloc(sizeof(sessiont) * MAXSESSION))) { LOG(0, 0, 0, "Error doing malloc for sessions: %s\n", strerror(errno)); exit(1); } if (!(sess_local = shared_malloc(sizeof(sessionlocalt) * MAXSESSION))) { LOG(0, 0, 0, "Error doing malloc for sess_local: %s\n", strerror(errno)); exit(1); } if (!(radius = shared_malloc(sizeof(radiust) * MAXRADIUS))) { LOG(0, 0, 0, "Error doing malloc for radius: %s\n", strerror(errno)); exit(1); } if (!(ip_address_pool = shared_malloc(sizeof(ippoolt) * MAXIPPOOL))) { LOG(0, 0, 0, "Error doing malloc for ip_address_pool: %s\n", strerror(errno)); exit(1); } if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER))) { LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno)); exit(1); } memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); if (!(cli_session_actions = shared_malloc(sizeof(struct cli_session_actions) * MAXSESSION))) { LOG(0, 0, 0, "Error doing malloc for cli session actions: %s\n", strerror(errno)); exit(1); } memset(cli_session_actions, 0, sizeof(struct cli_session_actions) * MAXSESSION); if (!(cli_tunnel_actions = shared_malloc(sizeof(struct cli_tunnel_actions) * MAXSESSION))) { LOG(0, 0, 0, "Error doing malloc for cli tunnel actions: %s\n", strerror(errno)); exit(1); } memset(cli_tunnel_actions, 0, sizeof(struct cli_tunnel_actions) * MAXSESSION); memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL); memset(bundle, 0, sizeof(bundlet) * MAXBUNDLE); memset(session, 0, sizeof(sessiont) * MAXSESSION); memset(radius, 0, sizeof(radiust) * MAXRADIUS); memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL); // Put all the sessions on the free list marked as undefined. for (i = 1; i < MAXSESSION; i++) { session[i].next = i + 1; session[i].tunnel = T_UNDEF; // mark it as not filled in. } session[MAXSESSION - 1].next = 0; sessionfree = 1; // Mark all the tunnels as undefined (waiting to be filled in by a download). for (i = 1; i < MAXTUNNEL; i++) tunnel[i].state = TUNNELUNDEF; // mark it as not filled in. for (i = 1; i < MAXBUNDLE; i++) { bundle[i].state = BUNDLEUNDEF; } if (!*hostname) { // Grab my hostname unless it's been specified gethostname(hostname, sizeof(hostname)); stripdomain(hostname); } _statistics->start_time = _statistics->last_reset = time(NULL); #ifdef BGP if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS))) { LOG(0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno)); exit(1); } #endif /* BGP */ lac_initremotelnsdata(); } static int assign_ip_address(sessionidt s) { uint32_t i; int best = -1; time_t best_time = time_now; char *u = session[s].user; char reuse = 0; CSTAT(assign_ip_address); for (i = 1; i < ip_pool_size; i++) { if (!ip_address_pool[i].address || ip_address_pool[i].assigned) continue; if (!session[s].walled_garden && ip_address_pool[i].user[0] && !strcmp(u, ip_address_pool[i].user)) { best = i; reuse = 1; break; } if (ip_address_pool[i].last < best_time) { best = i; if (!(best_time = ip_address_pool[i].last)) break; // never used, grab this one } } if (best < 0) { LOG(0, s, session[s].tunnel, "assign_ip_address(): out of addresses\n"); return 0; } session[s].ip = ip_address_pool[best].address; session[s].ip_pool_index = best; ip_address_pool[best].assigned = 1; ip_address_pool[best].last = time_now; ip_address_pool[best].session = s; if (session[s].walled_garden) /* Don't track addresses of users in walled garden (note: this means that their address isn't "sticky" even if they get un-gardened). */ ip_address_pool[best].user[0] = 0; else strncpy(ip_address_pool[best].user, u, sizeof(ip_address_pool[best].user) - 1); STAT(ip_allocated); LOG(4, s, session[s].tunnel, "assign_ip_address(): %s ip address %d from pool\n", reuse ? "Reusing" : "Allocating", best); return 1; } static void free_ip_address(sessionidt s) { int i = session[s].ip_pool_index; CSTAT(free_ip_address); if (!session[s].ip) return; // what the? if (i < 0) // Is this actually part of the ip pool? i = 0; STAT(ip_freed); cache_ipmap(session[s].ip, -i); // Change the mapping to point back to the ip pool index. session[s].ip = 0; ip_address_pool[i].assigned = 0; ip_address_pool[i].session = 0; ip_address_pool[i].last = time_now; } // // Fsck the address pool against the session table. // Normally only called when we become a master. // // This isn't perfect: We aren't keep tracking of which // users used to have an IP address. // void rebuild_address_pool(void) { int i; // // Zero the IP pool allocation, and build // a map from IP address to pool index. for (i = 1; i < MAXIPPOOL; ++i) { ip_address_pool[i].assigned = 0; ip_address_pool[i].session = 0; if (!ip_address_pool[i].address) continue; cache_ipmap(ip_address_pool[i].address, -i); // Map pool IP to pool index. } for (i = 0; i < MAXSESSION; ++i) { int ipid; if (!(session[i].opened && session[i].ip)) continue; ipid = - lookup_ipmap(htonl(session[i].ip)); if (session[i].ip_pool_index < 0) { // Not allocated out of the pool. if (ipid < 1) // Not found in the pool either? good. continue; LOG(0, i, 0, "Session %u has an IP address (%s) that was marked static, but is in the pool (%d)!\n", i, fmtaddr(session[i].ip, 0), ipid); // Fall through and process it as part of the pool. } if (ipid > MAXIPPOOL || ipid < 0) { LOG(0, i, 0, "Session %u has a pool IP that's not found in the pool! (%d)\n", i, ipid); ipid = -1; session[i].ip_pool_index = ipid; continue; } ip_address_pool[ipid].assigned = 1; ip_address_pool[ipid].session = i; ip_address_pool[ipid].last = time_now; strncpy(ip_address_pool[ipid].user, session[i].user, sizeof(ip_address_pool[ipid].user) - 1); session[i].ip_pool_index = ipid; cache_ipmap(session[i].ip, i); // Fix the ip map. } } // // Fix the address pool to match a changed session. // (usually when the master sends us an update). static void fix_address_pool(int sid) { int ipid; ipid = session[sid].ip_pool_index; if (ipid > ip_pool_size) return; // Ignore it. rebuild_address_pool will fix it up. if (ip_address_pool[ipid].address != session[sid].ip) return; // Just ignore it. rebuild_address_pool will take care of it. ip_address_pool[ipid].assigned = 1; ip_address_pool[ipid].session = sid; ip_address_pool[ipid].last = time_now; strncpy(ip_address_pool[ipid].user, session[sid].user, sizeof(ip_address_pool[ipid].user) - 1); } // // Add a block of addresses to the IP pool to hand out. // static void add_to_ip_pool(in_addr_t addr, int prefixlen) { int i; if (prefixlen == 0) prefixlen = 32; // Host route only. addr &= 0xffffffff << (32 - prefixlen); if (ip_pool_size >= MAXIPPOOL) // Pool is full! return ; for (i = addr ; i < addr+(1<<(32-prefixlen)); ++i) { if ((i & 0xff) == 0 || (i&0xff) == 255) continue; // Skip 0 and broadcast addresses. ip_address_pool[ip_pool_size].address = i; ip_address_pool[ip_pool_size].assigned = 0; ++ip_pool_size; if (ip_pool_size >= MAXIPPOOL) { LOG(0, 0, 0, "Overflowed IP pool adding %s\n", fmtaddr(htonl(addr), 0)); return; } } } // Initialize the IP address pool static void initippool() { FILE *f; char *p; char buf[4096]; memset(ip_address_pool, 0, sizeof(ip_address_pool)); if (!(f = fopen(IPPOOLFILE, "r"))) { LOG(0, 0, 0, "Can't load pool file " IPPOOLFILE ": %s\n", strerror(errno)); exit(1); } while (ip_pool_size < MAXIPPOOL && fgets(buf, 4096, f)) { char *pool = buf; buf[4095] = 0; // Force it to be zero terminated/ if (*buf == '#' || *buf == '\n') continue; // Skip comments / blank lines if ((p = (char *)strrchr(buf, '\n'))) *p = 0; if ((p = (char *)strchr(buf, ':'))) { in_addr_t src; *p = '\0'; src = inet_addr(buf); if (src == INADDR_NONE) { LOG(0, 0, 0, "Invalid address pool IP %s\n", buf); exit(1); } // This entry is for a specific IP only if (src != config->bind_address) continue; *p = ':'; pool = p+1; } if ((p = (char *)strchr(pool, '/'))) { // It's a range int numbits = 0; in_addr_t start = 0; LOG(2, 0, 0, "Adding IP address range %s\n", buf); *p++ = 0; if (!*p || !(numbits = atoi(p))) { LOG(0, 0, 0, "Invalid pool range %s\n", buf); continue; } start = ntohl(inet_addr(pool)); // Add a static route for this pool LOG(5, 0, 0, "Adding route for address pool %s/%d\n", fmtaddr(htonl(start), 0), numbits); routeset(0, start, numbits, 0, 1); add_to_ip_pool(start, numbits); } else { // It's a single ip address add_to_ip_pool(ntohl(inet_addr(pool)), 0); } } fclose(f); LOG(1, 0, 0, "IP address pool is %d addresses\n", ip_pool_size - 1); } void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, uint16_t port) { struct sockaddr_in snoop_addr = {0}; if (!destination || !port || snoopfd <= 0 || size <= 0 || !packet) return; snoop_addr.sin_family = AF_INET; snoop_addr.sin_addr.s_addr = destination; snoop_addr.sin_port = ntohs(port); LOG(5, 0, 0, "Snooping %d byte packet to %s:%u\n", size, fmtaddr(snoop_addr.sin_addr.s_addr, 0), htons(snoop_addr.sin_port)); if (sendto(snoopfd, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, (void *) &snoop_addr, sizeof(snoop_addr)) < 0) LOG(0, 0, 0, "Error sending intercept packet: %s\n", strerror(errno)); STAT(packets_snooped); } static int dump_session(FILE **f, sessiont *s) { if (!s->opened || (!s->ip && !s->forwardtosession) || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden) return 1; if (!*f) { char filename[1024]; char timestr[64]; time_t now = time(NULL); strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(&now)); snprintf(filename, sizeof(filename), "%s/%s", config->accounting_dir, timestr); if (!(*f = fopen(filename, "w"))) { LOG(0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno)); return 0; } LOG(3, 0, 0, "Dumping accounting information to %s\n", filename); if(config->account_all_origin) { fprintf(*f, "# dslwatch.pl dump file V1.01\n" "# host: %s\n" "# endpoint: %s\n" "# time: %ld\n" "# uptime: %ld\n" "# format: username ip qos uptxoctets downrxoctets origin(L=LAC, R=Remote LNS, P=PPPOE)\n", hostname, fmtaddr(config->iftun_n_address[tunnel[s->tunnel].indexudp] ? config->iftun_n_address[tunnel[s->tunnel].indexudp] : my_address, 0), now, now - basetime); } else { fprintf(*f, "# dslwatch.pl dump file V1.01\n" "# host: %s\n" "# endpoint: %s\n" "# time: %ld\n" "# uptime: %ld\n" "# format: username ip qos uptxoctets downrxoctets\n", hostname, fmtaddr(config->iftun_n_address[tunnel[s->tunnel].indexudp] ? config->iftun_n_address[tunnel[s->tunnel].indexudp] : my_address, 0), now, now - basetime); } } LOG(4, 0, 0, "Dumping accounting information for %s\n", s->user); if(config->account_all_origin) { fprintf(*f, "%s %s %d %u %u %s\n", s->user, // username fmtaddr(htonl(s->ip), 0), // ip (s->throttle_in || s->throttle_out) ? 2 : 1, // qos (uint32_t) s->cin_delta, // uptxoctets (uint32_t) s->cout_delta, // downrxoctets (s->tunnel == TUNNEL_ID_PPPOE)?"P":(tunnel[s->tunnel].isremotelns?"R":"L")); // Origin } else if (!tunnel[s->tunnel].isremotelns && (s->tunnel != TUNNEL_ID_PPPOE)) { fprintf(*f, "%s %s %d %u %u\n", s->user, // username fmtaddr(htonl(s->ip), 0), // ip (s->throttle_in || s->throttle_out) ? 2 : 1, // qos (uint32_t) s->cin_delta, // uptxoctets (uint32_t) s->cout_delta); // downrxoctets } s->cin_delta = s->cout_delta = 0; return 1; } static void dump_acct_info(int all) { int i; FILE *f = NULL; CSTAT(dump_acct_info); if (shut_acct_n) { for (i = 0; i < shut_acct_n; i++) dump_session(&f, &shut_acct[i]); shut_acct_n = 0; } if (all) for (i = 1; i <= config->cluster_highest_sessionid; i++) dump_session(&f, &session[i]); if (f) fclose(f); } // Main program int main(int argc, char *argv[]) { int i; int optdebug = 0; char *optconfig = CONFIGFILE; time(&basetime); // start clock // scan args while ((i = getopt(argc, argv, "dvc:h:")) >= 0) { switch (i) { case 'd': if (fork()) exit(0); setsid(); if(!freopen("/dev/null", "r", stdin)) LOG(0, 0, 0, "Error freopen stdin: %s\n", strerror(errno)); if(!freopen("/dev/null", "w", stdout)) LOG(0, 0, 0, "Error freopen stdout: %s\n", strerror(errno)); if(!freopen("/dev/null", "w", stderr)) LOG(0, 0, 0, "Error freopen stderr: %s\n", strerror(errno)); break; case 'v': optdebug++; break; case 'c': optconfig = optarg; break; case 'h': snprintf(hostname, sizeof(hostname), "%s", optarg); break; default: printf("Args are:\n" "\t-d\t\tDetach from terminal\n" "\t-c \tConfig file\n" "\t-h \tForce hostname\n" "\t-v\t\tDebug\n"); return (0); break; } } // Start the timer routine off time(&time_now); strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); initplugins(); initdata(optdebug, optconfig); init_cli(); read_config_file(); /* set hostname /after/ having read the config file */ if (*config->hostname) strcpy(hostname, config->hostname); cli_init_complete(hostname); update_config(); init_tbf(config->num_tbfs); LOG(0, 0, 0, "L2TPNS version " VERSION "\n"); LOG(0, 0, 0, "Copyright (c) 2012, 2013, 2014 ISP FDN & SAMESWIRELESS\n"); LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering\n"); LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); { struct rlimit rlim; rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; // Remove the maximum core size if (setrlimit(RLIMIT_CORE, &rlim) < 0) LOG(0, 0, 0, "Can't set ulimit: %s\n", strerror(errno)); // Make core dumps go to /tmp if(chdir("/tmp")) LOG(0, 0, 0, "Error chdir /tmp: %s\n", strerror(errno)); } if (config->scheduler_fifo) { int ret; struct sched_param params = {0}; params.sched_priority = 1; if (get_nprocs() < 2) { LOG(0, 0, 0, "Not using FIFO scheduler, there is only 1 processor in the system.\n"); config->scheduler_fifo = 0; } else { if ((ret = sched_setscheduler(0, SCHED_FIFO, ¶ms)) == 0) { LOG(1, 0, 0, "Using FIFO scheduler. Say goodbye to any other processes running\n"); } else { LOG(0, 0, 0, "Error setting scheduler to FIFO: %s\n", strerror(errno)); config->scheduler_fifo = 0; } } } initnetlink(); /* Set up the cluster communications port. */ if (cluster_init() < 0) exit(1); inittun(); LOG(1, 0, 0, "Set up on interface %s\n", config->tundevicename); if (*config->pppoe_if_to_bind) { init_pppoe(); LOG(1, 0, 0, "Set up on pppoe interface %s\n", config->pppoe_if_to_bind); } if (!config->nbmultiaddress) { config->bind_n_address[0] = config->bind_address; config->nbmultiaddress++; } config->nbudpfd = config->nbmultiaddress; for (i = 0; i < config->nbudpfd; i++) initudp(&udpfd[i], config->bind_n_address[i]); initlacudp(); config->indexlacudpfd = config->nbudpfd; udpfd[config->indexlacudpfd] = udplacfd; config->nbudpfd++; initcontrol(); initdae(); // Intercept snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); initrad(); initippool(); dhcpv6_init(); // seed prng { unsigned seed = time_now ^ getpid(); LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed); srand(seed); } signal(SIGHUP, sighup_handler); signal(SIGCHLD, sigchild_handler); signal(SIGTERM, shutdown_handler); signal(SIGINT, shutdown_handler); signal(SIGQUIT, shutdown_handler); // Prevent us from getting paged out if (config->lock_pages) { if (!mlockall(MCL_CURRENT)) LOG(1, 0, 0, "Locking pages into memory\n"); else LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno)); } mainloop(); /* remove plugins (so cleanup code gets run) */ plugins_done(); // Remove the PID file if we wrote it if (config->wrote_pid && *config->pid_file == '/') unlink(config->pid_file); /* kill CLI children */ signal(SIGTERM, SIG_IGN); kill(0, SIGTERM); return 0; } static void sighup_handler(int sig) { main_reload++; } static void shutdown_handler(int sig) { main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER; } static void sigchild_handler(int sig) { while (waitpid(-1, NULL, WNOHANG) > 0) ; } static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response) { MD5_CTX ctx; *challenge_response = NULL; if (!*config->l2tp_secret) { LOG(0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n"); return; } LOG(4, 0, 0, " Building challenge response for CHAP request\n"); *challenge_response = calloc(17, 1); MD5_Init(&ctx); MD5_Update(&ctx, &id, 1); MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, challenge, challenge_length); MD5_Final(*challenge_response, &ctx); return; } static int facility_value(char *name) { int i; for (i = 0; facilitynames[i].c_name; i++) { if (strcmp(facilitynames[i].c_name, name) == 0) return facilitynames[i].c_val; } return 0; } static void update_config() { int i; char *p; static int timeout = 0; static int interval = 0; // Update logging closelog(); syslog_log = 0; if (log_stream) { if (log_stream != stderr) fclose(log_stream); log_stream = NULL; } if (*config->log_filename) { if (strstr(config->log_filename, "syslog:") == config->log_filename) { char *p = config->log_filename + 7; if (*p) { openlog("l2tpns", LOG_PID, facility_value(p)); syslog_log = 1; } } else if (strchr(config->log_filename, '/') == config->log_filename) { if ((log_stream = fopen((char *)(config->log_filename), "a"))) { fseek(log_stream, 0, SEEK_END); setbuf(log_stream, NULL); } else { log_stream = stderr; setbuf(log_stream, NULL); } } } else { log_stream = stderr; setbuf(log_stream, NULL); } #define L2TP_HDRS (20+8+6+4) // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc) #define TCP_HDRS (20+20) // TCP encapsulation: ip + tcp if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU; else if (config->l2tp_mtu > MAXMTU) config->l2tp_mtu = MAXMTU; // reset MRU/MSS globals MRU = config->l2tp_mtu - L2TP_HDRS; if (MRU > PPPoE_MRU) MRU = PPPoE_MRU; MSS = MRU - TCP_HDRS; // Update radius config->numradiusservers = 0; for (i = 0; i < MAXRADSERVER; i++) if (config->radiusserver[i]) { config->numradiusservers++; // Set radius port: if not set, take the port from the // first radius server. For the first radius server, // take the #defined default value from l2tpns.h // test twice, In case someone works with // a secondary radius server without defining // a primary one, this will work even then. if (i > 0 && !config->radiusport[i]) config->radiusport[i] = config->radiusport[i-1]; if (!config->radiusport[i]) config->radiusport[i] = RADPORT; } if (!config->numradiusservers) LOG(0, 0, 0, "No RADIUS servers defined!\n"); // parse radius_authtypes_s config->radius_authtypes = config->radius_authprefer = 0; p = config->radius_authtypes_s; while (p && *p) { char *s = strpbrk(p, " \t,"); int type = 0; if (s) { *s++ = 0; while (*s == ' ' || *s == '\t') s++; if (!*s) s = 0; } if (!strncasecmp("chap", p, strlen(p))) type = AUTHCHAP; else if (!strncasecmp("pap", p, strlen(p))) type = AUTHPAP; else LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"\n", p); config->radius_authtypes |= type; if (!config->radius_authprefer) config->radius_authprefer = type; p = s; } if (!config->radius_authtypes) { LOG(0, 0, 0, "Defaulting to PAP authentication\n"); config->radius_authtypes = config->radius_authprefer = AUTHPAP; } // normalise radius_authtypes_s if (config->radius_authprefer == AUTHPAP) { strcpy(config->radius_authtypes_s, "pap"); if (config->radius_authtypes & AUTHCHAP) strcat(config->radius_authtypes_s, ", chap"); } else { strcpy(config->radius_authtypes_s, "chap"); if (config->radius_authtypes & AUTHPAP) strcat(config->radius_authtypes_s, ", pap"); } if (!config->radius_dae_port) config->radius_dae_port = DAEPORT; if(!config->bind_portremotelns) config->bind_portremotelns = L2TPLACPORT; if(!config->bind_address_remotelns) config->bind_address_remotelns = INADDR_ANY; if (*config->bind_multi_address) { char *sip = config->bind_multi_address; char *n = sip; char *e = config->bind_multi_address + strlen(config->bind_multi_address); config->nbmultiaddress = 0; while (*sip && (sip < e)) { in_addr_t ip = 0; uint8_t u = 0; while (n < e && (*n == ',' || *n == ' ')) n++; while (n < e && (isdigit(*n) || *n == '.')) { if (*n == '.') { ip = (ip << 8) + u; u = 0; } else u = u * 10 + *n - '0'; n++; } ip = (ip << 8) + u; n++; if (ip) { config->bind_n_address[config->nbmultiaddress] = htonl(ip); config->iftun_n_address[config->nbmultiaddress] = htonl(ip); config->nbmultiaddress++; LOG(1, 0, 0, "Bind address %s\n", fmtaddr(htonl(ip), 0)); if (config->nbmultiaddress >= MAX_BINDADDR) break; } sip = n; } if (config->nbmultiaddress >= 1) { config->bind_address = config->bind_n_address[0]; config->iftun_address = config->bind_address; } } if(!config->iftun_address) { config->iftun_address = config->bind_address; config->iftun_n_address[0] = config->iftun_address; } if (*config->multi_hostname) { char *shost = config->multi_hostname; char *n = shost; char *e = config->multi_hostname + strlen(config->multi_hostname); config->nbmultihostname = 0; while (*shost && (shost < e)) { while ((n < e) && (*n == ' ' || *n == ',' || *n == '\t')) n++; i = 0; while (n < e && (*n != ',') && (*n != '\t')) { config->multi_n_hostname[config->nbmultihostname][i] = *n; n++;i++; } if (i > 0) { config->multi_n_hostname[config->nbmultihostname][i] = 0; LOG(1, 0, 0, "Bind Hostname %s\n", config->multi_n_hostname[config->nbmultihostname]); config->nbmultihostname++; if (config->nbmultihostname >= MAX_NBHOSTNAME) break; } shost = n; } if (config->nbmultihostname >= 1) { strcpy(hostname, config->multi_n_hostname[0]); strcpy(config->hostname, hostname); } } if (!*config->pppoe_ac_name) strncpy(config->pppoe_ac_name, DEFAULT_PPPOE_AC_NAME, sizeof(config->pppoe_ac_name) - 1); // re-initialise the random number source initrandom(config->random_device); // Update plugins for (i = 0; i < MAXPLUGINS; i++) { if (strcmp(config->plugins[i], config->old_plugins[i]) == 0) continue; if (*config->plugins[i]) { // Plugin added add_plugin(config->plugins[i]); } else if (*config->old_plugins[i]) { // Plugin removed remove_plugin(config->old_plugins[i]); } } // Guest change guest_accounts_num = 0; char *p2 = config->guest_user; while (p2 && *p2) { char *s = strpbrk(p2, " \t,"); if (s) { *s++ = 0; while (*s == ' ' || *s == '\t') s++; if (!*s) s = 0; } strcpy(guest_users[guest_accounts_num], p2); LOG(1, 0, 0, "Guest account[%d]: %s\n", guest_accounts_num, guest_users[guest_accounts_num]); guest_accounts_num++; p2 = s; } // Rebuild the guest_user array strcpy(config->guest_user, ""); int ui = 0; for (ui=0; uiguest_user, guest_users[ui]); if (uiguest_user, ","); } } memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); if (!config->multi_read_count) config->multi_read_count = 10; if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR); if (!config->cluster_port) config->cluster_port = CLUSTERPORT; if (!*config->cluster_interface) strncpy(config->cluster_interface, DEFAULT_MCAST_INTERFACE, sizeof(config->cluster_interface) - 1); if (!config->cluster_hb_interval) config->cluster_hb_interval = PING_INTERVAL; // Heartbeat every 0.5 seconds. if (!config->cluster_hb_timeout) config->cluster_hb_timeout = HB_TIMEOUT; // 10 missed heartbeat triggers an election. if (interval != config->cluster_hb_interval || timeout != config->cluster_hb_timeout) { // Paranoia: cluster_check_master() treats 2 x interval + 1 sec as // late, ensure we're sufficiently larger than that int t = 4 * config->cluster_hb_interval + 11; if (config->cluster_hb_timeout < t) { LOG(0, 0, 0, "Heartbeat timeout %d too low, adjusting to %d\n", config->cluster_hb_timeout, t); config->cluster_hb_timeout = t; } // Push timing changes to the slaves immediately if we're the master if (config->cluster_iam_master) cluster_heartbeat(); interval = config->cluster_hb_interval; timeout = config->cluster_hb_timeout; } // Write PID file if (*config->pid_file == '/' && !config->wrote_pid) { FILE *f; if ((f = fopen(config->pid_file, "w"))) { fprintf(f, "%d\n", getpid()); fclose(f); config->wrote_pid = 1; } else { LOG(0, 0, 0, "Can't write to PID file %s: %s\n", config->pid_file, strerror(errno)); } } } static void read_config_file() { FILE *f; if (!config->config_file) return; if (!(f = fopen(config->config_file, "r"))) { fprintf(stderr, "Can't open config file %s: %s\n", config->config_file, strerror(errno)); return; } LOG(3, 0, 0, "Reading config file %s\n", config->config_file); cli_do_file(f); LOG(3, 0, 0, "Done reading config file\n"); fclose(f); } int sessionsetup(sessionidt s, tunnelidt t) { // A session now exists, set it up in_addr_t ip; char *user; sessionidt i; int r; CSTAT(sessionsetup); LOG(3, s, t, "Doing session setup for session\n"); // Join a bundle if the MRRU option is accepted if(session[s].mrru > 0 && session[s].bundle == 0) { LOG(3, s, t, "This session can be part of multilink bundle\n"); if (join_bundle(s) > 0) cluster_send_bundle(session[s].bundle); else { LOG(0, s, t, "MPPP: Mismatching mssf option with other sessions in bundle\n"); sessionshutdown(s, "Mismatching mssf option.", CDN_NONE, TERM_SERVICE_UNAVAILABLE); return 0; } } if (!session[s].ip) { assign_ip_address(s); if (!session[s].ip) { LOG(0, s, t, " No IP allocated. The IP address pool is FULL!\n"); sessionshutdown(s, "No IP addresses available.", CDN_TRY_ANOTHER, TERM_SERVICE_UNAVAILABLE); return 0; } LOG(3, s, t, " No IP allocated. Assigned %s from pool\n", fmtaddr(htonl(session[s].ip), 0)); } // Make sure this is right session[s].tunnel = t; // zap old sessions with same IP and/or username // Don't kill gardened sessions - doing so leads to a DoS // from someone who doesn't need to know the password { ip = session[s].ip; user = session[s].user; for (i = 1; i <= config->cluster_highest_sessionid; i++) { if (i == s) continue; if (!session[s].opened) break; // Allow duplicate sessions for multilink ones of the same bundle. if (session[s].bundle && session[i].bundle && session[s].bundle == session[i].bundle) continue; if (ip == session[i].ip) { sessionshutdown(i, "Duplicate IP address", CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc. continue; } if (config->allow_duplicate_users) continue; if (session[s].walled_garden || session[i].walled_garden) continue; // Guest change int found = 0; int gu; for (gu = 0; gu < guest_accounts_num; gu++) { if (!strcasecmp(user, guest_users[gu])) { found = 1; break; } } if (found) continue; // Drop the new session in case of duplicate sessionss, not the old one. if (!strcasecmp(user, session[i].user)) sessionshutdown(i, "Duplicate session for users", CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc. } } // no need to set a route for the same IP address of the bundle if (!session[s].bundle || (bundle[session[s].bundle].num_of_links == 1)) { int routed = 0; // Add the route for this session. for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) { if ((session[s].ip >> (32-session[s].route[r].prefixlen)) == (session[s].route[r].ip >> (32-session[s].route[r].prefixlen))) routed++; routeset(s, session[s].route[r].ip, session[s].route[r].prefixlen, 0, 1); } // Static IPs need to be routed if not already // convered by a Framed-Route. Anything else is part // of the IP address pool and is already routed, it // just needs to be added to the IP cache. // IPv6 route setup is done in ppp.c, when IPV6CP is acked. if (session[s].ip_pool_index == -1) // static ip { if (!routed) routeset(s, session[s].ip, 0, 0, 1); } else cache_ipmap(session[s].ip, s); } sess_local[s].lcp_authtype = 0; // RADIUS authentication complete lcp_open(s, t); // transition to Network phase and send initial IPCP // Run the plugin's against this new session. { struct param_new_session data = { &tunnel[t], &session[s] }; run_plugins(PLUGIN_NEW_SESSION, &data); } // Allocate TBFs if throttled if (session[s].throttle_in || session[s].throttle_out) throttle_session(s, session[s].throttle_in, session[s].throttle_out); session[s].last_packet = session[s].last_data = time_now; LOG(2, s, t, "Login by %s at %s from %s (%s)\n", session[s].user, fmtaddr(htonl(session[s].ip), 0), fmtaddr(htonl(tunnel[t].ip), 1), tunnel[t].hostname); cluster_send_session(s); // Mark it as dirty, and needing to the flooded to the cluster. return 1; // RADIUS OK and IP allocated, done... } // // This session just got dropped on us by the master or something. // Make sure our tables up up to date... // int load_session(sessionidt s, sessiont *new) { int i; int newip = 0; // Sanity checks. if (new->ip_pool_index >= MAXIPPOOL || new->tunnel >= MAXTUNNEL) { LOG(0, s, 0, "Strange session update received!\n"); // FIXME! What to do here? return 0; } // // Ok. All sanity checks passed. Now we're committed to // loading the new session. // session[s].tunnel = new->tunnel; // For logging in cache_ipmap // See if routes/ip cache need updating if (new->ip != session[s].ip) newip++; for (i = 0; !newip && i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++) if (new->route[i].ip != session[s].route[i].ip || new->route[i].prefixlen != session[s].route[i].prefixlen) newip++; // needs update if (newip) { int routed = 0; // remove old routes... for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++) { if ((session[s].ip >> (32-session[s].route[i].prefixlen)) == (session[s].route[i].ip >> (32-session[s].route[i].prefixlen))) routed++; routeset(s, session[s].route[i].ip, session[s].route[i].prefixlen, 0, 0); } // ...ip if (session[s].ip) { if (session[s].ip_pool_index == -1) // static IP { if (!routed) routeset(s, session[s].ip, 0, 0, 0); } else // It's part of the IP pool, remove it manually. uncache_ipmap(session[s].ip); } // remove old IPV6 routes... for (i = 0; i < MAXROUTE6 && session[s].route6[i].ipv6route.s6_addr[0] && session[s].route6[i].ipv6prefixlen; i++) { route6set(s, session[s].route6[i].ipv6route, session[s].route6[i].ipv6prefixlen, 0); } if (session[s].ipv6address.s6_addr[0]) { route6set(s, session[s].ipv6address, 128, 0); } routed = 0; // add new routes... for (i = 0; i < MAXROUTE && new->route[i].ip; i++) { if ((new->ip >> (32-new->route[i].prefixlen)) == (new->route[i].ip >> (32-new->route[i].prefixlen))) routed++; routeset(s, new->route[i].ip, new->route[i].prefixlen, 0, 1); } // ...ip if (new->ip) { // If there's a new one, add it. if (new->ip_pool_index == -1) { if (!routed) routeset(s, new->ip, 0, 0, 1); } else cache_ipmap(new->ip, s); } } // check v6 routing if (new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened) { for (i = 0; i < MAXROUTE6 && new->route6[i].ipv6prefixlen; i++) { route6set(s, new->route6[i].ipv6route, new->route6[i].ipv6prefixlen, 1); } } if (new->ipv6address.s6_addr[0] && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened) { // Check if included in prefix if (sessionbyipv6(new->ipv6address) != s) route6set(s, new->ipv6address, 128, 1); } // check filters if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0])) { LOG(2, s, session[s].tunnel, "Dropping invalid input filter %u\n", (int) new->filter_in); new->filter_in = 0; } if (new->filter_out && (new->filter_out > MAXFILTER || !ip_filters[new->filter_out - 1].name[0])) { LOG(2, s, session[s].tunnel, "Dropping invalid output filter %u\n", (int) new->filter_out); new->filter_out = 0; } if (new->filter_in != session[s].filter_in) { if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; if (new->filter_in) ip_filters[new->filter_in - 1].used++; } if (new->filter_out != session[s].filter_out) { if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--; if (new->filter_out) ip_filters[new->filter_out - 1].used++; } if (new->tunnel && s > config->cluster_highest_sessionid) // Maintain this in the slave. It's used // for walking the sessions to forward byte counts to the master. config->cluster_highest_sessionid = s; memcpy(&session[s], new, sizeof(session[s])); // Copy over.. // Do fixups into address pool. if (new->ip_pool_index != -1) fix_address_pool(s); return 1; } static void initplugins() { int i; loaded_plugins = ll_init(); // Initialize the plugins to nothing for (i = 0; i < MAX_PLUGIN_TYPES; i++) plugins[i] = ll_init(); } static void *open_plugin(char *plugin_name, int load) { char path[256] = ""; snprintf(path, 256, PLUGINDIR "/%s.so", plugin_name); LOG(2, 0, 0, "%soading plugin from %s\n", load ? "L" : "Un-l", path); return dlopen(path, RTLD_NOW); } // plugin callback to get a config value static void *getconfig(char *key, enum config_typet type) { int i; for (i = 0; config_values[i].key; i++) { if (!strcmp(config_values[i].key, key)) { if (config_values[i].type == type) return ((void *) config) + config_values[i].offset; LOG(1, 0, 0, "plugin requested config item \"%s\" expecting type %d, have type %d\n", key, type, config_values[i].type); return 0; } } LOG(1, 0, 0, "plugin requested unknown config item \"%s\"\n", key); return 0; } static int add_plugin(char *plugin_name) { static struct pluginfuncs funcs = { _log, _log_hex, fmtaddr, sessionbyuser, sessiontbysessionidt, sessionidtbysessiont, radiusnew, radiussend, getconfig, sessionshutdown, sessionkill, throttle_session, cluster_send_session, }; void *p = open_plugin(plugin_name, 1); int (*initfunc)(struct pluginfuncs *); int i; if (!p) { LOG(1, 0, 0, " Plugin load failed: %s\n", dlerror()); return -1; } if (ll_contains(loaded_plugins, p)) { dlclose(p); return 0; // already loaded } { int *v = dlsym(p, "plugin_api_version"); if (!v || *v != PLUGIN_API_VERSION) { LOG(1, 0, 0, " Plugin load failed: API version mismatch: %s\n", dlerror()); dlclose(p); return -1; } } if ((initfunc = dlsym(p, "plugin_init"))) { if (!initfunc(&funcs)) { LOG(1, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror()); dlclose(p); return -1; } } ll_push(loaded_plugins, p); for (i = 0; i < max_plugin_functions; i++) { void *x; if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i]))) { LOG(3, 0, 0, " Supports function \"%s\"\n", plugin_functions[i]); ll_push(plugins[i], x); } } LOG(2, 0, 0, " Loaded plugin %s\n", plugin_name); return 1; } static void run_plugin_done(void *plugin) { int (*donefunc)(void) = dlsym(plugin, "plugin_done"); if (donefunc) donefunc(); } static int remove_plugin(char *plugin_name) { void *p = open_plugin(plugin_name, 0); int loaded = 0; if (!p) return -1; if (ll_contains(loaded_plugins, p)) { int i; for (i = 0; i < max_plugin_functions; i++) { void *x; if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i]))) ll_delete(plugins[i], x); } ll_delete(loaded_plugins, p); run_plugin_done(p); loaded = 1; } dlclose(p); LOG(2, 0, 0, "Removed plugin %s\n", plugin_name); return loaded; } int run_plugins(int plugin_type, void *data) { int (*func)(void *data); if (!plugins[plugin_type] || plugin_type > max_plugin_functions) return PLUGIN_RET_ERROR; ll_reset(plugins[plugin_type]); while ((func = ll_next(plugins[plugin_type]))) { int r = func(data); if (r != PLUGIN_RET_OK) return r; // stop here } return PLUGIN_RET_OK; } static void plugins_done() { void *p; ll_reset(loaded_plugins); while ((p = ll_next(loaded_plugins))) run_plugin_done(p); } static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local) { struct nsctl request; struct nsctl response; int type = unpack_control(&request, buf, len); int r; void *p; if (log_stream && config->debug >= 4) { if (type < 0) { LOG(4, 0, 0, "Bogus control message from %s (%d)\n", fmtaddr(addr->sin_addr.s_addr, 0), type); } else { LOG(4, 0, 0, "Received [%s] ", fmtaddr(addr->sin_addr.s_addr, 0)); dump_control(&request, log_stream); } } switch (type) { case NSCTL_REQ_LOAD: if (request.argc != 1) { response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = "name of plugin required"; } else if ((r = add_plugin(request.argv[0])) < 1) { response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = !r ? "plugin already loaded" : "error loading plugin"; } else { response.type = NSCTL_RES_OK; response.argc = 0; } break; case NSCTL_REQ_UNLOAD: if (request.argc != 1) { response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = "name of plugin required"; } else if ((r = remove_plugin(request.argv[0])) < 1) { response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = !r ? "plugin not loaded" : "plugin not found"; } else { response.type = NSCTL_RES_OK; response.argc = 0; } break; case NSCTL_REQ_HELP: response.type = NSCTL_RES_OK; response.argc = 0; ll_reset(loaded_plugins); while ((p = ll_next(loaded_plugins))) { char **help = dlsym(p, "plugin_control_help"); while (response.argc < 0xff && help && *help) response.argv[response.argc++] = *help++; } break; case NSCTL_REQ_CONTROL: { struct param_control param = { config->cluster_iam_master, request.argc, request.argv, 0, NULL, }; int r = run_plugins(PLUGIN_CONTROL, ¶m); if (r == PLUGIN_RET_ERROR) { response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = param.additional ? param.additional : "error returned by plugin"; } else if (r == PLUGIN_RET_NOTMASTER) { static char msg[] = "must be run on master: 000.000.000.000"; response.type = NSCTL_RES_ERR; response.argc = 1; if (config->cluster_master_address) { strcpy(msg + 23, fmtaddr(config->cluster_master_address, 0)); response.argv[0] = msg; } else { response.argv[0] = "must be run on master: none elected"; } } else if (!(param.response & NSCTL_RESPONSE)) { response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = param.response ? "unrecognised response value from plugin" : "unhandled action"; } else { response.type = param.response; response.argc = 0; if (param.additional) { response.argc = 1; response.argv[0] = param.additional; } } } break; default: response.type = NSCTL_RES_ERR; response.argc = 1; response.argv[0] = "error unpacking control packet"; } buf = calloc(NSCTL_MAX_PKT_SZ, 1); if (!buf) { LOG(2, 0, 0, "Failed to allocate nsctl response\n"); return; } r = pack_control(buf, NSCTL_MAX_PKT_SZ, response.type, response.argc, response.argv); if (r > 0) { sendtofrom(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen, local); if (log_stream && config->debug >= 4) { LOG(4, 0, 0, "Sent [%s] ", fmtaddr(addr->sin_addr.s_addr, 0)); dump_control(&response, log_stream); } } else LOG(2, 0, 0, "Failed to pack nsctl response for %s (%d)\n", fmtaddr(addr->sin_addr.s_addr, 0), r); free(buf); } static tunnelidt new_tunnel() { tunnelidt i; for (i = 1; i < MAXTUNNEL; i++) { if ((tunnel[i].state == TUNNELFREE) && (i != TUNNEL_ID_PPPOE)) { LOG(4, 0, i, "Assigning tunnel ID %u\n", i); if (i > config->cluster_highest_tunnelid) config->cluster_highest_tunnelid = i; return i; } } LOG(0, 0, 0, "Can't find a free tunnel! There shouldn't be this many in use!\n"); return 0; } // // We're becoming the master. Do any required setup.. // // This is principally telling all the plugins that we're // now a master, and telling them about all the sessions // that are active too.. // void become_master(void) { int s, i; static struct event_data d[RADIUS_FDS]; struct epoll_event e; run_plugins(PLUGIN_BECOME_MASTER, NULL); // running a bunch of iptables commands is slow and can cause // the master to drop tunnels on takeover--kludge around the // problem by forking for the moment (note: race) if (!fork_and_close()) { for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { if (!session[s].opened) // Not an in-use session. continue; run_plugins(PLUGIN_NEW_SESSION_MASTER, &session[s]); } exit(0); } // add radius fds e.events = EPOLLIN; for (i = 0; i < RADIUS_FDS; i++) { d[i].type = FD_TYPE_RADIUS; d[i].index = i; e.data.ptr = &d[i]; epoll_ctl(epollfd, EPOLL_CTL_ADD, radfds[i], &e); } } int cmd_show_hist_idle(struct cli_def *cli, const char *command, char **argv, int argc) { int s, i; int count = 0; int buckets[64]; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; time(&time_now); for (i = 0; i < 64;++i) buckets[i] = 0; for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int idle; if (!session[s].opened) continue; idle = time_now - session[s].last_data; idle /= 5 ; // In multiples of 5 seconds. if (idle < 0) idle = 0; if (idle > 63) idle = 63; ++count; ++buckets[idle]; } for (i = 0; i < 63; ++i) { cli_print(cli, "%3d seconds : %7.2f%% (%6d)", i * 5, (double) buckets[i] * 100.0 / count , buckets[i]); } cli_print(cli, "lots of secs : %7.2f%% (%6d)", (double) buckets[63] * 100.0 / count , buckets[i]); cli_print(cli, "%d total sessions open.", count); return CLI_OK; } int cmd_show_hist_open(struct cli_def *cli, const char *command, char **argv, int argc) { int s, i; int count = 0; int buckets[64]; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; time(&time_now); for (i = 0; i < 64;++i) buckets[i] = 0; for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int open = 0, d; if (!session[s].opened) continue; d = time_now - session[s].opened; if (d < 0) d = 0; while (d > 1 && open < 32) { ++open; d >>= 1; // half. } ++count; ++buckets[open]; } s = 1; for (i = 0; i < 30; ++i) { cli_print(cli, " < %8d seconds : %7.2f%% (%6d)", s, (double) buckets[i] * 100.0 / count , buckets[i]); s <<= 1; } cli_print(cli, "%d total sessions open.", count); return CLI_OK; } /* Unhide an avp. * * This unencodes the AVP using the L2TP secret and the previously * stored random vector. It overwrites the hidden data with the * unhidden AVP subformat. */ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len) { MD5_CTX ctx; uint8_t digest[16]; uint8_t *last; size_t d = 0; uint16_t m = htons(type); // Compute initial pad MD5_Init(&ctx); MD5_Update(&ctx, (unsigned char *) &m, 2); MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, vector, vec_len); MD5_Final(digest, &ctx); // pointer to last decoded 16 octets last = value; while (len > 0) { // calculate a new pad based on the last decoded block if (d >= sizeof(digest)) { MD5_Init(&ctx); MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, last, sizeof(digest)); MD5_Final(digest, &ctx); d = 0; last = value; } *value++ ^= digest[d++]; len--; } } int find_filter(char const *name, size_t len) { int free = -1; int i; for (i = 0; i < MAXFILTER; i++) { if (!*ip_filters[i].name) { if (free < 0) free = i; continue; } if (strlen(ip_filters[i].name) != len) continue; if (!strncmp(ip_filters[i].name, name, len)) return i; } return free; } static int ip_filter_port(ip_filter_portt *p, uint16_t port) { switch (p->op) { case FILTER_PORT_OP_EQ: return port == p->port; case FILTER_PORT_OP_NEQ: return port != p->port; case FILTER_PORT_OP_GT: return port > p->port; case FILTER_PORT_OP_LT: return port < p->port; case FILTER_PORT_OP_RANGE: return port >= p->port && port <= p->port2; } return 0; } static int ip_filter_flag(uint8_t op, uint8_t sflags, uint8_t cflags, uint8_t flags) { switch (op) { case FILTER_FLAG_OP_ANY: return (flags & sflags) || (~flags & cflags); case FILTER_FLAG_OP_ALL: return (flags & sflags) == sflags && (~flags & cflags) == cflags; case FILTER_FLAG_OP_EST: return (flags & (TCP_FLAG_ACK|TCP_FLAG_RST)) && (~flags & TCP_FLAG_SYN); } return 0; } int ip_filter(uint8_t *buf, int len, uint8_t filter) { uint16_t frag_offset; uint8_t proto; in_addr_t src_ip; in_addr_t dst_ip; uint16_t src_port = 0; uint16_t dst_port = 0; uint8_t flags = 0; ip_filter_rulet *rule; if (len < 20) // up to end of destination address return 0; if ((*buf >> 4) != 4) // IPv4 return 0; frag_offset = ntohs(*(uint16_t *) (buf + 6)) & 0x1fff; proto = buf[9]; src_ip = *(in_addr_t *) (buf + 12); dst_ip = *(in_addr_t *) (buf + 16); if (frag_offset == 0 && (proto == IPPROTO_TCP || proto == IPPROTO_UDP)) { int l = (buf[0] & 0xf) * 4; // length of IP header if (len < l + 4) // ports return 0; src_port = ntohs(*(uint16_t *) (buf + l)); dst_port = ntohs(*(uint16_t *) (buf + l + 2)); if (proto == IPPROTO_TCP) { if (len < l + 14) // flags return 0; flags = buf[l + 13] & 0x3f; } } for (rule = ip_filters[filter].rules; rule->action; rule++) { if (rule->proto != IPPROTO_IP && proto != rule->proto) continue; if (rule->src_wild != INADDR_BROADCAST && (src_ip & ~rule->src_wild) != (rule->src_ip & ~rule->src_wild)) continue; if (rule->dst_wild != INADDR_BROADCAST && (dst_ip & ~rule->dst_wild) != (rule->dst_ip & ~rule->dst_wild)) continue; if (frag_offset) { // layer 4 deny rules are skipped if (rule->action == FILTER_ACTION_DENY && (rule->src_ports.op || rule->dst_ports.op || rule->tcp_flag_op)) continue; } else { if (rule->frag) continue; if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) { if (rule->src_ports.op && !ip_filter_port(&rule->src_ports, src_port)) continue; if (rule->dst_ports.op && !ip_filter_port(&rule->dst_ports, dst_port)) continue; if (proto == IPPROTO_TCP && rule->tcp_flag_op && !ip_filter_flag(rule->tcp_flag_op, rule->tcp_sflags, rule->tcp_cflags, flags)) continue; } } // matched rule->counter++; return rule->action == FILTER_ACTION_PERMIT; } // default deny return 0; } tunnelidt lac_new_tunnel() { return new_tunnel(); } void lac_tunnelclear(tunnelidt t) { tunnelclear(t); } void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len) { uint16_t version = 0x0100; // protocol version tunnel[t].state = TUNNELOPENING; // Sent SCCRQ - Start Control Connection Request controlt *c = controlnew(1); // sending SCCRQ controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name controls(c, 8, Vendor_name, 1); // Vendor name control16(c, 2, version, 1); // protocol version control32(c, 3, 3, 1); // framing Capabilities control16(c, 9, t, 1); // assigned tunnel controlb(c, 11, (uint8_t *) auth, auth_len, 1); // CHAP Challenge LOG(3, 0, t, "Sent SCCRQ to REMOTE LNS\n"); controladd(c, 0, t); // send } void lac_send_ICRQ(tunnelidt t, sessionidt s) { // Sent ICRQ Incoming-call-request controlt *c = controlnew(10); // ICRQ control16(c, 14, s, 1); // assigned sesion call_serial_number++; control32(c, 15, call_serial_number, 1); // call serial number LOG(3, s, t, "Sent ICRQ to REMOTE LNS (far ID %u)\n", tunnel[t].far); controladd(c, 0, t); // send } void lac_tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg) { tunnelshutdown(t, reason, result, error, msg); } l2tpns-2.3.3/l2tpns.h000066400000000000000000001036211400724550600143420ustar00rootroot00000000000000// L2TPNS Global Stuff // $Id: l2tpns.h,v 1.121 2009-12-08 14:49:28 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ #include #include #include #include #include #include #include #include #include #include #define VERSION "2.3.2" // Limits #define MAXTUNNEL 500 // could be up to 65535 #define MAXBUNDLE 300 // could be up to 65535 #define MAXBUNDLESES 12 // Maximum number of member links in bundle #define MAXADDRESS 20 // Maximum length for the Endpoint Discrminiator address #define MAXSESSION 60000 // could be up to 65535 #define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session. // Tunnel Id reserved for pppoe #define TUNNEL_ID_PPPOE 1 #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 MAXHOSTNAME 256 // hostname #define MAXUSER 128 // username #define MAXPASS 128 // password #define MAXCLASS 128 // radius class attribute size #define MAXPLUGINS 20 // maximum number of plugins to load #define MAXRADSERVER 10 // max radius servers #define MAXROUTE 10 // max static routes per session #define MAXROUTE6 5 // max static Ipv6 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 10 // Time between last packet sent and LCP ECHO generation #define IDLE_ECHO_TIMEOUT 240 // Time between last packet seen and session shutdown #define BUSY_WAIT_TIME 3000 // 5 minutes in 1/10th seconds to wait for radius to cleanup on shutdown #define MP_BEGIN 0x80 // This value is used when (b)egin bit is set in MP header #define MP_END 0x40 // This value is used when (e)nd bit is set in MP header #define MP_BOTH_BITS 0xC0 // This value is used when both bits (begin and end) are set in MP header #define MINFRAGLEN 64 // minimum fragment length #define MAXFRAGLEN 1496 // Maximum length for Multilink fragment (The multilink may contain only one link) #define MAXFRAGNUM 512 // Maximum number of Multilink fragment in a bundle (must be in the form of 2^X) // it's not expected to have a space for more than 10 unassembled packets = 10 * MAXBUNDLESES #define MAXFRAGNUM_MASK (MAXFRAGNUM - 1) // Must be equal to MAXFRAGNUM-1 // Multi bind address constants #define MAX_UDPFD 4 #define MAX_BINDADDR MAX_UDPFD // + 1 for the LAC Hostname #define MAX_NBHOSTNAME (MAX_UDPFD + 1) // 4 + 1 for the udplac #define INIT_TABUDPFD {-1, -1, -1, -1, -1} #define INIT_TABUDPVAR {0, 0, 0, 0, 0} // 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 bundleidt; 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; int prefixlen; } routet; // structures typedef struct // route { struct in6_addr ipv6route; // Static IPv6 route uint8_t ipv6prefixlen; // IPv6 route prefix length } routet6; typedef struct controls // control message { struct controls *next; // next in queue uint16_t length; // length uint8_t buf[MAXCONTROL]; } controlt; typedef struct { uint8_t length; // Endpoint Discriminator length uint8_t addr_class; // Endpoint Discriminator class uint8_t address[MAXADDRESS]; // Endpoint Discriminator address } epdist; typedef struct { sessionidt sid; // Fragment originating session tunnelidt tid; // Fragment originating tunnel uint8_t flags; // MP frame flags uint32_t seq; // fragment seq num uint32_t jitteravg; uint16_t length; // Fragment length uint8_t data[MAXFRAGLEN]; // Fragment data } fragmentt; 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; uint16_t mru; // maximum receive unit 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 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 snoop_port; // Interception destination port in_addr_t snoop_ip; // Interception destination IP clockt opened; // when started clockt die; // being closed, when to finally free uint32_t session_timeout; // Maximum session time in seconds uint32_t idle_timeout; // Maximum idle time in seconds time_t last_packet; // Last packet from the user (used for idle timeouts) time_t last_data; // Last data packet to/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 session for radius stop messages) char called[MAXTEL]; // called number char calling[MAXTEL]; // calling number uint32_t tx_connect_speed; uint32_t rx_connect_speed; clockt timeout; // Session timeout uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit epdist epdis; // Multilink Endpoint Discriminator bundleidt bundle; // Multilink Bundle Identifier uint8_t mssf; // Multilink Short Sequence Number Header Format uint8_t walled_garden; // is this session gardened? uint8_t classlen; // class (needed for radius accounting messages) char class[MAXCLASS]; sessionidt forwardtosession; // LNS id_session to forward uint8_t src_hwaddr[ETH_ALEN]; // MAC addr source (for pppoe sessions 6 bytes) uint32_t dhcpv6_prefix_iaid; // prefix iaid requested by client uint32_t dhcpv6_iana_iaid; // iaid of iana requested by client struct in6_addr ipv6address; // Framed Ipv6 address struct dhcp6_opt_clientid dhcpv6_client_id; // Size max (headers + DUID) routet6 route6[MAXROUTE6]; // static IPv6 routes char reserved[4]; // Space to expand structure without changing HB_VERSION } sessiont; typedef struct { int state; // current state (bundlestate enum) uint32_t seq_num_t; // Sequence Number (transmission) uint32_t timeout; // Session-Timeout for bundle uint32_t max_seq; // Max value of sequence number field uint8_t num_of_links; // Number of links joint to this bundle uint32_t online_time; // The time this bundle is online clockt last_check; // Last time the timeout is checked uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit uint8_t mssf; // Multilink Short Sequence Number Header Format epdist epdis; // Multilink Endpoint Discriminator char user[MAXUSER]; // Needed for matching member links sessionidt current_ses; // Current session to use for sending (used in RR load-balancing) sessionidt members[MAXBUNDLESES]; // Array for member links sessions } bundlet; typedef struct { fragmentt fragment[MAXFRAGNUM]; uint8_t reassembled_frame[MAXETHER]; // The reassembled frame uint16_t re_frame_len; // The reassembled frame length uint16_t re_frame_begin_index, re_frame_end_index; // reassembled frame begin index, end index respectively uint16_t start_index, end_index; // start and end sequence numbers available on the fragments array respectively uint32_t M; // minimum frame sequence number received over all bundle members uint32_t start_seq; // Last received frame sequence number (bearing B bit) } fragmentationt; #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; // our MRRU uint16_t mp_mrru; // our mssf uint16_t mp_mssf; // our Endpoint Discriminator in_addr_t mp_epdis; // 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; // Last Multilink frame sequence number received uint32_t last_seq; // jitter average of the session uint32_t jitteravg; // time in milliseconds of the last fragment. uint64_t prev_time; } 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 // 328 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 uint16_t isremotelns; // != 0 if the tunnel is to remote LNS (== index on the conf remote lns) uint16_t indexudp; // Index UDP file handle (in udpfd[]) char reserved[12]; // Space to expand structure without changing HB_VERSION } 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 { BUNDLEFREE, // Not in use BUNDLEOPEN, // Active bundle BUNDLEUNDEF, // Undefined }; enum { NULLCLASS = 0, //End Point Discriminator classes LOCALADDR, IPADDR, IEEEMACADDR, PPPMAGIC, PSNDN, }; 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 RADIUSJUSTAUTH, // sending auth to RADIUS server, just authentication, no ip assigning }; 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_sessionbyipv6new; 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; uint32_t call_dhcpv6_process; #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 #ifndef IFNAMSIZ # define IFNAMSIZ 16 #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 tundevicename[IFNAMSIZ]; // 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 int ppp_keepalive; // send echoes regardless 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; // port for radius DAE uint16_t radius_bind_min; // port range for udp sockets used to send/recv radius packets uint16_t radius_bind_max; 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 int kill_timedout_sessions; // kill authenticated sessions with "session_timeout == 0" 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]; int account_all_origin; // Accouting all origin (LAC data + Remote LNS Data + PPPOE data) in_addr_t bind_address; in_addr_t peer_address; int send_garp; // Set to true to garp for vip address on startup 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 char epdis_addr[20]; // MP Endpoint Discriminator address in_addr_t cluster_address; // Multicast address of cluster. int cluster_port; // UDP port 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_bundles; // How many bundles 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_bundleid; 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. in_addr_t cli_bind_address; // bind address for CLI char hostname[MAXHOSTNAME]; // hostname (overridden by -h on command line) // Guest change char guest_user[MAXUSER]; // Guest account username #ifdef BGP #define BGP_NUM_PEERS 2 uint16_t as_number; struct { char name[64]; uint16_t as; int keepalive; int hold; struct in_addr update_source; } neighbour[BGP_NUM_PEERS]; in_addr_t nexthop_address; struct in6_addr nexthop6_address; #endif int echo_timeout; // Time between last packet sent and LCP ECHO generation int idle_echo_timeout; // Time between last packet seen and // Drop sessions who have not responded within IDLE_ECHO_TIMEOUT seconds in_addr_t iftun_address; int disable_lac_func; int auth_tunnel_change_addr_src; int highest_rlnsid; uint16_t bind_portremotelns; in_addr_t bind_address_remotelns; char pppoe_if_to_bind[IFNAMSIZ]; // Name pppoe interface to bind char pppoe_service_name[64]; // pppoe service name char pppoe_ac_name[64]; uint8_t pppoe_hwaddr[ETH_ALEN]; // MAC addr of interface pppoe to bind int pppoe_only_equal_svc_name; // Accept only PADI with service-name equal to server int disable_sending_hello; // Disable l2tp sending HELLO message for Apple compatibility. int disable_no_spoof; // Disable no spoof (permit load balancing client --> internet) int nbudpfd; // number UDP file handle int nbmultiaddress; // number multi address to bind int indexlacudpfd; // Index UDP LAC file handle (in udpfd[]) int nbmultihostname; // number hostname, normally the same number as the nbudpfd int no_throttle_local_IP; // no throttle traffic from session to session in_addr_t bind_n_address[MAX_BINDADDR]; in_addr_t iftun_n_address[MAX_BINDADDR]; char bind_multi_address[256]; char multi_hostname[512]; char multi_n_hostname[MAX_NBHOSTNAME][MAXHOSTNAME]; // list hostname struct in6_addr default_ipv6_dns1; struct in6_addr default_ipv6_dns2; uint32_t dhcp6_preferred_lifetime; // preferred lifetime (see rfc3315) uint32_t dhcp6_valid_lifetime; // valid lifetime (see rfc3315) uint32_t dhcp6_server_duid; // DUID of dhcpv6 server (see rfc3315) uint32_t dns6_lifetime; // RDNSS lifetime default 1200 (see rfc6106, rfc4861) (MaxRtrAdvInterval <= Lifetime <= 2*MaxRtrAdvInterval) char default_ipv6_domain_list[255]; } 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 // on slaves, alow BGP to withdraw cleanly before exiting #define QUIT_DELAY 5 // quit actions (master) #define QUIT_FAILOVER 1 // SIGTERM: exit when all control messages have been acked (for cluster failover) #define QUIT_SHUTDOWN 2 // SIGQUIT: shutdown sessions/tunnels, reject new connections // 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 lcp_restart(sessionidt s); 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 processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l); void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra); 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, uint8_t prio, bundleidt bid, uint8_t mp_bits); uint8_t *opt_makeppp(uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits); 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); int join_bundle(sessionidt s); // 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); int rad_tunnel_pwdecode(uint8_t *pl2tpsecret, size_t *pl2tpsecretlen, const char *radiussecret, const uint8_t * auth); // 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 sessionbyipv6new(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, uint16_t indexudpfd); void processipout(uint8_t *buf, int len); 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, const char *command, char **argv, int argc); int cmd_show_hist_idle(struct cli_def *cli, const char *command, char **argv, int argc); int cmd_show_hist_open(struct cli_def *cli, const char *command, char **argv, int argc); tunnelidt lac_new_tunnel(); void lac_tunnelclear(tunnelidt t); void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len); void lac_send_ICRQ(tunnelidt t, sessionidt s); void lac_tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg); #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(); void cli_init_complete(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 bundlet *bundle; 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, FD_TYPE_NETLINK, FD_TYPE_PPPOEDISC, FD_TYPE_PPPOESESS } type; int index; // for RADIUS, BGP, UDP }; #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) #endif /* __L2TPNS_H__ */ l2tpns-2.3.3/l2tpns.spec000066400000000000000000000023221400724550600150410ustar00rootroot00000000000000Summary: A high-speed clustered L2TP LNS Name: l2tpns Version: 2.2.0 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 * Mon Dec 18 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes l2tpns-2.3.3/ll.c000066400000000000000000000036251400724550600135250ustar00rootroot00000000000000// L2TPNS Linked List Stuff #include #include #include #include #include #include #include #include #include #include #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; } l2tpns-2.3.3/ll.h000066400000000000000000000010701400724550600135220ustar00rootroot00000000000000#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__ */ l2tpns-2.3.3/md5.c000066400000000000000000000170061400724550600136010ustar00rootroot00000000000000/* * This is an OpenSSL-compatible implementation of the RSA Data Security, * Inc. MD5 Message-Digest Algorithm. * * Written by Solar Designer 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. */ #include #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 += MD5_BLOCK_SZ; } while (size -= MD5_BLOCK_SZ); 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 = MD5_BLOCK_SZ - 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, MD5_BLOCK_SZ); } if (size >= MD5_BLOCK_SZ) { 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 = MD5_BLOCK_SZ - used; if (free < 8) { memset(&ctx->buffer[used], 0, free); body(ctx, ctx->buffer, MD5_BLOCK_SZ); used = 0; free = MD5_BLOCK_SZ; } 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, MD5_BLOCK_SZ); 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)); } l2tpns-2.3.3/md5.h000066400000000000000000000014231400724550600136020ustar00rootroot00000000000000/* * This is an OpenSSL-compatible implementation of the RSA Data Security, * Inc. MD5 Message-Digest Algorithm. * * Written by Solar Designer in 2001, and placed * in the public domain. See md5.c for more information. */ #ifndef __MD5_H__ #define __MD5_H__ #define MD5_DIGEST_SZ 16 #define MD5_BLOCK_SZ 64 /* 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[MD5_BLOCK_SZ]; MD5_u32plus block[MD5_DIGEST_SZ]; } 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 /* __MD5_H__ */ l2tpns-2.3.3/nsctl.c000066400000000000000000000113531400724550600142360ustar00rootroot00000000000000/* l2tpns plugin control */ #include #include #include #include #include #include #include #include "dhcp6.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; } l2tpns-2.3.3/plugin.h000066400000000000000000000041441400724550600144160ustar00rootroot00000000000000#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_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_timer { time_t time_now; }; struct param_new_session { tunnelt *t; sessiont *s; }; struct param_kill_session { tunnelt *t; sessiont *s; }; struct param_control { int iam_master; int argc; char **argv; // output int response; char *additional; }; 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__ */ l2tpns-2.3.3/ppp.c000066400000000000000000002154771400724550600137270ustar00rootroot00000000000000// L2TPNS PPP Stuff #include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "constants.h" #include "plugin.h" #include "util.h" #include "tbf.h" #include "cluster.h" #include "l2tplac.h" #include "pppoe.h" extern tunnelt *tunnel; extern bundlet *bundle; extern fragmentationt *frag; extern sessiont *session; extern radiust *radius; extern int tunfd; extern char hostname[]; extern uint32_t eth_tx; extern time_t time_now; extern uint64_t time_now_ms; extern configt *config; static int add_lcp_auth(uint8_t *b, int size, int authtype); static bundleidt new_bundle(void); static int epdiscmp(epdist, epdist); static void setepdis(epdist *, epdist); static void ipcp_open(sessionidt s, tunnelidt t); static int first_session_in_bundle(sessionidt s) { bundleidt i; for (i = 1; i < MAXBUNDLE; i++) if (bundle[i].state != BUNDLEFREE) if (epdiscmp(session[s].epdis,bundle[i].epdis) && !strcmp(session[s].user, bundle[i].user)) return 0; return 1; } // Process PAP messages void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { char user[MAXUSER]; char pass[MAXPASS]; uint16_t hl; uint16_t r; CSTAT(processpap); LOG_HEX(5, "PAP", p, l); if (l < 4) { LOG(1, s, t, "Short PAP %u bytes\n", l); STAT(tunnel_rx_errors); sessionshutdown(s, "Short PAP packet.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } if ((hl = ntohs(*(uint16_t *) (p + 2))) > l) { LOG(1, s, t, "Length mismatch PAP %u/%u\n", hl, l); STAT(tunnel_rx_errors); sessionshutdown(s, "PAP length mismatch.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } l = hl; if (*p != 1) { LOG(1, s, t, "Unexpected PAP code %d\n", *p); STAT(tunnel_rx_errors); sessionshutdown(s, "Unexpected PAP code.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } if (session[s].ppp.phase != Authenticate) { LOG(2, s, t, "PAP ignored in %s phase\n", ppp_phase(session[s].ppp.phase)); return; } { uint8_t *b = p; b += 4; user[0] = pass[0] = 0; if (*b && *b < sizeof(user)) { memcpy(user, b + 1, *b); user[*b] = 0; b += 1 + *b; if (*b && *b < sizeof(pass)) { memcpy(pass, b + 1, *b); pass[*b] = 0; } } LOG(3, s, t, "PAP login %s/%s\n", user, pass); } if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, user)) { // Creating a tunnel/session has been started return; } if (session[s].ip || !(r = radiusnew(s))) { // respond now, either no RADIUS available or already authenticated uint8_t b[MAXETHER]; uint8_t id = p[1]; uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP, 0, 0, 0); if (!p) return; if (session[s].ip) *p = 2; // ACK else *p = 3; // cant authorise p[1] = id; *(uint16_t *) (p + 2) = htons(5); // length p[4] = 0; // no message tunnelsend(b, 5 + (p - b), t); // send it if (session[s].ip) { LOG(3, s, t, "Already an IP allocated: %s (%d)\n", fmtaddr(htonl(session[s].ip), 0), session[s].ip_pool_index); } else { LOG(1, s, t, "No RADIUS session available to authenticate session...\n"); sessionshutdown(s, "No free RADIUS sessions.", CDN_UNAVAILABLE, TERM_SERVICE_UNAVAILABLE); } } else { // Run PRE_AUTH plugins struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(user), strdup(pass), PPPPAP, 1 }; run_plugins(PLUGIN_PRE_AUTH, &packet); if (!packet.continue_auth) { LOG(3, s, t, "A plugin rejected PRE_AUTH\n"); if (packet.username) free(packet.username); if (packet.password) free(packet.password); return; } strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1); strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass) - 1); free(packet.username); free(packet.password); radius[r].id = p[1]; LOG(3, s, t, "Sending login for %s/%s to RADIUS\n", user, pass); if ((session[s].mrru) && (!first_session_in_bundle(s))) radiussend(r, RADIUSJUSTAUTH); else radiussend(r, RADIUSAUTH); } } // Process CHAP messages void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { uint16_t r; uint16_t hl; CSTAT(processchap); LOG_HEX(5, "CHAP", p, l); if (l < 4) { LOG(1, s, t, "Short CHAP %u bytes\n", l); STAT(tunnel_rx_errors); sessionshutdown(s, "Short CHAP packet.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } if ((hl = ntohs(*(uint16_t *) (p + 2))) > l) { LOG(1, s, t, "Length mismatch CHAP %u/%u\n", hl, l); STAT(tunnel_rx_errors); sessionshutdown(s, "CHAP length mismatch.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } l = hl; if (*p != 2) { LOG(1, s, t, "Unexpected CHAP response code %d\n", *p); STAT(tunnel_rx_errors); sessionshutdown(s, "CHAP length mismatch.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } if (session[s].ppp.phase != Authenticate) { LOG(2, s, t, "CHAP ignored in %s phase\n", ppp_phase(session[s].ppp.phase)); return; } r = sess_local[s].radius; if (!r) { LOG(3, s, t, "Unexpected CHAP message\n"); // Some modems (Netgear DM602, possibly others) persist in using CHAP even // after ACKing our ConfigReq for PAP. if (sess_local[s].lcp_authtype == AUTHPAP && config->radius_authtypes & AUTHCHAP) { sess_local[s].lcp_authtype = AUTHCHAP; sendchap(s, t); } return; } if (p[1] != radius[r].id) { LOG(1, s, t, "Wrong CHAP response ID %d (should be %d) (%d)\n", p[1], radius[r].id, r); STAT(tunnel_rx_errors); sessionshutdown(s, "Unexpected CHAP response ID.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } if (l < 5 || p[4] != 16) { LOG(1, s, t, "Bad CHAP response length %d\n", l < 5 ? -1 : p[4]); STAT(tunnel_rx_errors); sessionshutdown(s, "Bad CHAP response length.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } l -= 5; p += 5; if (l < 16 || l - 16 >= sizeof(session[s].user)) { LOG(1, s, t, "CHAP user too long %d\n", l - 16); STAT(tunnel_rx_errors); sessionshutdown(s, "CHAP username too long.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } // Run PRE_AUTH plugins { struct param_pre_auth packet = { &tunnel[t], &session[s], NULL, NULL, PPPCHAP, 1 }; packet.password = calloc(17, 1); memcpy(packet.password, p, 16); p += 16; l -= 16; packet.username = calloc(l + 1, 1); memcpy(packet.username, p, l); if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, packet.username)) { free(packet.username); free(packet.password); // Creating a tunnel/session has been started return; } run_plugins(PLUGIN_PRE_AUTH, &packet); if (!packet.continue_auth) { LOG(3, s, t, "A plugin rejected PRE_AUTH\n"); if (packet.username) free(packet.username); if (packet.password) free(packet.password); return; } strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1); memcpy(radius[r].pass, packet.password, 16); free(packet.username); free(packet.password); } radius[r].chap = 1; LOG(3, s, t, "CHAP login %s\n", session[s].user); if ((session[s].mrru) && (!first_session_in_bundle(s))) radiussend(r, RADIUSJUSTAUTH); else radiussend(r, RADIUSAUTH); } static void dumplcp(uint8_t *p, int l) { int x = l - 4; uint8_t *o = (p + 4); LOG_HEX(5, "PPP LCP Packet", p, l); LOG(4, 0, 0, "PPP LCP Packet type %d (%s len %d)\n", *p, ppp_code((int)*p), ntohs( ((uint16_t *) p)[1]) ); LOG(4, 0, 0, "Length: %d\n", l); if (*p != ConfigReq && *p != ConfigRej && *p != ConfigAck) return; while (x > 2) { int type = o[0]; int length = o[1]; if (length < 2) { LOG(4, 0, 0, " Option length is %d...\n", length); break; } if (type == 0) { LOG(4, 0, 0, " Option type is 0...\n"); x -= length; o += length; continue; } switch (type) { case 1: // Maximum-Receive-Unit if (length == 4) LOG(4, 0, 0, " %s %d\n", ppp_lcp_option(type), ntohs(*(uint16_t *)(o + 2))); else LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length); break; case 2: // Async-Control-Character-Map if (length == 6) { uint32_t asyncmap = ntohl(*(uint32_t *)(o + 2)); LOG(4, 0, 0, " %s %x\n", ppp_lcp_option(type), asyncmap); } else LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length); break; case 3: // Authentication-Protocol if (length == 4) { int proto = ntohs(*(uint16_t *)(o + 2)); LOG(4, 0, 0, " %s 0x%x (%s)\n", ppp_lcp_option(type), proto, proto == PPPPAP ? "PAP" : "UNSUPPORTED"); } else if (length == 5) { int proto = ntohs(*(uint16_t *)(o + 2)); int algo = *(o + 4); LOG(4, 0, 0, " %s 0x%x 0x%x (%s)\n", ppp_lcp_option(type), proto, algo, (proto == PPPCHAP && algo == 5) ? "CHAP MD5" : "UNSUPPORTED"); } else LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length); break; case 4: // Quality-Protocol { uint32_t qp = ntohl(*(uint32_t *)(o + 2)); LOG(4, 0, 0, " %s %x\n", ppp_lcp_option(type), qp); } break; case 5: // Magic-Number if (length == 6) { uint32_t magicno = ntohl(*(uint32_t *)(o + 2)); LOG(4, 0, 0, " %s %x\n", ppp_lcp_option(type), magicno); } else LOG(4, 0, 0, " %s odd length %d\n", ppp_lcp_option(type), length); break; case 7: // Protocol-Field-Compression case 8: // Address-And-Control-Field-Compression LOG(4, 0, 0, " %s\n", ppp_lcp_option(type)); break; default: LOG(2, 0, 0, " Unknown PPP LCP Option type %d\n", type); break; } x -= length; o += length; } } void lcp_open(sessionidt s, tunnelidt t) { // transition to Authentication or Network phase: session[s].ppp.phase = sess_local[s].lcp_authtype ? Authenticate : Network; LOG(3, s, t, "LCP: Opened, phase %s\n", ppp_phase(session[s].ppp.phase)); // LCP now Opened change_state(s, lcp, Opened); if (session[s].ppp.phase == Authenticate) { if (sess_local[s].lcp_authtype == AUTHCHAP) sendchap(s, t); } else { if(session[s].bundle == 0 || bundle[session[s].bundle].num_of_links == 1) { // This-Layer-Up sendipcp(s, t); change_state(s, ipcp, RequestSent); // move to passive state for IPv6 (if configured), CCP if (config->ipv6_prefix.s6_addr[0]) change_state(s, ipv6cp, Stopped); else change_state(s, ipv6cp, Closed); change_state(s, ccp, Stopped); } else { sessionidt first_ses = bundle[session[s].bundle].members[0]; LOG(3, s, t, "MPPP: Skipping IPCP negotiation for session:%d, first session of bundle is:%d\n",s,first_ses); ipcp_open(s, t); } } } void lcp_restart(sessionidt s) { session[s].ppp.phase = Establish; // This-Layer-Down change_state(s, ipcp, Dead); change_state(s, ipv6cp, Dead); change_state(s, ccp, Dead); } static uint8_t *ppp_conf_rej(sessionidt s, uint8_t *buf, size_t blen, uint16_t mtype, uint8_t **response, uint8_t *queued, uint8_t *packet, uint8_t *option) { if (!*response || **response != ConfigRej) { queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0); if (!queued) return 0; *queued = ConfigRej; queued += 4; } if ((queued - buf + option[1]) > blen) { LOG(2, s, session[s].tunnel, "PPP overflow for ConfigRej (proto %u, option %u).\n", mtype, *option); return 0; } memcpy(queued, option, option[1]); return queued + option[1]; } static uint8_t *ppp_conf_nak(sessionidt s, uint8_t *buf, size_t blen, uint16_t mtype, uint8_t **response, uint8_t *queued, uint8_t *packet, uint8_t *option, uint8_t *value, size_t vlen) { int *nak_sent; switch (mtype) { case PPPLCP: nak_sent = &sess_local[s].lcp.nak_sent; break; case PPPIPCP: nak_sent = &sess_local[s].ipcp.nak_sent; break; case PPPIPV6CP: nak_sent = &sess_local[s].ipv6cp.nak_sent; break; default: return 0; // ? } if (*response && **response != ConfigNak) { if (*nak_sent < config->ppp_max_failure) // reject queued return queued; return ppp_conf_rej(s, buf, blen, mtype, response, 0, packet, option); } if (!*response) { if (*nak_sent >= config->ppp_max_failure) return ppp_conf_rej(s, buf, blen, mtype, response, 0, packet, option); queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0); if (!queued) return 0; (*nak_sent)++; *queued = ConfigNak; queued += 4; } if ((queued - buf + vlen + 2) > blen) { LOG(2, s, session[s].tunnel, "PPP overflow for ConfigNak (proto %u, option %u).\n", mtype, *option); return 0; } *queued++ = *option; *queued++ = vlen + 2; memcpy(queued, value, vlen); return queued + vlen; } static void ppp_code_rej(sessionidt s, tunnelidt t, uint16_t proto, char *pname, uint8_t *p, uint16_t l, uint8_t *buf, size_t size) { uint8_t *q; int mru = session[s].mru; if (mru < MINMTU) mru = MINMTU; if (mru > size) mru = size; l += 4; if (l > mru) l = mru; q = makeppp(buf, size, 0, 0, s, t, proto, 0, 0, 0); if (!q) return; *q = CodeRej; *(q + 1) = ++sess_local[s].lcp_ident; *(uint16_t *)(q + 2) = htons(l); memcpy(q + 4, p, l - 4); LOG(2, s, t, "Unexpected %s code %s\n", pname, ppp_code(*p)); LOG(3, s, t, "%s: send %s\n", pname, ppp_code(*q)); if (config->debug > 3) dumplcp(q, l); tunnelsend(buf, l + (q - buf), t); } // Process LCP messages void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { uint8_t b[MAXETHER]; uint8_t *q = NULL; uint16_t hl; CSTAT(processlcp); LOG_HEX(5, "LCP", p, l); if (l < 4) { LOG(1, s, t, "Short LCP %d bytes\n", l); STAT(tunnel_rx_errors); return ; } if ((hl = ntohs(*(uint16_t *) (p + 2))) > l) { LOG(1, s, t, "Length mismatch LCP %u/%u\n", hl, l); STAT(tunnel_rx_errors); return ; } l = hl; if (session[s].die) // going down... return; LOG(((*p == EchoReq || *p == EchoReply) ? 4 : 3), s, t, "LCP: recv %s\n", ppp_code(*p)); if (config->debug > 3) dumplcp(p, l); if (*p == ConfigAck) { int x = l - 4; uint8_t *o = (p + 4); int authtype = 0; while (x > 2) { int type = o[0]; int length = o[1]; if (length == 0 || type == 0 || x < length) break; switch (type) { case 3: // Authentication-Protocol { int proto = ntohs(*(uint16_t *)(o + 2)); if (proto == PPPPAP) authtype = AUTHPAP; else if (proto == PPPCHAP && *(o + 4) == 5) authtype = AUTHCHAP; } break; } x -= length; o += length; } if (!session[s].ip && authtype) sess_local[s].lcp_authtype = authtype; switch (session[s].ppp.lcp) { case RequestSent: initialise_restart_count(s, lcp); change_state(s, lcp, AckReceived); break; case AckReceived: case Opened: LOG(2, s, t, "LCP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.lcp)); if (session[s].ppp.lcp == Opened) lcp_restart(s); sendlcp(s, t); change_state(s, lcp, RequestSent); break; case AckSent: lcp_open(s, t); break; default: LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp)); } } else if (*p == ConfigReq) { int x = l - 4; uint8_t *o = (p + 4); uint8_t *response = 0; static uint8_t asyncmap[4] = { 0, 0, 0, 0 }; // all zero static uint8_t authproto[5]; int changed = 0; while (x > 2) { int type = o[0]; int length = o[1]; if (length == 0 || type == 0 || x < length) break; switch (type) { case 1: // Maximum-Receive-Unit { uint16_t mru = ntohs(*(uint16_t *)(o + 2)); if (mru >= MINMTU) { session[s].mru = mru; changed++; break; } LOG(3, s, t, " Remote requesting MRU of %u. Rejecting.\n", mru); mru = htons(MRU); q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, (uint8_t *) &mru, sizeof(mru)); } break; case 2: // Async-Control-Character-Map if (!ntohl(*(uint32_t *)(o + 2))) // all bits zero is OK break; LOG(3, s, t, " Remote requesting asyncmap. Rejecting.\n"); q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, asyncmap, sizeof(asyncmap)); break; case 3: // Authentication-Protocol { int proto = ntohs(*(uint16_t *)(o + 2)); char proto_name[] = "0x0000"; int alen; if (proto == PPPPAP) { if (config->radius_authtypes & AUTHPAP) { sess_local[s].lcp_authtype = AUTHPAP; break; } strcpy(proto_name, "PAP"); } else if (proto == PPPCHAP) { if (config->radius_authtypes & AUTHCHAP && *(o + 4) == 5) // MD5 { sess_local[s].lcp_authtype = AUTHCHAP; break; } strcpy(proto_name, "CHAP"); } else sprintf(proto_name, "%#4.4x", proto); LOG(3, s, t, " Remote requesting %s authentication. Rejecting.\n", proto_name); alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authprefer); if (alen < 2) break; // paranoia q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, authproto + 2, alen - 2); if (q && *response == ConfigNak && config->radius_authtypes != config->radius_authprefer) { // alternate type alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authtypes & ~config->radius_authprefer); if (alen < 2) break; q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, authproto + 2, alen - 2); } break; } break; case 4: // Quality-Protocol case 5: // Magic-Number case 7: // Protocol-Field-Compression case 8: // Address-And-Control-Field-Compression break; case 17: // Multilink Max-Receive-Reconstructed-Unit { uint16_t mrru = ntohs(*(uint16_t *)(o + 2)); session[s].mrru = mrru; changed++; LOG(3, s, t, " Received PPP LCP option MRRU: %d\n",mrru); } break; case 18: // Multilink Short Sequence Number Header Format { session[s].mssf = 1; changed++; LOG(3, s, t, " Received PPP LCP option MSSN format\n"); } break; case 19: // Multilink Endpoint Discriminator { uint8_t epdis_class = o[2]; int addr; session[s].epdis.addr_class = epdis_class; session[s].epdis.length = length - 3; if (session[s].epdis.length > 20) { LOG(1, s, t, "Error: received EndDis Address Length more than 20: %d\n", session[s].epdis.length); session[s].epdis.length = 20; } for (addr = 0; addr < session[s].epdis.length; addr++) session[s].epdis.address[addr] = o[3+addr]; changed++; switch (epdis_class) { case LOCALADDR: LOG(3, s, t, " Received PPP LCP option Multilink EndDis Local Address Class: %d\n",epdis_class); break; case IPADDR: LOG(3, s, t, " Received PPP LCP option Multilink EndDis IP Address Class: %d\n",epdis_class); break; case IEEEMACADDR: LOG(3, s, t, " Received PPP LCP option Multilink EndDis IEEE MAC Address Class: %d\n",epdis_class); break; case PPPMAGIC: LOG(3, s, t, " Received PPP LCP option Multilink EndDis PPP Magic No Class: %d\n",epdis_class); break; case PSNDN: LOG(3, s, t, " Received PPP LCP option Multilink EndDis PSND No Class: %d\n",epdis_class); break; default: LOG(3, s, t, " Received PPP LCP option Multilink EndDis NULL Class %d\n",epdis_class); } } break; default: // Reject any unknown options LOG(3, s, t, " Rejecting unknown PPP LCP option %d\n", type); q = ppp_conf_rej(s, b, sizeof(b), PPPLCP, &response, q, p, o); } x -= length; o += length; } if (changed) cluster_send_session(s); if (response) { l = q - response; // LCP packet length *((uint16_t *) (response + 2)) = htons(l); // update header } else { // Send packet back as ConfigAck response = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0); if (!response) return; *response = ConfigAck; } switch (session[s].ppp.lcp) { case Closed: response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP, 0, 0, 0); if (!response) return; *response = TerminateAck; *((uint16_t *) (response + 2)) = htons(l = 4); break; case Stopped: initialise_restart_count(s, lcp); sendlcp(s, t); if (*response == ConfigAck) change_state(s, lcp, AckSent); else change_state(s, lcp, RequestSent); break; case RequestSent: if (*response == ConfigAck) change_state(s, lcp, AckSent); break; case AckReceived: if (*response == ConfigAck) lcp_open(s, t); break; case Opened: lcp_restart(s); sendlcp(s, t); /* fallthrough */ case AckSent: if (*response == ConfigAck) change_state(s, lcp, AckSent); else change_state(s, lcp, RequestSent); break; case Closing: sessionshutdown(s, "LCP: ConfigReq in state Closing. This should not happen. Killing session.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); break; default: LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp)); return; } LOG(3, s, t, "LCP: send %s\n", ppp_code(*response)); if (config->debug > 3) dumplcp(response, l); tunnelsend(b, l + (response - b), t); } else if (*p == ConfigNak || *p == ConfigRej) { int x = l - 4; uint8_t *o = (p + 4); int authtype = -1; while (x > 2) { int type = o[0]; int length = o[1]; if (length == 0 || type == 0 || x < length) break; switch (type) { case 1: // Maximum-Receive-Unit if (*p == ConfigNak) { if (length < 4) break; sess_local[s].ppp_mru = ntohs(*(uint16_t *)(o + 2)); LOG(3, s, t, " Remote requested MRU of %u\n", sess_local[s].ppp_mru); } else { sess_local[s].ppp_mru = 0; LOG(3, s, t, " Remote rejected MRU negotiation\n"); } break; case 3: // Authentication-Protocol if (authtype > 0) break; if (*p == ConfigNak) { int proto; if (length < 4) break; proto = ntohs(*(uint16_t *)(o + 2)); if (proto == PPPPAP) { authtype = config->radius_authtypes & AUTHPAP; LOG(3, s, t, " Remote requested PAP authentication...%sing\n", authtype ? "accept" : "reject"); } else if (proto == PPPCHAP && length > 4 && *(o + 4) == 5) { authtype = config->radius_authtypes & AUTHCHAP; LOG(3, s, t, " Remote requested CHAP authentication...%sing\n", authtype ? "accept" : "reject"); } else { LOG(3, s, t, " Rejecting unsupported authentication %#4x\n", proto); } } else { LOG(2, s, t, "LCP: remote rejected auth negotiation\n"); authtype = 0; // shutdown } break; case 5: // Magic-Number session[s].magic = 0; if (*p == ConfigNak) { if (length < 6) break; session[s].magic = ntohl(*(uint32_t *)(o + 2)); } if (session[s].magic) LOG(3, s, t, " Remote requested magic-no %x\n", session[s].magic); else LOG(3, s, t, " Remote rejected magic-no\n"); cluster_send_session(s); break; case 17: // Multilink Max-Receive-Reconstructed-Unit { if (*p == ConfigNak) { sess_local[s].mp_mrru = ntohs(*(uint16_t *)(o + 2)); LOG(3, s, t, " Remote requested MRRU of %u\n", sess_local[s].mp_mrru); } else { sess_local[s].mp_mrru = 0; LOG(3, s, t, " Remote rejected MRRU negotiation\n"); } } break; case 18: // Multilink Short Sequence Number Header Format { if (*p == ConfigNak) { sess_local[s].mp_mssf = 0; LOG(3, s, t, " Remote requested Naked mssf\n"); } else { sess_local[s].mp_mssf = 0; LOG(3, s, t, " Remote rejected mssf\n"); } } break; case 19: // Multilink Endpoint Discriminator { if (*p == ConfigNak) { LOG(2, s, t, " Remote should not configNak Endpoint Dis!\n"); } else { sess_local[s].mp_epdis = 0; LOG(3, s, t, " Remote rejected Endpoint Discriminator\n"); } } break; default: LOG(2, s, t, "LCP: remote sent %s for type %u?\n", ppp_code(*p), type); sessionshutdown(s, "Unable to negotiate LCP.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } x -= length; o += length; } if (!authtype) { sessionshutdown(s, "Unsupported authentication.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } if (authtype > 0) sess_local[s].lcp_authtype = authtype; switch (session[s].ppp.lcp) { case Closed: case Stopped: { uint8_t *response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP, 0, 0, 0); if (!response) return; *response = TerminateAck; *((uint16_t *) (response + 2)) = htons(l = 4); LOG(3, s, t, "LCP: send %s\n", ppp_code(*response)); if (config->debug > 3) dumplcp(response, l); tunnelsend(b, l + (response - b), t); } break; case RequestSent: case AckSent: initialise_restart_count(s, lcp); sendlcp(s, t); break; case AckReceived: LOG(2, s, t, "LCP: ConfigNak in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.lcp)); sendlcp(s, t); break; case Opened: lcp_restart(s); sendlcp(s, t); break; default: LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp)); return; } } else if (*p == TerminateReq) { switch (session[s].ppp.lcp) { case Closed: case Stopped: case Closing: case Stopping: case RequestSent: case AckReceived: case AckSent: break; case Opened: lcp_restart(s); zero_restart_count(s, lcp); change_state(s, lcp, Closing); break; default: LOG(2, s, t, "LCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.lcp)); return; } *p = TerminateAck; // send ack q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0); if (!q) return; LOG(3, s, t, "LCP: send %s\n", ppp_code(*q)); if (config->debug > 3) dumplcp(q, l); tunnelsend(b, l + (q - b), t); // send it } else if (*p == ProtocolRej) { uint16_t proto = 0; if (l > 4) { proto = *(p+4); if (l > 5 && !(proto & 1)) { proto <<= 8; proto |= *(p+5); } } if (proto == PPPIPV6CP) { LOG(3, s, t, "IPv6 rejected\n"); change_state(s, ipv6cp, Closed); } else { LOG(3, s, t, "LCP protocol reject: 0x%04X\n", proto); } } else if (*p == EchoReq) { *p = EchoReply; // reply *(uint32_t *) (p + 4) = htonl(session[s].magic); // our magic number q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0); if (!q) return; LOG(4, s, t, "LCP: send %s\n", ppp_code(*q)); if (config->debug > 3) dumplcp(q, l); tunnelsend(b, l + (q - b), t); // send it if (session[s].ppp.phase == Network && session[s].ppp.ipv6cp == Opened) send_ipv6_ra(s, t, NULL); // send a RA } else if (*p == EchoReply) { // Ignore it, last_packet time is set earlier than this. } else if (*p != CodeRej) { ppp_code_rej(s, t, PPPLCP, "LCP", p, l, b, sizeof(b)); } } int join_bundle(sessionidt s) { // Search for a bundle to join bundleidt i; bundleidt b; for (i = 1; i < MAXBUNDLE; i++) { if (bundle[i].state != BUNDLEFREE) { if (epdiscmp(session[s].epdis,bundle[i].epdis) && !strcmp(session[s].user, bundle[i].user)) { sessionidt first_ses = bundle[i].members[0]; if (bundle[i].mssf != session[s].mssf) { // uniformity of sequence number format must be insured LOG(3, s, session[s].tunnel, "MPPP: unable to bundle session %d in bundle %d cause of different mssf\n", s, i); return -1; } session[s].bundle = i; session[s].ip = session[first_ses].ip; session[s].dns1 = session[first_ses].dns1; session[s].dns2 = session[first_ses].dns2; session[s].timeout = session[first_ses].timeout; if(session[s].epdis.length > 0) setepdis(&bundle[i].epdis, session[s].epdis); strcpy(bundle[i].user, session[s].user); bundle[i].members[bundle[i].num_of_links] = s; bundle[i].num_of_links++; LOG(3, s, session[s].tunnel, "MPPP: Bundling additional line in bundle (%d), lines:%d\n",i,bundle[i].num_of_links); return i; } } } // No previously created bundle was found for this session, so create a new one if (!(b = new_bundle())) return 0; session[s].bundle = b; bundle[b].mrru = session[s].mrru; bundle[b].mssf = session[s].mssf; // FIXME !!! to enable l2tpns reading mssf frames receiver_max_seq, sender_max_seq must be introduce // now session[s].mssf flag indecates that the receiver wish to receive frames in mssf, so max_seq (i.e. recv_max_seq) = 1<<24 /* if (bundle[b].mssf) bundle[b].max_seq = 1 << 12; else */ bundle[b].max_seq = 1 << 24; if(session[s].epdis.length > 0) setepdis(&bundle[b].epdis, session[s].epdis); strcpy(bundle[b].user, session[s].user); bundle[b].members[0] = s; bundle[b].timeout = session[s].timeout; LOG(3, s, session[s].tunnel, "MPPP: Created a new bundle (%d)\n", b); return b; } static int epdiscmp(epdist ep1, epdist ep2) { int ad; if (ep1.length != ep2.length) return 0; if (ep1.addr_class != ep2.addr_class) return 0; for (ad = 0; ad < ep1.length; ad++) if (ep1.address[ad] != ep2.address[ad]) return 0; return 1; } static void setepdis(epdist *ep1, epdist ep2) { int ad; ep1->length = ep2.length; ep1->addr_class = ep2.addr_class; for (ad = 0; ad < ep2.length; ad++) ep1->address[ad] = ep2.address[ad]; } static bundleidt new_bundle() { bundleidt i; for (i = 1; i < MAXBUNDLE; i++) { if (bundle[i].state == BUNDLEFREE) { LOG(4, 0, 0, "MPPP: Assigning bundle ID %d\n", i); bundle[i].num_of_links = 1; bundle[i].last_check = time_now; // Initialize last_check value bundle[i].state = BUNDLEOPEN; bundle[i].current_ses = -1; // This is to enforce the first session 0 to be used at first memset(&frag[i], 0, sizeof(fragmentationt)); if (i > config->cluster_highest_bundleid) config->cluster_highest_bundleid = i; return i; } } LOG(0, 0, 0, "MPPP: Can't find a free bundle! There shouldn't be this many in use!\n"); return 0; } static void ipcp_open(sessionidt s, tunnelidt t) { LOG(3, s, t, "IPCP: Opened, session is now active\n"); change_state(s, ipcp, Opened); if (!(session[s].walled_garden || session[s].flags & SESSION_STARTED)) { uint16_t r = radiusnew(s); if (r) { radiussend(r, RADIUSSTART); // send radius start // don't send further Start records if IPCP is restarted session[s].flags |= SESSION_STARTED; cluster_send_session(s); } } // start IPv6 if configured and still in passive state if (session[s].ppp.ipv6cp == Stopped) { sendipv6cp(s, t); change_state(s, ipv6cp, RequestSent); } } // Process IPCP messages void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { uint8_t b[MAXETHER]; uint8_t *q = 0; uint16_t hl; CSTAT(processipcp); LOG_HEX(5, "IPCP", p, l); if (l < 4) { LOG(1, s, t, "Short IPCP %d bytes\n", l); STAT(tunnel_rx_errors); return ; } if ((hl = ntohs(*(uint16_t *) (p + 2))) > l) { LOG(1, s, t, "Length mismatch IPCP %u/%u\n", hl, l); STAT(tunnel_rx_errors); return ; } l = hl; if (session[s].ppp.phase < Network) { LOG(2, s, t, "IPCP %s ignored in %s phase\n", ppp_code(*p), ppp_phase(session[s].ppp.phase)); return; } LOG(3, s, t, "IPCP: recv %s\n", ppp_code(*p)); if (*p == ConfigAck) { switch (session[s].ppp.ipcp) { case RequestSent: initialise_restart_count(s, ipcp); change_state(s, ipcp, AckReceived); break; case AckReceived: case Opened: LOG(2, s, t, "IPCP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.ipcp)); sendipcp(s, t); change_state(s, ipcp, RequestSent); break; case AckSent: ipcp_open(s, t); break; default: LOG(2, s, t, "IPCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipcp)); } } else if (*p == ConfigReq) { uint8_t *response = 0; uint8_t *o = p + 4; int length = l - 4; int gotip = 0; in_addr_t addr; while (length > 2) { if (!o[1] || o[1] > length) return; switch (*o) { case 3: // ip address gotip++; // seen address if (o[1] != 6) return; addr = htonl(session[s].ip); if (memcmp(o + 2, &addr, (sizeof addr))) { uint8_t *oq = q; q = ppp_conf_nak(s, b, sizeof(b), PPPIPCP, &response, q, p, o, (uint8_t *) &addr, sizeof(addr)); if (!q || (q != oq && *response == ConfigRej)) { sessionshutdown(s, "Can't negotiate IPCP.", CDN_ADMIN_DISC, TERM_USER_ERROR); return; } } break; case 129: // primary DNS if (o[1] != 6) return; addr = htonl(session[s].dns1); if (memcmp(o + 2, &addr, (sizeof addr))) { q = ppp_conf_nak(s, b, sizeof(b), PPPIPCP, &response, q, p, o, (uint8_t *) &addr, sizeof(addr)); if (!q) return; } break; case 131: // secondary DNS if (o[1] != 6) return; addr = htonl(session[s].dns2); if (memcmp(o + 2, &addr, sizeof(addr))) { q = ppp_conf_nak(s, b, sizeof(b), PPPIPCP, &response, q, p, o, (uint8_t *) &addr, sizeof(addr)); if (!q) return; } break; default: LOG(2, s, t, " Rejecting PPP IPCP Option type %d\n", *o); q = ppp_conf_rej(s, b, sizeof(b), PPPIPCP, &response, q, p, o); if (!q) return; } length -= o[1]; o += o[1]; } if (response) { l = q - response; // IPCP packet length *((uint16_t *) (response + 2)) = htons(l); // update header } else if (gotip) { // Send packet back as ConfigAck response = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0); if (!response) return; *response = ConfigAck; } else { LOG(1, s, t, "No IP in IPCP request\n"); STAT(tunnel_rx_errors); return; } switch (session[s].ppp.ipcp) { case Closed: response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPCP, 0, 0, 0); if (!response) return; *response = TerminateAck; *((uint16_t *) (response + 2)) = htons(l = 4); break; case Stopped: initialise_restart_count(s, ipcp); sendipcp(s, t); if (*response == ConfigAck) change_state(s, ipcp, AckSent); else change_state(s, ipcp, RequestSent); break; case RequestSent: if (*response == ConfigAck) change_state(s, ipcp, AckSent); break; case AckReceived: if (*response == ConfigAck) ipcp_open(s, t); break; case Opened: initialise_restart_count(s, ipcp); sendipcp(s, t); /* fallthrough */ case AckSent: if (*response == ConfigAck) change_state(s, ipcp, AckSent); else change_state(s, ipcp, RequestSent); break; default: LOG(2, s, t, "IPCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipcp)); return; } LOG(3, s, t, "IPCP: send %s\n", ppp_code(*response)); tunnelsend(b, l + (response - b), t); } else if (*p == TerminateReq) { switch (session[s].ppp.ipcp) { case Closed: case Stopped: case Closing: case Stopping: case RequestSent: case AckReceived: case AckSent: break; case Opened: zero_restart_count(s, ipcp); change_state(s, ipcp, Closing); break; default: LOG(2, s, t, "IPCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipcp)); return; } *p = TerminateAck; // send ack q = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0); if (!q) return; LOG(3, s, t, "IPCP: send %s\n", ppp_code(*q)); tunnelsend(b, l + (q - b), t); // send it } else if (*p != CodeRej) { ppp_code_rej(s, t, PPPIPCP, "IPCP", p, l, b, sizeof(b)); } } static void ipv6cp_open(sessionidt s, tunnelidt t) { int i; LOG(3, s, t, "IPV6CP: Opened\n"); change_state(s, ipv6cp, Opened); for (i = 0; i < MAXROUTE6 && session[s].route6[i].ipv6prefixlen; i++) { route6set(s, session[s].route6[i].ipv6route, session[s].route6[i].ipv6prefixlen, 1); } if (session[s].ipv6address.s6_addr[0]) { // Check if included in prefix if (sessionbyipv6(session[s].ipv6address) != s) route6set(s, session[s].ipv6address, 128, 1); } // Send an initial RA (TODO: Should we send these regularly?) send_ipv6_ra(s, t, NULL); } // Process IPV6CP messages void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { uint8_t b[MAXETHER]; uint8_t *q = 0; uint16_t hl; CSTAT(processipv6cp); LOG_HEX(5, "IPV6CP", p, l); if (l < 4) { LOG(1, s, t, "Short IPV6CP %d bytes\n", l); STAT(tunnel_rx_errors); return ; } if ((hl = ntohs(*(uint16_t *) (p + 2))) > l) { LOG(1, s, t, "Length mismatch IPV6CP %u/%u\n", hl, l); STAT(tunnel_rx_errors); return ; } l = hl; if (session[s].ppp.phase < Network) { LOG(2, s, t, "IPV6CP %s ignored in %s phase\n", ppp_code(*p), ppp_phase(session[s].ppp.phase)); return; } LOG(3, s, t, "IPV6CP: recv %s\n", ppp_code(*p)); if (!session[s].ip) { LOG(3, s, t, "IPV6CP: no IPv4 address (IPCP in state %s)\n", ppp_state(session[s].ppp.ipcp)); return; // need IPCP to complete... } if (*p == ConfigAck) { switch (session[s].ppp.ipv6cp) { case RequestSent: initialise_restart_count(s, ipv6cp); change_state(s, ipv6cp, AckReceived); break; case AckReceived: case Opened: LOG(2, s, t, "IPV6CP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.ipv6cp)); sendipv6cp(s, t); change_state(s, ipv6cp, RequestSent); break; case AckSent: ipv6cp_open(s, t); break; default: LOG(2, s, t, "IPV6CP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipv6cp)); } } else if (*p == ConfigReq) { uint8_t *response = 0; uint8_t *o = p + 4; int length = l - 4; int gotip = 0; uint32_t ident[2]; while (length > 2) { if (!o[1] || o[1] > length) return; switch (*o) { case 1: // interface identifier gotip++; // seen address if (o[1] != 10) return; if (session[s].ipv6address.s6_addr[0]) { // LSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address) memcpy(&ident[0], &session[s].ipv6address.s6_addr[8], 8); } else { ident[0] = htonl(session[s].ip); ident[1] = 0; } if (memcmp(o + 2, ident, sizeof(ident))) { q = ppp_conf_nak(s, b, sizeof(b), PPPIPV6CP, &response, q, p, o, (uint8_t *)ident, sizeof(ident)); if (!q) return; } break; default: LOG(2, s, t, " Rejecting PPP IPV6CP Option type %d\n", *o); q = ppp_conf_rej(s, b, sizeof(b), PPPIPV6CP, &response, q, p, o); if (!q) return; } length -= o[1]; o += o[1]; } if (response) { l = q - response; // IPV6CP packet length *((uint16_t *) (response + 2)) = htons(l); // update header } else if (gotip) { // Send packet back as ConfigAck response = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0); if (!response) return; *response = ConfigAck; } else { LOG(1, s, t, "No interface identifier in IPV6CP request\n"); STAT(tunnel_rx_errors); return; } switch (session[s].ppp.ipv6cp) { case Closed: response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPV6CP, 0, 0, 0); if (!response) return; *response = TerminateAck; *((uint16_t *) (response + 2)) = htons(l = 4); break; case Stopped: initialise_restart_count(s, ipv6cp); sendipv6cp(s, t); if (*response == ConfigAck) change_state(s, ipv6cp, AckSent); else change_state(s, ipv6cp, RequestSent); break; case RequestSent: if (*response == ConfigAck) change_state(s, ipv6cp, AckSent); break; case AckReceived: if (*response == ConfigAck) ipv6cp_open(s, t); break; case Opened: initialise_restart_count(s, ipv6cp); sendipv6cp(s, t); /* fallthrough */ case AckSent: if (*response == ConfigAck) change_state(s, ipv6cp, AckSent); else change_state(s, ipv6cp, RequestSent); break; default: LOG(2, s, t, "IPV6CP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipv6cp)); return; } LOG(3, s, t, "IPV6CP: send %s\n", ppp_code(*response)); tunnelsend(b, l + (response - b), t); } else if (*p == TerminateReq) { switch (session[s].ppp.ipv6cp) { case Closed: case Stopped: case Closing: case Stopping: case RequestSent: case AckReceived: case AckSent: break; case Opened: zero_restart_count(s, ipv6cp); change_state(s, ipv6cp, Closing); break; default: LOG(2, s, t, "IPV6CP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ipv6cp)); return; } *p = TerminateAck; // send ack q = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0); if (!q) return; LOG(3, s, t, "IPV6CP: send %s\n", ppp_code(*q)); tunnelsend(b, l + (q - b), t); // send it } else if (*p != CodeRej) { ppp_code_rej(s, t, PPPIPV6CP, "IPV6CP", p, l, b, sizeof(b)); } } static void update_sessions_in_stat(sessionidt s, uint16_t l) { bundleidt b = session[s].bundle; if (!b) { increment_counter(&session[s].cin, &session[s].cin_wrap, l); session[s].cin_delta += l; session[s].pin++; sess_local[s].cin += l; sess_local[s].pin++; } else { int i = frag[b].re_frame_begin_index; int end = frag[b].re_frame_end_index; for (;;) { l = frag[b].fragment[i].length; s = frag[b].fragment[i].sid; increment_counter(&session[s].cin, &session[s].cin_wrap, l); session[s].cin_delta += l; session[s].pin++; sess_local[s].cin += l; sess_local[s].pin++; if (i == end) return; i = (i + 1) & MAXFRAGNUM_MASK; } } } // process IP packet received // // This MUST be called with at least 4 byte behind 'p'. // (i.e. this routine writes to p[-4]). void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { in_addr_t ip, ip_dst; CSTAT(processipin); LOG_HEX(5, "IP", p, l); if (l < 20) { LOG(1, s, t, "IP packet too short %d\n", l); STAT(tunnel_rx_errors); return ; } ip = ntohl(*(uint32_t *)(p + 12)); ip_dst = *(uint32_t *)(p + 16); if (l > MAXETHER) { LOG(1, s, t, "IP packet too long %d\n", l); STAT(tunnel_rx_errors); return ; } if (session[s].ppp.phase != Network || session[s].ppp.ipcp != Opened) return; if (!session[s].bundle || bundle[session[s].bundle].num_of_links < 2) // FIXME: { // no spoof (do sessionbyip to handled statically routed subnets) if (!config->disable_no_spoof && ip != session[s].ip && sessionbyip(htonl(ip)) != s) { LOG(4, s, t, "Dropping packet with spoofed IP %s\n", fmtaddr(htonl(ip), 0)); return; } } // run access-list if any if (session[s].filter_in && !ip_filter(p, l, session[s].filter_in - 1)) return; // adjust MSS on SYN and SYN,ACK packets with options if ((ntohs(*(uint16_t *) (p + 6)) & 0x1fff) == 0 && p[9] == IPPROTO_TCP) // first tcp fragment { int ihl = (p[0] & 0xf) * 4; // length of IP header if (l >= ihl + 20 && (p[ihl + 13] & TCP_FLAG_SYN) && ((p[ihl + 12] >> 4) > 5)) adjust_tcp_mss(s, t, p, l, p + ihl); } // Add on the tun header p -= 4; *(uint32_t *) p = htonl(PKTIP); l += 4; if (session[s].tbf_in) { if (!config->no_throttle_local_IP || !sessionbyip(ip_dst)) { // Are we throttling this session? if (config->cluster_iam_master) tbf_queue_packet(session[s].tbf_in, p, l); else master_throttle_packet(session[s].tbf_in, p, l); return; } } // send to ethernet if (tun_write(p, l) < 0) { STAT(tun_tx_errors); LOG(0, s, t, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n", l, strerror(errno), tunfd, p); return; } p += 4; l -= 4; if (session[s].snoop_ip && session[s].snoop_port) { // Snooping this session snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port); } update_sessions_in_stat(s, l); eth_tx += l; STAT(tun_tx_packets); INC_STAT(tun_tx_bytes, l); } // process Multilink PPP packet received void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { bundleidt b = session[s].bundle; bundlet * this_bundle = &bundle[b]; uint32_t maskSeq, max_seq; int frag_offset; uint16_t frag_index, frag_index_next, frag_index_prev; fragmentationt *this_fragmentation = &frag[b]; uint8_t begin_frame = (*p & MP_BEGIN); uint8_t end_frame = (*p & MP_END); uint32_t seq_num, seq_num_next, seq_num_prev; uint32_t i; uint8_t flags = *p; uint16_t begin_index, end_index; // Perform length checking if(l > MAXFRAGLEN) { LOG(2, s, t, "MPPP: discarding fragment larger than MAXFRAGLEN\n"); return; } if(!b) { LOG(2, s, t, "MPPP: Invalid bundle id: 0\n"); return; } // FIXME !! session[s].mssf means that the receiver wants to receive frames in mssf not means the receiver will send frames in mssf /* if(session[s].mssf) { // Get 12 bit for seq number seq_num = ntohs((*(uint16_t *) p) & 0xFF0F); p += 2; l -= 2; // After this point the pointer should be advanced 2 bytes LOG(3, s, t, "MPPP: 12 bits, sequence number: %d\n",seq_num); } else */ { // Get 24 bit for seq number seq_num = ntohl((*(uint32_t *) p) & 0xFFFFFF00); p += 4; l -= 4; // After this point the pointer should be advanced 4 bytes LOG(4, s, t, "MPPP: 24 bits sequence number:%d\n",seq_num); } max_seq = this_bundle->max_seq; maskSeq = max_seq - 1; /* * Expand sequence number to 32 bits, making it as close * as possible to this_fragmentation->M. */ seq_num |= this_fragmentation->M & ~maskSeq; if ((int)(this_fragmentation->M - seq_num) > (int)(maskSeq >> 1)) { seq_num += maskSeq + 1; } else if ((int)(seq_num - this_fragmentation->M) > (int)(maskSeq >> 1)) { seq_num -= maskSeq + 1; /* should never happen */ } // calculate this fragment's offset from the begin seq in the bundle frag_offset = (int) (seq_num - this_fragmentation->start_seq); sess_local[s].last_seq = seq_num; // calculate the jitter average uint32_t ljitter = time_now_ms - sess_local[s].prev_time; if (ljitter > 0) { sess_local[s].jitteravg = (sess_local[s].jitteravg + ljitter)>>1; sess_local[s].prev_time = time_now_ms; } uint32_t Mmin; if (seq_num < this_fragmentation->M) { Mmin = seq_num; this_fragmentation->M = seq_num; } else { Mmin = sess_local[(this_bundle->members[0])].last_seq; for (i = 1; i < this_bundle->num_of_links; i++) { uint32_t s_seq = sess_local[(this_bundle->members[i])].last_seq; if (s_seq < Mmin) Mmin = s_seq; } this_fragmentation->M = Mmin; } // calculate M offset of the M seq in the bundle int M_offset = (int) (Mmin - this_fragmentation->start_seq); if (M_offset >= MAXFRAGNUM) { // There have a long break of the link !!!!!!!! // M_offset is bigger that the fragmentation buffer size LOG(3, s, t, "MPPP: M_offset out of range, min:%d, begin_seq:%d\n", Mmin, this_fragmentation->start_seq); // Calculate the new start index, the previous frag are lost begin_index = (M_offset + this_fragmentation->start_index) & MAXFRAGNUM_MASK; // Set new Start sequence this_fragmentation->start_index = begin_index; this_fragmentation->start_seq = Mmin; M_offset = 0; // recalculate the fragment offset from the new begin seq in the bundle frag_offset = (int) (seq_num - Mmin); } // discard this fragment if the packet comes before the start sequence if (frag_offset < 0) { // this packet comes before the next LOG(3, s, t, "MPPP: (COMES BEFORE) the next, seq:%d, begin_seq:%d, size_frag:%d, flags:%02X is LOST\n", seq_num, this_fragmentation->start_seq, l, flags); return; } // discard if frag_offset is bigger that the fragmentation buffer size if (frag_offset >= MAXFRAGNUM) { // frag_offset is bigger that the fragmentation buffer size LOG(3, s, t, "MPPP: Index out of range, seq:%d, begin_seq:%d\n", seq_num, this_fragmentation->start_seq); return; } //caculate received fragment's index in the fragment array frag_index = (frag_offset + this_fragmentation->start_index) & MAXFRAGNUM_MASK; // insert the frame in it's place fragmentt *this_frag = &this_fragmentation->fragment[frag_index]; if (this_frag->length > 0) // This fragment is lost, It was around the buffer and it was never completed the packet. LOG(3, this_frag->sid, this_frag->tid, "MPPP: (INSERT) seq_num:%d frag_index:%d flags:%02X is LOST\n", this_frag->seq, frag_index, this_frag->flags); this_frag->length = l; this_frag->sid = s; this_frag->tid = t; this_frag->flags = flags; this_frag->seq = seq_num; this_frag->jitteravg = sess_local[s].jitteravg; memcpy(this_frag->data, p, l); LOG(4, s, t, "MPPP: seq_num:%d frag_index:%d INSERTED flags: %02X\n", seq_num, frag_index, flags); //next frag index frag_index_next = (frag_index + 1) & MAXFRAGNUM_MASK; //previous frag index frag_index_prev = (frag_index - 1) & MAXFRAGNUM_MASK; // next seq seq_num_next = seq_num + 1; // previous seq seq_num_prev = seq_num - 1; // Clean the buffer and log the lost fragments if ((frag_index_next != this_fragmentation->start_index) && this_fragmentation->fragment[frag_index_next].length) { // check if the next frag is a lost fragment if (this_fragmentation->fragment[frag_index_next].seq != seq_num_next) { // This fragment is lost, It was around the buffer and it was never completed the packet. LOG(3, this_fragmentation->fragment[frag_index_next].sid, this_fragmentation->fragment[frag_index_next].tid, "MPPP: (NEXT) seq_num:%d frag_index:%d flags:%02X is LOST\n", this_fragmentation->fragment[frag_index_next].seq, frag_index_next, this_fragmentation->fragment[frag_index_next].flags); // this frag is lost this_fragmentation->fragment[frag_index_next].length = 0; this_fragmentation->fragment[frag_index_next].flags = 0; if (begin_frame && (!end_frame)) return; // assembling frame failed } } // Clean the buffer and log the lost fragments if ((frag_index != this_fragmentation->start_index) && this_fragmentation->fragment[frag_index_prev].length) { // check if the next frag is a lost fragment if (this_fragmentation->fragment[frag_index_prev].seq != seq_num_prev) { // This fragment is lost, It was around the buffer and it was never completed the packet. LOG(3, this_fragmentation->fragment[frag_index_prev].sid, this_fragmentation->fragment[frag_index_prev].tid, "MPPP: (PREV) seq_num:%d frag_index:%d flags:%02X is LOST\n", this_fragmentation->fragment[frag_index_prev].seq, frag_index_prev, this_fragmentation->fragment[frag_index_prev].flags); this_fragmentation->fragment[frag_index_prev].length = 0; this_fragmentation->fragment[frag_index_prev].flags = 0; if (end_frame && (!begin_frame)) return; // assembling frame failed } } find_frame: begin_index = this_fragmentation->start_index; uint32_t b_seq = this_fragmentation->start_seq; // Try to find a Begin sequence from the start_seq sequence to M sequence while (b_seq < Mmin) { if (this_fragmentation->fragment[begin_index].length) { if (b_seq == this_fragmentation->fragment[begin_index].seq) { if (this_fragmentation->fragment[begin_index].flags & MP_BEGIN) { int isfoundE = 0; // Adjust the new start sequence and start index this_fragmentation->start_index = begin_index; this_fragmentation->start_seq = b_seq; // Begin Sequence found, now try to found the End Sequence to complete the frame end_index = begin_index; while (b_seq < Mmin) { if (this_fragmentation->fragment[end_index].length) { if (b_seq == this_fragmentation->fragment[end_index].seq) { if (this_fragmentation->fragment[end_index].flags & MP_END) { // The End sequence was found and the frame is complete isfoundE = 1; break; } } else { // This fragment is lost, it was never completed the packet. LOG(3, this_fragmentation->fragment[end_index].sid, this_fragmentation->fragment[end_index].tid, "MPPP: (FIND END) seq_num:%d frag_index:%d flags:%02X is LOST\n", this_fragmentation->fragment[end_index].seq, begin_index, this_fragmentation->fragment[end_index].flags); // this frag is lost this_fragmentation->fragment[end_index].length = 0; this_fragmentation->fragment[end_index].flags = 0; // This frame is not complete find the next Begin break; } } else { // This frame is not complete find the next Begin if exist break; } end_index = (end_index +1) & MAXFRAGNUM_MASK; b_seq++; } if (isfoundE) // The End sequence was found and the frame is complete break; else // find the next Begin begin_index = end_index; } } else { // This fragment is lost, it was never completed the packet. LOG(3, this_fragmentation->fragment[begin_index].sid, this_fragmentation->fragment[begin_index].tid, "MPPP: (FIND BEGIN) seq_num:%d frag_index:%d flags:%02X is LOST\n", this_fragmentation->fragment[begin_index].seq, begin_index, this_fragmentation->fragment[begin_index].flags); // this frag is lost this_fragmentation->fragment[begin_index].length = 0; this_fragmentation->fragment[begin_index].flags = 0; } } begin_index = (begin_index +1) & MAXFRAGNUM_MASK; b_seq++; } assembling_frame: // try to assemble the frame that has the received fragment as a member // get the beginning of this frame begin_index = end_index = this_fragmentation->start_index; if (this_fragmentation->fragment[begin_index].length) { if (!(this_fragmentation->fragment[begin_index].flags & MP_BEGIN)) { LOG(3, this_fragmentation->fragment[begin_index].sid, this_fragmentation->fragment[begin_index].tid, "MPPP: (NOT BEGIN) seq_num:%d frag_index:%d flags:%02X\n", this_fragmentation->fragment[begin_index].seq, begin_index, this_fragmentation->fragment[begin_index].flags); // should occur only after an "M_Offset out of range" // The start sequence must be a begin sequence this_fragmentation->start_index = (begin_index +1) & MAXFRAGNUM_MASK; this_fragmentation->start_seq++; return; // assembling frame failed } } else return; // assembling frame failed // get the end of his frame while (this_fragmentation->fragment[end_index].length) { if (this_fragmentation->fragment[end_index].flags & MP_END) break; end_index = (end_index +1) & MAXFRAGNUM_MASK; if (end_index == this_fragmentation->start_index) return; // assembling frame failed } // return if a lost fragment is found if (!(this_fragmentation->fragment[end_index].length)) return; // assembling frame failed // assemble the packet //assemble frame, process it, reset fragmentation uint16_t cur_len = 4; // This is set to 4 to leave 4 bytes for function processipin LOG(4, s, t, "MPPP: processing fragments from %d to %d\n", begin_index, end_index); // Push to the receive buffer for (i = begin_index;; i = (i + 1) & MAXFRAGNUM_MASK) { this_frag = &this_fragmentation->fragment[i]; if(cur_len + this_frag->length > MAXETHER) { LOG(2, s, t, "MPPP: discarding reassembled frames larger than MAXETHER\n"); break; } memcpy(this_fragmentation->reassembled_frame+cur_len, this_frag->data, this_frag->length); LOG(5, s, t, "MPPP: processing frame at %d, with len %d\n", i, this_frag->length); cur_len += this_frag->length; if (i == end_index) { this_fragmentation->re_frame_len = cur_len; this_fragmentation->re_frame_begin_index = begin_index; this_fragmentation->re_frame_end_index = end_index; // Process the resassembled frame LOG(5, s, t, "MPPP: Process the reassembled frame, len=%d\n",cur_len); processmpframe(s, t, this_fragmentation->reassembled_frame, this_fragmentation->re_frame_len, 1); break; } } // Set reassembled frame length to zero after processing it this_fragmentation->re_frame_len = 0; for (i = begin_index;; i = (i + 1) & MAXFRAGNUM_MASK) { this_fragmentation->fragment[i].length = 0; // Indicates that this fragment has been consumed this_fragmentation->fragment[i].flags = 0; if (i == end_index) break; } // Set the new start_index and start_seq this_fragmentation->start_index = (end_index + 1) & MAXFRAGNUM_MASK; this_fragmentation->start_seq = this_fragmentation->fragment[end_index].seq + 1; LOG(4, s, t, "MPPP after assembling: start index is = %d, start seq=%d\n", this_fragmentation->start_index, this_fragmentation->start_seq); begin_index = this_fragmentation->start_index; if ((this_fragmentation->fragment[begin_index].length) && (this_fragmentation->fragment[begin_index].seq != this_fragmentation->start_seq)) { LOG(3, this_fragmentation->fragment[begin_index].sid, this_fragmentation->fragment[begin_index].tid, "MPPP: (START) seq_num:%d frag_index:%d flags:%02X is LOST\n", this_fragmentation->fragment[begin_index].seq, begin_index, this_fragmentation->fragment[begin_index].flags); this_fragmentation->fragment[begin_index].length = 0; this_fragmentation->fragment[begin_index].flags = 0; } if (this_fragmentation->start_seq <= Mmin) // It's possible to find other complete frame or lost frame. goto find_frame; else if ((this_fragmentation->fragment[begin_index].length) && (this_fragmentation->fragment[begin_index].flags & MP_BEGIN)) // may be that the next frame is completed goto assembling_frame; return; } // process IPv6 packet received // // This MUST be called with at least 4 byte behind 'p'. // (i.e. this routine writes to p[-4]). void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { struct in6_addr ip; in_addr_t ipv4; CSTAT(processipv6in); LOG_HEX(5, "IPv6", p, l); ip = *(struct in6_addr *) (p + 8); ipv4 = ntohl(*(uint32_t *)(p + 16)); if (l > MAXETHER) { LOG(1, s, t, "IP packet too long %d\n", l); STAT(tunnel_rx_errors); return ; } if (session[s].ppp.phase != Network || session[s].ppp.ipv6cp != Opened) return; // no spoof if (session[s].ipv6address.s6_addr[0]) { if ((sessionbyipv6new(ip) != s) && (ip.s6_addr[0] != 0xFE || ip.s6_addr[1] != 0x80 || ip.s6_addr16[1] != 0 || ip.s6_addr16[2] != 0 || ip.s6_addr16[3] != 0)) { char str[INET6_ADDRSTRLEN]; LOG(5, s, t, "Dropping packet with spoofed IP %s\n", inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN)); return; } } else if ((ipv4 != session[s].ip || memcmp(&config->ipv6_prefix, &ip, 8)) && sessionbyipv6(ip) != s) { char str[INET6_ADDRSTRLEN]; LOG(5, s, t, "Dropping packet with spoofed IP %s\n", inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN)); return; } // Check if it's a Router Solicition message. if (*(p + 6) == 58 && *(p + 7) == 255 && *(p + 24) == 0xFF && *(p + 25) == 2 && *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 && *(uint32_t *)(p + 34) == 0 && *(p + 38) == 0 && *(p + 39) == 2 && *(p + 40) == 133) { LOG(3, s, t, "Got IPv6 RS\n"); send_ipv6_ra(s, t, &ip); return; } // Check if DhcpV6, IP dst: FF02::1:2, Src Port 0x0222 (546), Dst Port 0x0223 (547) if (*(p + 6) == 17 && *(p + 24) == 0xFF && *(p + 25) == 2 && *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 && *(uint16_t *)(p + 34) == 0 && *(p + 36) == 0 && *(p + 37) == 1 && *(p + 38) == 0 && *(p + 39) == 2 && *(p + 40) == 2 && *(p + 41) == 0x22 && *(p + 42) == 2 && *(p + 43) == 0x23) { dhcpv6_process(s, t, p, l); return; } // Add on the tun header p -= 4; *(uint32_t *) p = htonl(PKTIPV6); l += 4; // Are we throttled and a slave? if (session[s].tbf_in && !config->cluster_iam_master) { // Pass it to the master for handling. master_throttle_packet(session[s].tbf_in, p, l); return; } // Are we throttled and a master?? if (session[s].tbf_in && config->cluster_iam_master) { // Actually handle the throttled packets. tbf_queue_packet(session[s].tbf_in, p, l); return; } // send to ethernet if (tun_write(p, l) < 0) { STAT(tun_tx_errors); LOG(0, s, t, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n", l, strerror(errno), tunfd, p); return; } p += 4; l -= 4; if (session[s].snoop_ip && session[s].snoop_port) { // Snooping this session snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port); } update_sessions_in_stat(s, l); eth_tx += l; STAT(tun_tx_packets); INC_STAT(tun_tx_bytes, l); } // // Helper routine for the TBF filters. // Used to send queued data in from the user. // void send_ipin(sessionidt s, uint8_t *buf, int len) { LOG_HEX(5, "IP in throttled", buf, len); if (write(tunfd, buf, len) < 0) { STAT(tun_tx_errors); LOG(0, 0, 0, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n", len, strerror(errno), tunfd, buf); return; } buf += 4; len -= 4; if (session[s].snoop_ip && session[s].snoop_port) { // Snooping this session snoop_send_packet(buf, len, session[s].snoop_ip, session[s].snoop_port); } // Increment packet counters increment_counter(&session[s].cin, &session[s].cin_wrap, len); session[s].cin_delta += len; session[s].pin++; sess_local[s].cin += len; sess_local[s].pin++; eth_tx += len; STAT(tun_tx_packets); INC_STAT(tun_tx_bytes, len - 4); } // Process CCP messages void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { uint8_t b[MAXETHER]; uint8_t *q; CSTAT(processccp); LOG_HEX(5, "CCP", p, l); if (session[s].ppp.phase < Network) { LOG(2, s, t, "CCP %s ignored in %s phase\n", ppp_code(*p), ppp_phase(session[s].ppp.phase)); return; } if (l < 1) { LOG(1, s, t, "Short CCP packet\n"); STAT(tunnel_rx_errors); } LOG(4, s, t, "CCP: recv %s\n", ppp_code(*p)); if (*p == ConfigAck) { switch (session[s].ppp.ccp) { case RequestSent: initialise_restart_count(s, ccp); change_state(s, ccp, AckReceived); break; case AckReceived: case Opened: LOG(2, s, t, "CCP: ConfigAck in state %s? Sending ConfigReq\n", ppp_state(session[s].ppp.ccp)); sendccp(s, t); change_state(s, ccp, RequestSent); break; case AckSent: LOG(3, s, t, "CCP: Opened\n"); change_state(s, ccp, Opened); break; default: LOG(2, s, t, "CCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ccp)); } } else if (*p == ConfigReq) { if (l < 6) // accept no compression *p = ConfigAck; else // compression requested--reject *p = ConfigRej; q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP, 0, 0, 0); if (!q) return; switch (session[s].ppp.ccp) { case Closed: q = makeppp(b, sizeof(b), p, 2, s, t, PPPCCP, 0, 0, 0); if (!q) return; *q = TerminateAck; *((uint16_t *) (q + 2)) = htons(l = 4); break; case Stopped: initialise_restart_count(s, ccp); sendccp(s, t); if (*q == ConfigAck) change_state(s, ccp, AckSent); else change_state(s, ccp, RequestSent); break; case RequestSent: if (*q == ConfigAck) change_state(s, ccp, AckSent); break; case AckReceived: if (*q == ConfigAck) change_state(s, ccp, Opened); break; case Opened: initialise_restart_count(s, ccp); sendccp(s, t); /* fallthrough */ case AckSent: if (*q == ConfigAck) change_state(s, ccp, AckSent); else change_state(s, ccp, RequestSent); break; default: LOG(2, s, t, "CCP: ignoring %s in state %s\n", ppp_code(*p), ppp_state(session[s].ppp.ccp)); return; } LOG(4, s, t, "CCP: send %s\n", ppp_code(*q)); tunnelsend(b, l + (q - b), t); } else if (*p == TerminateReq) { *p = TerminateAck; q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP, 0, 0, 0); if (!q) return; LOG(3, s, t, "CCP: send %s\n", ppp_code(*q)); tunnelsend(b, l + (q - b), t); change_state(s, ccp, Stopped); } else if (*p != CodeRej) { ppp_code_rej(s, t, PPPCCP, "CCP", p, l, b, sizeof(b)); } } // send a CHAP challenge void sendchap(sessionidt s, tunnelidt t) { uint8_t b[MAXETHER]; uint16_t r; uint8_t *q; CSTAT(sendchap); r = radiusnew(s); if (!r) { LOG(1, s, t, "No RADIUS to send challenge\n"); STAT(tunnel_tx_errors); return; } LOG(1, s, t, "Send CHAP challenge\n"); radius[r].chap = 1; // CHAP not PAP radius[r].id++; if (radius[r].state != RADIUSCHAP) radius[r].try = 0; radius[r].state = RADIUSCHAP; radius[r].retry = backoff(radius[r].try++); if (radius[r].try > 5) { sessionshutdown(s, "CHAP timeout.", CDN_ADMIN_DISC, TERM_REAUTHENTICATION_FAILURE); STAT(tunnel_tx_errors); return ; } q = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0); if (!q) return; *q = 1; // challenge q[1] = radius[r].id; // ID q[4] = 16; // value size (size of challenge) memcpy(q + 5, radius[r].auth, 16); // challenge strcpy((char *) q + 21, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname); // our name *(uint16_t *) (q + 2) = htons(strlen(config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname) + 21); // length tunnelsend(b, strlen(config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname) + 21 + (q - b), t); // send it } // fill in a L2TP message with a PPP frame, // returns start of PPP frame uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits) { uint16_t hdr = 0x0002; // L2TP with no options uint16_t type = mtype; uint8_t *start = b; if (t == TUNNEL_ID_PPPOE) { return pppoe_makeppp(b, size, p, l, s, t, mtype, prio, bid, mp_bits); } if (size < 16) // Need more space than this!! { LOG(0, s, t, "makeppp buffer too small for L2TP header (size=%d)\n", size); return NULL; } if (prio) hdr |= 0x0100; // set priority bit *(uint16_t *) (b + 0) = htons(hdr); *(uint16_t *) (b + 2) = htons(tunnel[t].far); // tunnel *(uint16_t *) (b + 4) = htons(session[s].far); // session b += 6; // Check whether this session is part of multilink if (bid) { if (bundle[bid].num_of_links > 1) type = PPPMP; // Change PPP message type to the PPPMP else bid = 0; } if (type == PPPLCP || !(session[s].flags & SESSION_ACFC)) { *(uint16_t *) b = htons(0xFF03); // HDLC header b += 2; } if (type < 0x100 && session[s].flags & SESSION_PFC) { *b++ = type; } else { *(uint16_t *) b = htons(type); b += 2; } if (bid) { // Set the sequence number and (B)egin (E)nd flags if (session[s].mssf) { // Set the multilink bits uint16_t bits_send = mp_bits; *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send); b += 2; } else { *(uint32_t *) b = htonl(bundle[bid].seq_num_t); // Set the multilink bits *b = mp_bits; b += 4; } bundle[bid].seq_num_t++; // Add the message type if this fragment has the begin bit set if (mp_bits & MP_BEGIN) { //*b++ = mtype; // The next two lines are instead of this *(uint16_t *) b = htons(mtype); // Message type b += 2; } } if ((b - start) + l > size) { LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%td)\n", size, (b - start) + l); return NULL; } // Copy the payload if (p && l) memcpy(b, p, l); return b; } // fill in a L2TP message with a PPP frame, // returns start of PPP frame //(note: THIS ROUTINE CAN WRITES TO p[-28]). uint8_t *opt_makeppp(uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits) { uint16_t hdr = 0x0002; // L2TP with no options uint16_t type = mtype; uint8_t *b = p; if (t == TUNNEL_ID_PPPOE) { return opt_pppoe_makeppp(p, l, s, t, mtype, prio, bid, mp_bits); } // Check whether this session is part of multilink if (bid) { if (bundle[bid].num_of_links > 1) type = PPPMP; // Change PPP message type to the PPPMP else bid = 0; } if (bid) { // Add the message type if this fragment has the begin bit set if (mp_bits & MP_BEGIN) { b -= 2; *(uint16_t *) b = htons(mtype); // Message type } // Set the sequence number and (B)egin (E)nd flags if (session[s].mssf) { // Set the multilink bits uint16_t bits_send = mp_bits; b -= 2; *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send); } else { b -= 4; *(uint32_t *) b = htonl(bundle[bid].seq_num_t); // Set the multilink bits *b = mp_bits; } bundle[bid].seq_num_t++; } if (type < 0x100 && session[s].flags & SESSION_PFC) { b--; *b = type; } else { b -= 2; *(uint16_t *) b = htons(type); } if (type == PPPLCP || !(session[s].flags & SESSION_ACFC)) { b -= 2; *(uint16_t *) b = htons(0xFF03); // HDLC header } if (prio) hdr |= 0x0100; // set priority bit b -= 2; *(uint16_t *) b = htons(session[s].far); // session b -= 2; *(uint16_t *) b = htons(tunnel[t].far); // tunnel b -= 2; *(uint16_t *) b = htons(hdr); return b; } static int add_lcp_auth(uint8_t *b, int size, int authtype) { int len = 0; if ((authtype == AUTHCHAP && size < 5) || size < 4) return 0; *b++ = 3; // Authentication-Protocol if (authtype == AUTHCHAP) { len = *b++ = 5; // length *(uint16_t *) b = htons(PPPCHAP); b += 2; *b++ = 5; // MD5 } else if (authtype == AUTHPAP) { len = *b++ = 4; // length *(uint16_t *) b = htons(PPPPAP); b += 2; } else { LOG(0, 0, 0, "add_lcp_auth called with unsupported auth type %d\n", authtype); } return len; } // Send LCP ConfigReq for MRU, authentication type and magic no void sendlcp(sessionidt s, tunnelidt t) { uint8_t b[500], *q, *l; int authtype = sess_local[s].lcp_authtype; if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPLCP, 0, 0, 0))) return; LOG(3, s, t, "LCP: send ConfigReq%s%s%s including MP options\n", authtype ? " (" : "", authtype ? (authtype == AUTHCHAP ? "CHAP" : "PAP") : "", authtype ? ")" : ""); l = q; *l++ = ConfigReq; *l++ = ++sess_local[s].lcp_ident; // ID l += 2; //Save space for length if (sess_local[s].ppp_mru) { *l++ = 1; *l++ = 4; // Maximum-Receive-Unit (length 4) *(uint16_t *) l = htons(sess_local[s].ppp_mru); l += 2; } if (authtype) l += add_lcp_auth(l, sizeof(b) - (l - b), authtype); if (session[s].magic) { *l++ = 5; *l++ = 6; // Magic-Number (length 6) *(uint32_t *) l = htonl(session[s].magic); l += 4; } if (sess_local[s].mp_mrru) { *l++ = 17; *l++ = 4; // Multilink Max-Receive-Reconstructed-Unit (length 4) *(uint16_t *) l = htons(sess_local[s].mp_mrru); l += 2; } if (sess_local[s].mp_epdis) { *l++ = 19; *l++ = 7; // Multilink Endpoint Discriminator (length 7) *l++ = IPADDR; // Endpoint Discriminator class *(uint32_t *) l = htonl(sess_local[s].mp_epdis); l += 4; } *(uint16_t *)(q + 2) = htons(l - q); // Length LOG_HEX(5, "PPPLCP", q, l - q); if (config->debug > 3) dumplcp(q, l - q); tunnelsend(b, (l - b), t); restart_timer(s, lcp); } // Send CCP request for no compression void sendccp(sessionidt s, tunnelidt t) { uint8_t b[500], *q; if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPCCP, 0, 0, 0))) return; LOG(3, s, t, "CCP: send ConfigReq (no compression)\n"); *q = ConfigReq; *(q + 1) = ++sess_local[s].lcp_ident; // ID *(uint16_t *)(q + 2) = htons(4); // Length LOG_HEX(5, "PPPCCP", q, 4); tunnelsend(b, (q - b) + 4 , t); restart_timer(s, ccp); } // Reject unknown/unconfigured protocols void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t proto) { uint8_t buf[MAXETHER]; uint8_t *q; int mru = session[s].mru; if (mru < MINMTU) mru = MINMTU; if (mru > sizeof(buf)) mru = sizeof(buf); l += 6; if (l > mru) l = mru; q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP, 0, 0, 0); if (!q) return; *q = ProtocolRej; *(q + 1) = ++sess_local[s].lcp_ident; *(uint16_t *)(q + 2) = htons(l); *(uint16_t *)(q + 4) = htons(proto); memcpy(q + 6, p, l - 6); if (proto == PPPIPV6CP) LOG(3, s, t, "LCP: send ProtocolRej (IPV6CP: not configured)\n"); else LOG(2, s, t, "LCP: sent ProtocolRej (0x%04X: unsupported)\n", proto); tunnelsend(buf, l + (q - buf), t); } l2tpns-2.3.3/pppoe.c000066400000000000000000000753321400724550600142450ustar00rootroot00000000000000/* * Fernando ALVES 2013 * Add functionality "server pppoe" to l2tpns. * inspiration pppoe.c of accel-ppp * GPL licenced */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhcp6.h" #include "l2tpns.h" #include "cluster.h" #include "constants.h" #include "md5.h" #include "util.h" int pppoediscfd = -1; int pppoesessfd = -1; static uint8_t bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; /* PPPoE codes */ #define CODE_PADI 0x09 #define CODE_PADO 0x07 #define CODE_PADR 0x19 #define CODE_PADS 0x65 #define CODE_PADT 0xA7 #define CODE_SESS 0x00 /* PPPoE Tags */ #define TAG_END_OF_LIST 0x0000 #define TAG_SERVICE_NAME 0x0101 #define TAG_AC_NAME 0x0102 #define TAG_HOST_UNIQ 0x0103 #define TAG_AC_COOKIE 0x0104 #define TAG_VENDOR_SPECIFIC 0x0105 #define TAG_RELAY_SESSION_ID 0x0110 #define TAG_SERVICE_NAME_ERROR 0x0201 #define TAG_AC_SYSTEM_ERROR 0x0202 #define TAG_GENERIC_ERROR 0x0203 static char *code_pad[] = { "PADI", "PADO", "PADR", "PADS", "PADT", "SESS", NULL }; enum { INDEX_PADI = 0, INDEX_PADO, INDEX_PADR, INDEX_PADS, INDEX_PADT, INDEX_SESS }; // set up pppoe discovery socket static void init_pppoe_disc(void) { int on = 1; struct ifreq ifr; struct sockaddr_ll sa; memset(&ifr, 0, sizeof(ifr)); memset(&sa, 0, sizeof(sa)); pppoediscfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_DISC)); if (pppoediscfd < 0) { LOG(0, 0, 0, "Error pppoe: socket: %s\n", strerror(errno)); exit(1); } fcntl(pppoediscfd, F_SETFD, fcntl(pppoediscfd, F_GETFD) | FD_CLOEXEC); if (setsockopt(pppoediscfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))) { LOG(0, 0, 0, "Error pppoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno)); exit(1); } assert(strlen(ifr.ifr_name) < sizeof(config->pppoe_if_to_bind) - 1); if (*config->pppoe_if_to_bind) strncpy(ifr.ifr_name, config->pppoe_if_to_bind, IFNAMSIZ); if (ioctl(pppoediscfd, SIOCGIFHWADDR, &ifr)) { LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFHWADDR): %s\n", strerror(errno)); exit(1); } if ((ifr.ifr_hwaddr.sa_data[0] & 1) != 0) { LOG(0, 0, 0, "Error pppoe: interface %s has not unicast address\n", config->pppoe_if_to_bind); exit(1); } memcpy(config->pppoe_hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); if (ioctl(pppoediscfd, SIOCGIFMTU, &ifr)) { LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFMTU): %s\n", strerror(errno)); exit(1); } if (ifr.ifr_mtu < ETH_DATA_LEN) LOG(0, 0, 0, "Error pppoe: interface %s has MTU of %i, should be %i\n", config->pppoe_if_to_bind, ifr.ifr_mtu, ETH_DATA_LEN); if (ioctl(pppoediscfd, SIOCGIFINDEX, &ifr)) { LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFINDEX): %s\n", strerror(errno)); exit(1); } memset(&sa, 0, sizeof(sa)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_PPP_DISC); sa.sll_ifindex = ifr.ifr_ifindex; if (bind(pppoediscfd, (struct sockaddr *)&sa, sizeof(sa))) { LOG(0, 0, 0, "Error pppoe: bind: %s\n", strerror(errno)); exit(1); } if (fcntl(pppoediscfd, F_SETFL, O_NONBLOCK)) { LOG(0, 0, 0, "Error pppoe: failed to set nonblocking mode: %s\n", strerror(errno)); exit(1); } } // set up pppoe session socket static void init_pppoe_sess(void) { int on = 1; struct ifreq ifr; struct sockaddr_ll sa; memset(&ifr, 0, sizeof(ifr)); memset(&sa, 0, sizeof(sa)); pppoesessfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PPP_SES)); if (pppoesessfd < 0) { LOG(0, 0, 0, "Error pppoe: socket: %s\n", strerror(errno)); exit(1); } fcntl(pppoesessfd, F_SETFD, fcntl(pppoesessfd, F_GETFD) | FD_CLOEXEC); if (setsockopt(pppoesessfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))) { LOG(0, 0, 0, "Error pppoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno)); exit(1); } assert(strlen(ifr.ifr_name) < sizeof(config->pppoe_if_to_bind) - 1); if (*config->pppoe_if_to_bind) strncpy(ifr.ifr_name, config->pppoe_if_to_bind, IFNAMSIZ); if (ioctl(pppoesessfd, SIOCGIFHWADDR, &ifr)) { LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFHWADDR): %s\n", strerror(errno)); exit(1); } if ((ifr.ifr_hwaddr.sa_data[0] & 1) != 0) { LOG(0, 0, 0, "Error pppoe: interface %s has not unicast address\n", config->pppoe_if_to_bind); exit(1); } memcpy(config->pppoe_hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); if (ioctl(pppoesessfd, SIOCGIFMTU, &ifr)) { LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFMTU): %s\n", strerror(errno)); exit(1); } if (ifr.ifr_mtu < ETH_DATA_LEN) LOG(0, 0, 0, "Error pppoe: interface %s has MTU of %i, should be %i\n", config->pppoe_if_to_bind, ifr.ifr_mtu, ETH_DATA_LEN); if (ioctl(pppoesessfd, SIOCGIFINDEX, &ifr)) { LOG(0, 0, 0, "Error pppoe: ioctl(SIOCGIFINDEX): %s\n", strerror(errno)); exit(1); } memset(&sa, 0, sizeof(sa)); sa.sll_family = AF_PACKET; sa.sll_protocol = htons(ETH_P_PPP_SES); sa.sll_ifindex = ifr.ifr_ifindex; if (bind(pppoesessfd, (struct sockaddr *)&sa, sizeof(sa))) { LOG(0, 0, 0, "Error pppoe: bind: %s\n", strerror(errno)); exit(1); } if (fcntl(pppoesessfd, F_SETFL, O_NONBLOCK)) { LOG(0, 0, 0, "Error pppoe: failed to set nonblocking mode: %s\n", strerror(errno)); exit(1); } } // set up pppoe discovery/session socket void init_pppoe(void) { tunnelidt t = TUNNEL_ID_PPPOE; init_pppoe_disc(); init_pppoe_sess(); // Reserve the a pseudo tunnel for pppoe server if (t > config->cluster_highest_tunnelid) config->cluster_highest_tunnelid = t; memset(&tunnel[t], 0, sizeof(tunnel[t])); tunnel[t].state = TUNNELOPEN; STAT(tunnel_created); } char * get_string_codepad(uint8_t codepad) { char * ptrch = NULL; switch(codepad) { case CODE_PADI: ptrch = code_pad[INDEX_PADI]; break; case CODE_PADO: ptrch = code_pad[INDEX_PADO]; break; case CODE_PADR: ptrch = code_pad[INDEX_PADR]; break; case CODE_PADS: ptrch = code_pad[INDEX_PADS]; break; case CODE_PADT: ptrch = code_pad[INDEX_PADT]; break; case CODE_SESS: ptrch = code_pad[INDEX_SESS]; break; } return ptrch; } static uint8_t * setup_header(uint8_t *pack, const uint8_t *src, const uint8_t *dst, int code, uint16_t sid, uint16_t h_proto) { uint8_t * p; // 14 bytes ethernet Header + 6 bytes header pppoe struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); memcpy(ethhdr->h_source, src, ETH_ALEN); memcpy(ethhdr->h_dest, dst, ETH_ALEN); ethhdr->h_proto = htons(h_proto); hdr->ver = 1; hdr->type = 1; hdr->code = code; hdr->sid = htons(sid); hdr->length = 0; p = (uint8_t *)(pack + ETH_HLEN + sizeof(*hdr)); return p; } static void add_tag(uint8_t *pack, int type, const uint8_t *data, int len) { struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)); tag->tag_type = htons(type); tag->tag_len = htons(len); memcpy(tag->tag_data, data, len); hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + len); } static void add_tag2(uint8_t *pack, const struct pppoe_tag *t) { struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); struct pppoe_tag *tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)); memcpy(tag, t, sizeof(*t) + ntohs(t->tag_len)); hdr->length = htons(ntohs(hdr->length) + sizeof(*tag) + ntohs(t->tag_len)); } static void pppoe_disc_send(const uint8_t *pack) { struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); int n, s; s = ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length); LOG(3, 0, 0, "SENT pppoe_disc: Code %s to %s\n", get_string_codepad(hdr->code), fmtMacAddr(ethhdr->h_dest)); LOG_HEX(5, "pppoe_disc_send", pack, s); n = write(pppoediscfd, pack, s); if (n < 0 ) LOG(0, 0, 0, "pppoe: write: %s\n", strerror(errno)); else if (n != s) { LOG(0, 0, 0, "pppoe: short write %i/%i\n", n,s); } } void pppoe_sess_send(const uint8_t *pack, uint16_t l, tunnelidt t) { struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); int n; uint16_t sizeppp; sessionidt s; if (t != TUNNEL_ID_PPPOE) { LOG(3, 0, t, "ERROR pppoe_sess_send: Tunnel %d is not a tunnel pppoe\n", t); return; } s = ntohs(hdr->sid); if (session[s].tunnel != t) { LOG(3, s, t, "ERROR pppoe_sess_send: Session is not a session pppoe\n"); return; } if (l < (ETH_HLEN + sizeof(*hdr) + 3)) { LOG(0, s, t, "ERROR pppoe_sess_send: packet too small for pppoe sent (size=%d)\n", l); return; } // recalculate the ppp frame length sizeppp = l - (ETH_HLEN + sizeof(*hdr)); hdr->length = htons(sizeppp); LOG_HEX(5, "pppoe_sess_send", pack, l); n = write(pppoesessfd, pack, l); if (n < 0 ) LOG(0, s, t, "pppoe_sess_send: write: %s\n", strerror(errno)); else if (n != l) LOG(0, s, t, "pppoe_sess_send: short write %i/%i\n", n,l); } static void pppoe_send_err(const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, int code, int tag_type) { uint8_t pack[ETHER_MAX_LEN]; setup_header(pack, config->pppoe_hwaddr, addr, code, 0, ETH_P_PPP_DISC); add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name)); add_tag(pack, tag_type, NULL, 0); if (host_uniq) add_tag2(pack, host_uniq); if (relay_sid) add_tag2(pack, relay_sid); pppoe_disc_send(pack); } // generate cookie static void pppoe_gen_cookie(const uint8_t *serv_hwaddr, const uint8_t *client_hwaddr, uint8_t *out) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, (void *) serv_hwaddr, ETH_ALEN); MD5_Update(&ctx, (void *) client_hwaddr, ETH_ALEN); MD5_Final(out, &ctx); } // check cookie static int pppoe_check_cookie(const uint8_t *serv_hwaddr, const uint8_t *client_hwaddr, uint8_t *cookie) { hasht hash; pppoe_gen_cookie(serv_hwaddr, client_hwaddr, hash); return memcmp(hash, cookie, 16); } static void pppoe_send_PADO(const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, const struct pppoe_tag *service_name) { uint8_t pack[ETHER_MAX_LEN]; hasht hash; setup_header(pack, config->pppoe_hwaddr, addr, CODE_PADO, 0, ETH_P_PPP_DISC); add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name)); if (service_name) add_tag2(pack, service_name); pppoe_gen_cookie(config->pppoe_hwaddr, addr, hash); add_tag(pack, TAG_AC_COOKIE, hash, 16); if (host_uniq) add_tag2(pack, host_uniq); if (relay_sid) add_tag2(pack, relay_sid); pppoe_disc_send(pack); } static void pppoe_send_PADS(uint16_t sid, const uint8_t *addr, const struct pppoe_tag *host_uniq, const struct pppoe_tag *relay_sid, const struct pppoe_tag *service_name) { uint8_t pack[ETHER_MAX_LEN]; setup_header(pack, config->pppoe_hwaddr, addr, CODE_PADS, sid, ETH_P_PPP_DISC); add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name)); add_tag2(pack, service_name); if (host_uniq) add_tag2(pack, host_uniq); if (relay_sid) add_tag2(pack, relay_sid); pppoe_disc_send(pack); } static void pppoe_send_PADT(uint16_t sid) { uint8_t pack[ETHER_MAX_LEN]; setup_header(pack, config->pppoe_hwaddr, session[sid].src_hwaddr, CODE_PADT, sid, ETH_P_PPP_DISC); add_tag(pack, TAG_AC_NAME, (uint8_t *)config->pppoe_ac_name, strlen(config->pppoe_ac_name)); LOG(3, sid, session[sid].tunnel, "pppoe: Sent PADT\n"); pppoe_disc_send(pack); } void pppoe_shutdown_session(sessionidt s) { if (session[s].tunnel != TUNNEL_ID_PPPOE) { LOG(3, s, session[s].tunnel, "ERROR pppoe_shutdown_session: Session is not a session pppoe\n"); return; } pppoe_send_PADT(s); } static void pppoe_recv_PADI(uint8_t *pack, int size) { struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); struct pppoe_tag *tag; struct pppoe_tag *host_uniq_tag = NULL; struct pppoe_tag *relay_sid_tag = NULL; struct pppoe_tag *service_name_tag = NULL; int n, service_match = 0; int len; if (hdr->sid) return; len = ntohs(hdr->length); for (n = 0; n < len; n += sizeof(*tag) + ntohs(tag->tag_len)) { tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n); if (n + sizeof(*tag) + ntohs(tag->tag_len) > len) return; switch (ntohs(tag->tag_type)) { case TAG_END_OF_LIST: break; case TAG_SERVICE_NAME: if (config->pppoe_only_equal_svc_name && *config->pppoe_service_name && !tag->tag_len) { break; } else if (*config->pppoe_service_name && tag->tag_len) { if (ntohs(tag->tag_len) != strlen(config->pppoe_service_name)) break; if (memcmp(tag->tag_data, config->pppoe_service_name, ntohs(tag->tag_len))) break; service_name_tag = tag; service_match = 1; } else { service_name_tag = tag; service_match = 1; } break; case TAG_HOST_UNIQ: host_uniq_tag = tag; break; case TAG_RELAY_SESSION_ID: relay_sid_tag = tag; break; } } if (!service_match) { LOG(3, 0, 0, "pppoe: discarding PADI packet (Service-Name mismatch)\n"); return; } pppoe_send_PADO(ethhdr->h_source, host_uniq_tag, relay_sid_tag, service_name_tag); } static void pppoe_recv_PADR(uint8_t *pack, int size) { struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); struct pppoe_tag *tag; struct pppoe_tag *host_uniq_tag = NULL; struct pppoe_tag *relay_sid_tag = NULL; struct pppoe_tag *ac_cookie_tag = NULL; struct pppoe_tag *service_name_tag = NULL; int n, service_match = 0; uint16_t sid; if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) { LOG(1, 0, 0, "Rcv pppoe: discard PADR (destination address is broadcast)\n"); return; } if (hdr->sid) { LOG(1, 0, 0, "Rcv pppoe: discarding PADR packet (sid is not zero)\n"); return; } for (n = 0; n < ntohs(hdr->length); n += sizeof(*tag) + ntohs(tag->tag_len)) { tag = (struct pppoe_tag *)(pack + ETH_HLEN + sizeof(*hdr) + n); switch (ntohs(tag->tag_type)) { case TAG_END_OF_LIST: break; case TAG_SERVICE_NAME: service_name_tag = tag; if (tag->tag_len == 0) service_match = 1; else if (*config->pppoe_service_name) { if (ntohs(tag->tag_len) != strlen(config->pppoe_service_name)) break; if (memcmp(tag->tag_data, config->pppoe_service_name, ntohs(tag->tag_len))) break; service_match = 1; } else { service_match = 1; } break; case TAG_HOST_UNIQ: host_uniq_tag = tag; break; case TAG_AC_COOKIE: ac_cookie_tag = tag; break; case TAG_RELAY_SESSION_ID: relay_sid_tag = tag; break; } } if (!service_match) { LOG(3, 0, 0, "pppoe: Service-Name mismatch\n"); pppoe_send_err(ethhdr->h_source, host_uniq_tag, relay_sid_tag, CODE_PADS, TAG_SERVICE_NAME_ERROR); return; } if (!ac_cookie_tag) { LOG(3, 0, 0, "pppoe: discard PADR packet (no AC-Cookie tag present)\n"); return; } if (ntohs(ac_cookie_tag->tag_len) != 16) { LOG(3, 0, 0, "pppoe: discard PADR packet (incorrect AC-Cookie tag length)\n"); return; } if (pppoe_check_cookie(ethhdr->h_dest, ethhdr->h_source, (uint8_t *) ac_cookie_tag->tag_data)) { LOG(3, 0, 0, "pppoe: discard PADR packet (incorrect AC-Cookie)\n"); return; } sid = sessionfree; sessionfree = session[sid].next; memset(&session[sid], 0, sizeof(session[0])); if (sid > config->cluster_highest_sessionid) config->cluster_highest_sessionid = sid; session[sid].opened = time_now; session[sid].tunnel = TUNNEL_ID_PPPOE; session[sid].last_packet = session[sid].last_data = time_now; //strncpy(session[sid].called, called, sizeof(session[sid].called) - 1); //strncpy(session[sid].calling, calling, sizeof(session[sid].calling) - 1); session[sid].ppp.phase = Establish; session[sid].ppp.lcp = Starting; session[sid].magic = time_now; // set magic number session[sid].mru = PPPoE_MRU; // default // start LCP sess_local[sid].lcp_authtype = config->radius_authprefer; sess_local[sid].ppp_mru = MRU; // Set multilink options before sending initial LCP packet sess_local[sid].mp_mrru = 1614; sess_local[sid].mp_epdis = ntohl(config->iftun_address ? config->iftun_address : my_address); memcpy(session[sid].src_hwaddr, ethhdr->h_source, ETH_ALEN); pppoe_send_PADS(sid, ethhdr->h_source, host_uniq_tag, relay_sid_tag, service_name_tag); sendlcp(sid, session[sid].tunnel); change_state(sid, lcp, RequestSent); } static void pppoe_recv_PADT(uint8_t *pack) { struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); if (!memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN)) { LOG(3, 0, 0, "pppoe: discard PADT (destination address is broadcast)\n"); return; } if (hdr->sid) { if ((hdr->sid < MAXSESSION) && (session[hdr->sid].tunnel == TUNNEL_ID_PPPOE)) sessionshutdown(hdr->sid, "Client shutdown", CDN_ADMIN_DISC, 0); } } // fill in a PPPOE message with a PPP frame, // returns start of PPP frame uint8_t *pppoe_makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits) { uint16_t type = mtype; uint8_t *start = b; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(b + ETH_HLEN); if (t != TUNNEL_ID_PPPOE) return NULL; if (size < 28) // Need more space than this!! { LOG(0, s, t, "pppoe_makeppp buffer too small for pppoe header (size=%d)\n", size); return NULL; } // 14 bytes ethernet Header + 6 bytes header pppoe b = setup_header(b, config->pppoe_hwaddr, session[s].src_hwaddr, CODE_SESS, s, ETH_P_PPP_SES); // Check whether this session is part of multilink if (bid) { if (bundle[bid].num_of_links > 1) type = PPPMP; // Change PPP message type to the PPPMP else bid = 0; } *(uint16_t *) b = htons(type); b += 2; hdr->length += 2; if (bid) { // Set the sequence number and (B)egin (E)nd flags if (session[s].mssf) { // Set the multilink bits uint16_t bits_send = mp_bits; *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send); b += 2; hdr->length += 2; } else { *(uint32_t *) b = htonl(bundle[bid].seq_num_t); // Set the multilink bits *b = mp_bits; b += 4; hdr->length += 4; } bundle[bid].seq_num_t++; // Add the message type if this fragment has the begin bit set if (mp_bits & MP_BEGIN) { //*b++ = mtype; // The next two lines are instead of this *(uint16_t *) b = htons(mtype); // Message type b += 2; hdr->length += 2; } } if ((b - start) + l > size) { LOG(0, s, t, "pppoe_makeppp would overflow buffer (size=%d, header+payload=%td)\n", size, (b - start) + l); return NULL; } // Copy the payload if (p && l) { memcpy(b, p, l); hdr->length += l; } return b; } // fill in a PPPOE message with a PPP frame, // returns start of PPP frame //(note: THIS ROUTINE WRITES TO p[-28]). uint8_t *opt_pppoe_makeppp(uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits) { uint16_t type = mtype; uint16_t hdrlen = l; uint8_t *b = p; struct pppoe_hdr *hdr; if (t != TUNNEL_ID_PPPOE) return NULL; // Check whether this session is part of multilink if (bid) { if (bundle[bid].num_of_links > 1) type = PPPMP; // Change PPP message type to the PPPMP else bid = 0; } if (bid) { // Add the message type if this fragment has the begin bit set if (mp_bits & MP_BEGIN) { b -= 2; *(uint16_t *) b = htons(mtype); // Message type } // Set the sequence number and (B)egin (E)nd flags if (session[s].mssf) { // Set the multilink bits uint16_t bits_send = mp_bits; b -= 2; *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send); } else { b -= 4; *(uint32_t *) b = htonl(bundle[bid].seq_num_t); // Set the multilink bits *b = mp_bits; } bundle[bid].seq_num_t++; } b -= 2; *(uint16_t *) b = htons(type); // Size ppp packet hdrlen += (p - b); // 14 bytes ethernet Header + 6 bytes header pppoe b -= (ETH_HLEN + sizeof(*hdr)); setup_header(b, config->pppoe_hwaddr, session[s].src_hwaddr, CODE_SESS, s, ETH_P_PPP_SES); hdr = (struct pppoe_hdr *)(b + ETH_HLEN); // Store length on header pppoe hdr->length = hdrlen; return b; } // pppoe discovery recv data void process_pppoe_disc(uint8_t *pack, int size) { struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); LOG(3, 0, 0, "RCV pppoe_disc: Code %s from %s\n", get_string_codepad(hdr->code), fmtMacAddr(ethhdr->h_source)); LOG_HEX(5, "PPPOE Disc", pack, size); if (!config->cluster_iam_master) { if (hdr->code == CODE_PADI) return; // Discard because the PADI is received by all (PADI is a broadcast diffusion) master_forward_pppoe_packet(pack, size, hdr->code); return; } if (size < (ETH_HLEN + sizeof(*hdr))) { LOG(1, 0, 0, "Error pppoe_disc: short packet received (%i)\n", size); return; } if (memcmp(ethhdr->h_dest, bc_addr, ETH_ALEN) && memcmp(ethhdr->h_dest, config->pppoe_hwaddr, ETH_ALEN)) { LOG(1, 0, 0, "Error pppoe_disc: h_dest != bc_addr and h_dest != config->pppoe_hwaddr\n"); return; } if (!memcmp(ethhdr->h_source, bc_addr, ETH_ALEN)) { LOG(1, 0, 0, "Error pppoe_disc: discarding packet (source address is broadcast)\n"); return; } if ((ethhdr->h_source[0] & 1) != 0) { LOG(1, 0, 0, "Error pppoe_disc: discarding packet (host address is not unicast)\n"); return; } if (size < ETH_HLEN + sizeof(*hdr) + ntohs(hdr->length)) { LOG(1, 0, 0, "Error pppoe_disc: short packet received\n"); return; } if (hdr->ver != 1) { LOG(1, 0, 0, "Error pppoe_disc: discarding packet (unsupported version %i)\n", hdr->ver); return; } if (hdr->type != 1) { LOG(1, 0, 0, "Error pppoe_disc: discarding packet (unsupported type %i)\n", hdr->type); return; } switch (hdr->code) { case CODE_PADI: pppoe_recv_PADI(pack, size); break; case CODE_PADR: pppoe_recv_PADR(pack, size); break; case CODE_PADT: pppoe_recv_PADT(pack); break; } } // Forward from pppoe to l2tp remote LNS static void pppoe_forwardto_session_rmlns(uint8_t *pack, int size, sessionidt sess, uint16_t proto) { struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); uint16_t lppp = ntohs(hdr->length); uint16_t ll2tp = lppp + 6; uint8_t *pppdata = (uint8_t *) hdr->tag; uint8_t *pl2tp = pppdata - 6; uint8_t *p = pl2tp; uint16_t t = 0, s = 0; s = session[sess].forwardtosession; if (session[s].forwardtosession != sess) { LOG(3, sess, session[sess].tunnel, "pppoe: Link Session (%u) broken\n", s); return; } t = session[s].tunnel; if (t >= MAXTUNNEL) { LOG(1, s, t, "pppoe: Session with invalid tunnel ID\n"); return; } if (!tunnel[t].isremotelns) { LOG(3, sess, session[sess].tunnel, "pppoe: Link Tunnel/Session (%u/%u) broken\n", s, t); return; } // First word L2TP options (with no options) *(uint16_t *) p = htons(0x0002); p += 2; *(uint16_t *) p = htons(tunnel[t].far); // tunnel p += 2; *(uint16_t *) p = htons(session[s].far); // session p += 2; if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])) { session[sess].last_packet = session[sess].last_data = time_now; // Update STAT IN increment_counter(&session[sess].cin, &session[sess].cin_wrap, ll2tp); session[sess].cin_delta += ll2tp; session[sess].pin++; sess_local[sess].cin += ll2tp; sess_local[sess].pin++; session[s].last_data = time_now; // Update STAT OUT increment_counter(&session[s].cout, &session[s].cout_wrap, ll2tp); // byte count session[s].cout_delta += ll2tp; session[s].pout++; sess_local[s].cout += ll2tp; sess_local[s].pout++; } else session[sess].last_packet = time_now; tunnelsend(pl2tp, ll2tp, t); // send it... } // Forward from l2tp to pppoe // (note: THIS ROUTINE WRITES TO pack[-20]). void pppoe_forwardto_session_pppoe(uint8_t *pack, int size, sessionidt sess, uint16_t proto) { uint16_t t = 0, s = 0; uint16_t lpppoe = size - 2; uint8_t *p = pack + 2; // First word L2TP options LOG(5, sess, session[sess].tunnel, "Forwarding data session to pppoe session %u\n", session[sess].forwardtosession); s = session[sess].forwardtosession; t = session[s].tunnel; if (*pack & 0x40) { // length p += 2; lpppoe -= 2; } *(uint16_t *) p = htons(tunnel[t].far); // tunnel p += 2; *(uint16_t *) p = htons(session[s].far); // session p += 2; lpppoe -= 4; if (*pack & 0x08) { // ns/nr *(uint16_t *) p = htons(tunnel[t].ns); // sequence p += 2; *(uint16_t *) p = htons(tunnel[t].nr); // sequence p += 2; lpppoe -= 4; } if (lpppoe > 2 && p[0] == 0xFF && p[1] == 0x03) { // HDLC address header, discard in pppoe p += 2; lpppoe -= 2; } lpppoe += (ETH_HLEN + sizeof(struct pppoe_hdr)); p -= (ETH_HLEN + sizeof(struct pppoe_hdr)); // 14 bytes ethernet Header + 6 bytes header pppoe setup_header(p, config->pppoe_hwaddr, session[s].src_hwaddr, CODE_SESS, s, ETH_P_PPP_SES); if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])) { session[sess].last_packet = session[sess].last_data = time_now; // Update STAT IN increment_counter(&session[sess].cin, &session[sess].cin_wrap, lpppoe); session[sess].cin_delta += lpppoe; session[sess].pin++; sess_local[sess].cin += lpppoe; sess_local[sess].pin++; session[s].last_data = time_now; // Update STAT OUT increment_counter(&session[s].cout, &session[s].cout_wrap, lpppoe); // byte count session[s].cout_delta += lpppoe; session[s].pout++; sess_local[s].cout += lpppoe; sess_local[s].pout++; } else session[sess].last_packet = time_now; tunnelsend(p, lpppoe, t); // send it.... } void process_pppoe_sess(uint8_t *pack, int size) { //struct ethhdr *ethhdr = (struct ethhdr *)pack; struct pppoe_hdr *hdr = (struct pppoe_hdr *)(pack + ETH_HLEN); uint16_t lppp = ntohs(hdr->length); uint8_t *pppdata = (uint8_t *) hdr->tag; uint16_t proto, sid, t; sid = ntohs(hdr->sid); t = TUNNEL_ID_PPPOE; LOG_HEX(5, "RCV PPPOE Sess", pack, size); if (sid >= MAXSESSION) { LOG(0, sid, t, "Received pppoe packet with invalid session ID\n"); STAT(tunnel_rx_errors); return; } if (session[sid].tunnel != t) { if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } LOG(1, sid, t, "ERROR process_pppoe_sess: Session is not a session pppoe\n"); return; } if (hdr->ver != 1) { LOG(3, sid, t, "Error process_pppoe_sess: discarding packet (unsupported version %i)\n", hdr->ver); return; } if (hdr->type != 1) { LOG(3, sid, t, "Error process_pppoe_sess: discarding packet (unsupported type %i)\n", hdr->type); return; } if (lppp > 2 && pppdata[0] == 0xFF && pppdata[1] == 0x03) { // HDLC address header, discard LOG(5, sid, t, "pppoe_sess: HDLC address header, discard\n"); pppdata += 2; lppp -= 2; } if (lppp < 2) { LOG(3, sid, t, "Error process_pppoe_sess: Short ppp length %d\n", lppp); return; } if (*pppdata & 1) { proto = *pppdata++; lppp--; } else { proto = ntohs(*(uint16_t *) pppdata); pppdata += 2; lppp -= 2; } if (session[sid].forwardtosession) { // Must be forwaded to a remote lns tunnel l2tp pppoe_forwardto_session_rmlns(pack, size, sid, proto); return; } if (proto == PPPPAP) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processpap(sid, t, pppdata, lppp); } else if (proto == PPPCHAP) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processchap(sid, t, pppdata, lppp); } else if (proto == PPPLCP) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processlcp(sid, t, pppdata, lppp); } else if (proto == PPPIPCP) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processipcp(sid, t, pppdata, lppp); } else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0]) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processipv6cp(sid, t, pppdata, lppp); } else if (proto == PPPCCP) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processccp(sid, t, pppdata, lppp); } else if (proto == PPPIP) { session[sid].last_packet = session[sid].last_data = time_now; if (session[sid].walled_garden && !config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processipin(sid, t, pppdata, lppp); } else if (proto == PPPMP) { session[sid].last_packet = session[sid].last_data = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processmpin(sid, t, pppdata, lppp); } else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) { session[sid].last_packet = session[sid].last_data = time_now; if (session[sid].walled_garden && !config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } processipv6in(sid, t, pppdata, lppp); } else if (session[sid].ppp.lcp == Opened) { session[sid].last_packet = time_now; if (!config->cluster_iam_master) { master_forward_pppoe_packet(pack, size, hdr->code); return; } protoreject(sid, t, pppdata, lppp, proto); } else { LOG(3, sid, t, "process_pppoe_sess: Unknown PPP protocol 0x%04X received in LCP %s state\n", proto, ppp_state(session[sid].ppp.lcp)); } } void pppoe_send_garp() { int s; struct ifreq ifr; uint8_t mac[6]; if (!*config->pppoe_if_to_bind) return; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { LOG(0, 0, 0, "Error creating socket for GARP: %s\n", strerror(errno)); return; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, config->pppoe_if_to_bind, sizeof(ifr.ifr_name) - 1); if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) { LOG(0, 0, 0, "Error getting eth0 hardware address for GARP: %s\n", strerror(errno)); close(s); return; } memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { LOG(0, 0, 0, "Error getting eth0 interface index for GARP: %s\n", strerror(errno)); close(s); return; } close(s); sendarp(ifr.ifr_ifindex, mac, config->iftun_address); } // rcv pppoe data from slave void pppoe_process_forward(uint8_t *pack, int size, in_addr_t addr) { struct ethhdr *ethhdr = (struct ethhdr *)pack; if (ethhdr->h_proto == htons(ETH_P_PPP_DISC)) process_pppoe_disc(pack, size); else if (ethhdr->h_proto == htons(ETH_P_PPP_SES)) process_pppoe_sess(pack, size); else LOG(0, 0, 0, "pppoe_process_forward: I got a C_PPPOE_FORWARD from %s, but not a PPPOE data?\n", fmtaddr(addr, 0)); } l2tpns-2.3.3/pppoe.h000066400000000000000000000017321400724550600142430ustar00rootroot00000000000000 #ifndef __PPPOE_H__ #define __PPPOE_H__ #define DEFAULT_PPPOE_AC_NAME "l2tpns-pppoe" // pppoe.c void init_pppoe(void); void process_pppoe_disc(uint8_t *pack, int size); void process_pppoe_sess(uint8_t *pack, int size); void pppoe_sess_send(const uint8_t *pack, uint16_t l, tunnelidt t); uint8_t *pppoe_makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits); uint8_t *opt_pppoe_makeppp(uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits); void pppoe_shutdown_session(sessionidt s); void pppoe_forwardto_session_pppoe(uint8_t *pack, int size, sessionidt sess, uint16_t proto); void pppoe_process_forward(uint8_t *pack, int size, in_addr_t addr); void pppoe_send_garp(); char * get_string_codepad(uint8_t codepad); extern int pppoediscfd; // pppoe discovery socket extern int pppoesessfd; // pppoe session socket #endif /* __PPPOE_H__ */ l2tpns-2.3.3/radius.c000066400000000000000000001056101400724550600144020ustar00rootroot00000000000000// L2TPNS Radius Stuff #include #include #include #include #include #include #include #include #include #include #include #include "md5.h" #include "constants.h" #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" #include "util.h" #include "cluster.h" #include "l2tplac.h" #include "pppoe.h" extern radiust *radius; extern sessiont *session; extern tunnelt *tunnel; extern configt *config; extern int *radfds; extern ip_filtert *ip_filters; static const hasht zero; static void calc_auth(const void *buf, size_t len, const uint8_t *in, uint8_t *out) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, (void *)buf, 4); // code, id, length MD5_Update(&ctx, (void *)in, 16); // auth MD5_Update(&ctx, (void *)(buf + 20), len - 20); MD5_Update(&ctx, config->radiussecret, strlen(config->radiussecret)); MD5_Final(out, &ctx); } // Set up socket for radius requests void initrad(void) { int i; uint16_t port = 0; uint16_t min = config->radius_bind_min; uint16_t max = config->radius_bind_max; int inc = 1; struct sockaddr_in addr; if (min) { port = min; if (!max) max = ~0 - 1; } else if (max) /* no minimum specified, bind from max down */ { port = max; min = 1; inc = -1; } LOG(3, 0, 0, "Creating %d sockets for RADIUS queries\n", RADIUS_FDS); radfds = calloc(sizeof(int), RADIUS_FDS); for (i = 0; i < RADIUS_FDS; i++) { int flags; radfds[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); flags = fcntl(radfds[i], F_GETFL, 0); fcntl(radfds[i], F_SETFL, flags | O_NONBLOCK); if (port) { int b; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; do { addr.sin_port = htons(port); if ((b = bind(radfds[i], (struct sockaddr *) &addr, sizeof(addr))) < 0) { if ((port += inc) < min || port > max) { LOG(0, 0, 0, "Can't bind RADIUS socket in range %u-%u\n", min, max); exit(1); } } } while (b < 0); } } } void radiusclear(uint16_t r, sessionidt s) { if (s) sess_local[s].radius = 0; memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL; } static uint16_t get_free_radius() { int count; static uint32_t next_radius_id = 0; for (count = MAXRADIUS; count > 0; --count) { ++next_radius_id; // Find the next ID to check. if (next_radius_id >= MAXRADIUS) next_radius_id = 1; if (radius[next_radius_id].state == RADIUSNULL) { return next_radius_id; } } LOG(0, 0, 0, "Can't find a free radius session! This is very bad!\n"); return 0; } uint16_t radiusnew(sessionidt s) { uint16_t r = sess_local[s].radius; /* re-use */ if (r) { LOG(3, s, session[s].tunnel, "Re-used radius %d\n", r); return r; } if (!(r = get_free_radius())) { LOG(1, s, session[s].tunnel, "No free RADIUS sessions\n"); STAT(radius_overflow); return 0; }; memset(&radius[r], 0, sizeof(radius[r])); sess_local[s].radius = r; radius[r].session = s; radius[r].state = RADIUSWAIT; radius[r].retry = TIME + 1200; // Wait at least 120 seconds to re-claim this. random_data(radius[r].auth, sizeof(radius[r].auth)); LOG(3, s, session[s].tunnel, "Allocated radius %d\n", r); return r; } // Send a RADIUS request void radiussend(uint16_t r, uint8_t state) { struct sockaddr_in addr; uint8_t b[4096]; // RADIUS packet char pass[129]; int pl; uint8_t *p; sessionidt s; CSTAT(radiussend); s = radius[r].session; if (!config->numradiusservers) { LOG(0, s, session[s].tunnel, "No RADIUS servers\n"); return; } if (!*config->radiussecret) { LOG(0, s, session[s].tunnel, "No RADIUS secret\n"); return; } if (state != RADIUSAUTH && state != RADIUSJUSTAUTH && !config->radius_accounting) { // Radius accounting is turned off radiusclear(r, s); return; } if (radius[r].state != state) radius[r].try = 0; radius[r].state = state; radius[r].retry = backoff(radius[r].try++) + 20; // 3s, 4s, 6s, 10s... LOG(4, s, session[s].tunnel, "Send RADIUS id %d sock %d state %s try %d\n", r >> RADIUS_SHIFT, r & RADIUS_MASK, radius_state(radius[r].state), radius[r].try); if (radius[r].try > config->numradiusservers * 2) { if (s) { if (state == RADIUSAUTH || state == RADIUSJUSTAUTH) sessionshutdown(s, "RADIUS timeout.", CDN_ADMIN_DISC, TERM_REAUTHENTICATION_FAILURE); else { LOG(1, s, session[s].tunnel, "RADIUS timeout, but in state %s so don't timeout session\n", radius_state(state)); radiusclear(r, s); } STAT(radius_timeout); } else { STAT(radius_retries); radius[r].state = RADIUSWAIT; radius[r].retry = 100; } return; } // contruct RADIUS access request switch (state) { case RADIUSAUTH: case RADIUSJUSTAUTH: b[0] = AccessRequest; // access request break; case RADIUSSTART: case RADIUSSTOP: case RADIUSINTERIM: b[0] = AccountingRequest; // accounting request break; default: LOG(0, 0, 0, "Unknown radius state %d\n", state); } b[1] = r >> RADIUS_SHIFT; // identifier memcpy(b + 4, radius[r].auth, 16); p = b + 20; if (s) { *p = 1; // user name p[1] = strlen(session[s].user) + 2; strcpy((char *) p + 2, session[s].user); p += p[1]; } if (state == RADIUSAUTH || state == RADIUSJUSTAUTH) { if (radius[r].chap) { *p = 3; // CHAP password p[1] = 19; // length p[2] = radius[r].id; // ID memcpy(p + 3, radius[r].pass, 16); // response from CHAP request p += p[1]; *p = 60; // CHAP Challenge p[1] = 18; // length memcpy(p + 2, radius[r].auth, 16); p += p[1]; } else { strcpy(pass, radius[r].pass); pl = strlen(pass); while (pl & 15) pass[pl++] = 0; // pad if (pl) { // encrypt hasht hash; int p = 0; while (p < pl) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, config->radiussecret, strlen(config->radiussecret)); if (p) MD5_Update(&ctx, pass + p - 16, 16); else MD5_Update(&ctx, radius[r].auth, 16); MD5_Final(hash, &ctx); do { pass[p] ^= hash[p & 15]; p++; } while (p & 15); } } *p = 2; // password p[1] = pl + 2; if (pl) memcpy(p + 2, pass, pl); p += p[1]; } } else // accounting { *p = 40; // accounting type p[1] = 6; *(uint32_t *) (p + 2) = htonl(state - RADIUSSTART + 1); // start=1, stop=2, interim=3 p += p[1]; if (s) { *p = 44; // session ID p[1] = 18; sprintf((char *) p + 2, "%08X%08X", session[s].unique_id, session[s].opened); p += p[1]; if (state == RADIUSSTART) { // start *p = 41; // delay p[1] = 6; *(uint32_t *) (p + 2) = htonl(time(NULL) - session[s].opened); p += p[1]; sess_local[s].last_interim = time_now; // Setup "first" Interim } else { // stop, interim *p = 42; // input octets p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].cin); p += p[1]; *p = 43; // output octets p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].cout); p += p[1]; *p = 46; // session time p[1] = 6; *(uint32_t *) (p + 2) = htonl(time(NULL) - session[s].opened); p += p[1]; *p = 47; // input packets p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].pin); p += p[1]; *p = 48; // output packets p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].pout); p += p[1]; *p = 52; // input gigawords p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].cin_wrap); p += p[1]; *p = 53; // output gigawords p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].cout_wrap); p += p[1]; if (state == RADIUSSTOP && radius[r].term_cause) { *p = 49; // acct-terminate-cause p[1] = 6; *(uint32_t *) (p + 2) = htonl(radius[r].term_cause); p += p[1]; if (radius[r].term_msg) { *p = 26; // vendor-specific *(uint32_t *) (p + 2) = htonl(9); // Cisco p[6] = 1; // Cisco-AVPair p[7] = 2 + sprintf((char *) p + 8, "disc-cause-ext=%s", radius[r].term_msg); p[1] = p[7] + 6; p += p[1]; } } } if (session[s].classlen) { *p = 25; // class p[1] = session[s].classlen + 2; memcpy(p + 2, session[s].class, session[s].classlen); p += p[1]; } { struct param_radius_account acct = { &tunnel[session[s].tunnel], &session[s], &p }; run_plugins(PLUGIN_RADIUS_ACCOUNT, &acct); } } } if (s) { *p = 5; // NAS-Port p[1] = 6; *(uint32_t *) (p + 2) = htonl(s); p += p[1]; *p = 6; // Service-Type p[1] = 6; *(uint32_t *) (p + 2) = htonl((state == RADIUSJUSTAUTH ? 8 : 2)); // Authenticate only or Framed-User respectevily p += p[1]; *p = 7; // Framed-Protocol p[1] = htonl((state == RADIUSJUSTAUTH ? 0 : 6)); *(uint32_t *) (p + 2) = htonl((state == RADIUSJUSTAUTH ? 0 : 1)); // PPP p += p[1]; if (session[s].ip) { *p = 8; // Framed-IP-Address p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].ip); p += p[1]; } if (session[s].route[0].ip) { int r; for (r = 0; s && r < MAXROUTE && session[s].route[r].ip; r++) { *p = 22; // Framed-Route p[1] = sprintf((char *) p + 2, "%s/%d %s 1", fmtaddr(htonl(session[s].route[r].ip), 0), session[s].route[r].prefixlen, fmtaddr(htonl(session[s].ip), 1)) + 2; p += p[1]; } } if (session[s].session_timeout) { *p = 27; // Session-Timeout p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].session_timeout); p += p[1]; } if (session[s].idle_timeout) { *p = 28; // Idle-Timeout p[1] = 6; *(uint32_t *) (p + 2) = htonl(session[s].idle_timeout); p += p[1]; } if (*session[s].called) { *p = 30; // called p[1] = strlen(session[s].called) + 2; strcpy((char *) p + 2, session[s].called); p += p[1]; } if (*session[s].calling) { *p = 31; // calling p[1] = strlen(session[s].calling) + 2; strcpy((char *) p + 2, session[s].calling); p += p[1]; } } // NAS-IP-Address *p = 4; p[1] = 6; *(uint32_t *)(p + 2) = config->bind_address; p += p[1]; // All AVpairs added *(uint16_t *) (b + 2) = htons(p - b); if (state != RADIUSAUTH && state != RADIUSJUSTAUTH) { // Build auth for accounting packet calc_auth(b, p - b, zero, b + 4); memcpy(radius[r].auth, b + 4, 16); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; *(uint32_t *) & addr.sin_addr = config->radiusserver[(radius[r].try - 1) % config->numradiusservers]; { // get radius port uint16_t port = config->radiusport[(radius[r].try - 1) % config->numradiusservers]; // assume RADIUS accounting port is the authentication port +1 addr.sin_port = htons((state == RADIUSAUTH || state == RADIUSJUSTAUTH) ? port : port+1); } LOG_HEX(5, "RADIUS Send", b, (p - b)); sendto(radfds[r & RADIUS_MASK], b, p - b, 0, (void *) &addr, sizeof(addr)); } static void handle_avpair(sessionidt s, uint8_t *avp, int len) { uint8_t *key = avp; uint8_t *value = memchr(avp, '=', len); uint8_t tmp[2048] = ""; if (value) { *value++ = 0; len -= value - key; } else { value = tmp; len = 0; } // strip quotes if (len > 2 && (*value == '"' || *value == '\'') && value[len - 1] == *value) { value++; len--; value[len - 1] = 0; } // copy and null terminate else if (len < sizeof(tmp) - 1) { memcpy(tmp, value, len); tmp[len] = 0; value = tmp; } else return; // Run hooks { struct param_radius_response p = { &tunnel[session[s].tunnel], &session[s], (char *) key, (char *) value }; run_plugins(PLUGIN_RADIUS_RESPONSE, &p); } } // process RADIUS response void processrad(uint8_t *buf, int len, char socket_index) { uint8_t b[MAXETHER]; uint16_t r; sessionidt s; tunnelidt t = 0; hasht hash; int routes = 0; int routes6 = 0; int r_code; int r_id; int OpentunnelReq = 0; CSTAT(processrad); LOG_HEX(5, "RADIUS Response", buf, len); if (len < 20 || len < ntohs(*(uint16_t *) (buf + 2))) { LOG(1, 0, 0, "Duff RADIUS response length %d\n", len); return ; } r_code = buf[0]; // response type r_id = buf[1]; // radius reply indentifier. len = ntohs(*(uint16_t *) (buf + 2)); r = socket_index | (r_id << RADIUS_SHIFT); s = radius[r].session; LOG(3, s, session[s].tunnel, "Received %s, radius %d response for session %u (%s, id %d)\n", radius_state(radius[r].state), r, s, radius_code(r_code), r_id); if (!s && radius[r].state != RADIUSSTOP) { LOG(1, s, session[s].tunnel, " Unexpected RADIUS response\n"); return; } if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSJUSTAUTH && radius[r].state != RADIUSSTART && radius[r].state != RADIUSSTOP && radius[r].state != RADIUSINTERIM) { LOG(1, s, session[s].tunnel, " Unexpected RADIUS response\n"); return; } t = session[s].tunnel; calc_auth(buf, len, radius[r].auth, hash); do { if (memcmp(hash, buf + 4, 16)) { LOG(0, s, session[s].tunnel, " Incorrect auth on RADIUS response!! (wrong secret in radius config?)\n"); return; // Do nothing. On timeout, it will try the next radius server. } if (((radius[r].state == RADIUSAUTH ||radius[r].state == RADIUSJUSTAUTH) && r_code != AccessAccept && r_code != AccessReject) || ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP || radius[r].state == RADIUSINTERIM) && r_code != AccountingResponse)) { LOG(1, s, session[s].tunnel, " Unexpected RADIUS response %s\n", radius_code(r_code)); return; // We got something we didn't expect. Let the timeouts take // care off finishing the radius session if that's really correct. } if (radius[r].state == RADIUSAUTH || radius[r].state == RADIUSJUSTAUTH) { // run post-auth plugin struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (r_code == AccessAccept), radius[r].chap ? PPPCHAP : PPPPAP }; run_plugins(PLUGIN_POST_AUTH, &packet); r_code = packet.auth_allowed ? AccessAccept : AccessReject; if (r_code == AccessAccept) { // Login successful // Extract IP, routes, etc uint8_t *p = buf + 20; uint8_t *e = buf + len; uint8_t tag; uint8_t strtemp[256]; lac_reset_rad_tag_tunnel_ctxt(); for (; p + 2 <= e && p[1] && p + p[1] <= e; p += p[1]) { if (*p == 26 && p[1] >= 7) { // Vendor-Specific Attribute uint32_t vendor = ntohl(*(int *)(p + 2)); uint8_t attrib = *(p + 6); int attrib_length = *(p + 7) - 2; LOG(4, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%u Attrib=%u Length=%d\n", vendor, attrib, attrib_length); if (vendor == 9 && attrib == 1) // Cisco-AVPair { if (attrib_length < 0) continue; LOG(3, s, session[s].tunnel, " Cisco-AVPair value: %.*s\n", attrib_length, p + 8); handle_avpair(s, p + 8, attrib_length); continue; } else if (vendor == 529 && attrib >= 135 && attrib <= 136) // Ascend { // handle old-format ascend DNS attributes below p += 6; } else { LOG(3, s, session[s].tunnel, " Unknown vendor-specific\n"); continue; } } if (*p == 8) { // Framed-IP-Address if (p[1] < 6) continue; session[s].ip = ntohl(*(uint32_t *) (p + 2)); session[s].ip_pool_index = -1; LOG(3, s, session[s].tunnel, " Radius reply contains IP address %s\n", fmtaddr(htonl(session[s].ip), 0)); if (session[s].ip == 0xFFFFFFFE) session[s].ip = 0; // assign from pool } else if (*p == 135) { // DNS address if (p[1] < 6) continue; session[s].dns1 = ntohl(*(uint32_t *) (p + 2)); LOG(3, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", fmtaddr(htonl(session[s].dns1), 0)); } else if (*p == 136) { // DNS address if (p[1] < 6) continue; session[s].dns2 = ntohl(*(uint32_t *) (p + 2)); LOG(3, s, session[s].tunnel, " Radius reply contains secondary DNS address %s\n", fmtaddr(htonl(session[s].dns2), 0)); } else if (*p == 22) { // Framed-Route in_addr_t ip = 0; uint8_t u = 0; uint8_t bits = 0; uint8_t *n = p + 2; uint8_t *e = p + p[1]; while (n < e && (isdigit(*n) || *n == '.')) { if (*n == '.') { ip = (ip << 8) + u; u = 0; } else u = u * 10 + *n - '0'; n++; } ip = (ip << 8) + u; if (*n == '/') { n++; while (n < e && isdigit(*n)) bits = bits * 10 + *n++ - '0'; } else if ((ip >> 24) < 128) bits = 8; else if ((ip >> 24) < 192) bits = 16; else bits = 24; if (routes == MAXROUTE) { LOG(1, s, session[s].tunnel, " Too many routes\n"); } else if (ip) { LOG(3, s, session[s].tunnel, " Radius reply contains route for %s/%d\n", fmtaddr(htonl(ip), 0), bits); session[s].route[routes].ip = ip; session[s].route[routes].prefixlen = bits; routes++; } } else if (*p == 11) { // Filter-Id char *filter = (char *) p + 2; int l = p[1] - 2; char *suffix; int f; uint8_t *fp = 0; LOG(3, s, session[s].tunnel, " Radius reply contains Filter-Id \"%.*s\"\n", l, filter); if ((suffix = memchr(filter, '.', l))) { int b = suffix - filter; if (l - b == 3 && !memcmp("in", suffix+1, 2)) fp = &session[s].filter_in; else if (l - b == 4 && !memcmp("out", suffix+1, 3)) fp = &session[s].filter_out; l = b; } if (!fp) { LOG(3, s, session[s].tunnel, " Invalid filter\n"); continue; } if ((f = find_filter(filter, l)) < 0 || !*ip_filters[f].name) { LOG(3, s, session[s].tunnel, " Unknown filter\n"); } else { *fp = f + 1; ip_filters[f].used++; } } else if (*p == 27) { // Session-Timeout if (p[1] < 6) continue; session[s].session_timeout = ntohl(*(uint32_t *)(p + 2)); LOG(3, s, session[s].tunnel, " Radius reply contains Session-Timeout = %u\n", session[s].session_timeout); if(!session[s].session_timeout && config->kill_timedout_sessions) sessionshutdown(s, "Session timeout is zero", CDN_ADMIN_DISC, 0); } else if (*p == 28) { // Idle-Timeout if (p[1] < 6) continue; session[s].idle_timeout = ntohl(*(uint32_t *)(p + 2)); LOG(3, s, session[s].tunnel, " Radius reply contains Idle-Timeout = %u\n", session[s].idle_timeout); } else if (*p == 99) { // Framed-IPv6-Route struct in6_addr r6; int prefixlen; uint8_t *n = p + 2; uint8_t *e = p + p[1]; uint8_t *m = memchr(n, '/', e - n); *m++ = 0; inet_pton(AF_INET6, (char *) n, &r6); prefixlen = 0; while (m < e && isdigit(*m)) { prefixlen = prefixlen * 10 + *m++ - '0'; } if (prefixlen) { if (routes6 == MAXROUTE6) { LOG(1, s, session[s].tunnel, " Too many IPv6 routes\n"); } else { LOG(3, s, session[s].tunnel, " Radius reply contains route for %s/%d\n", n, prefixlen); session[s].route6[routes6].ipv6route = r6; session[s].route6[routes6].ipv6prefixlen = prefixlen; routes6++; } } } else if (*p == 123) { // Delegated-IPv6-Prefix if ((p[1] > 4) && (p[3] > 0) && (p[3] <= 128)) { char ipv6addr[INET6_ADDRSTRLEN]; if (routes6 == MAXROUTE6) { LOG(1, s, session[s].tunnel, " Too many IPv6 routes\n"); } else { memcpy(&session[s].route6[routes6].ipv6route, &p[4], p[1] - 4); session[s].route6[routes6].ipv6prefixlen = p[3]; LOG(3, s, session[s].tunnel, " Radius reply contains Delegated IPv6 Prefix %s/%d\n", inet_ntop(AF_INET6, &session[s].route6[routes6].ipv6route, ipv6addr, INET6_ADDRSTRLEN), session[s].route6[routes6].ipv6prefixlen); routes6++; } } } else if (*p == 168) { // Framed-IPv6-Address if (p[1] == 18) { char ipv6addr[INET6_ADDRSTRLEN]; memcpy(&session[s].ipv6address, &p[2], 16); LOG(3, s, session[s].tunnel, " Radius reply contains Framed-IPv6-Address %s\n", inet_ntop(AF_INET6, &session[s].ipv6address, ipv6addr, INET6_ADDRSTRLEN)); } } else if (*p == 25) { // Class if (p[1] < 3) continue; session[s].classlen = p[1] - 2; if (session[s].classlen > MAXCLASS) session[s].classlen = MAXCLASS; memcpy(session[s].class, p + 2, session[s].classlen); } else if (*p == 64) { // Tunnel-Type if (p[1] != 6) continue; tag = p[2]; LOG(3, s, session[s].tunnel, " Radius reply Tunnel-Type:%d %d\n", tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF); // Fill context lac_set_rad_tag_tunnel_type(tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF); /* Request open tunnel to remote LNS*/ OpentunnelReq = 1; } else if (*p == 65) { // Tunnel-Medium-Type if (p[1] < 6) continue; tag = p[2]; LOG(3, s, session[s].tunnel, " Radius reply Tunnel-Medium-Type:%d %d\n", tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF); // Fill context lac_set_rad_tag_tunnel_medium_type(tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF); } else if (*p == 67) { // Tunnel-Server-Endpoint if (p[1] < 3) continue; tag = p[2]; //If the Tag field is greater than 0x1F, // it SHOULD be interpreted as the first byte of the following String field. memset(strtemp, 0, 256); if (tag > 0x1F) { tag = 0; memcpy(strtemp, (p + 2), p[1]-2); } else memcpy(strtemp, (p + 3), p[1]-3); LOG(3, s, session[s].tunnel, " Radius reply Tunnel-Server-Endpoint:%d %s\n", tag, strtemp); // Fill context lac_set_rad_tag_tunnel_serv_endpt(tag, (char *) strtemp); } else if (*p == 69) { // Tunnel-Password size_t lentemp; if (p[1] < 5) continue; tag = p[2]; memset(strtemp, 0, 256); lentemp = p[1]-3; memcpy(strtemp, (p + 3), lentemp); if (!rad_tunnel_pwdecode(strtemp, &lentemp, config->radiussecret, radius[r].auth)) { LOG_HEX(3, "Error Decode Tunnel-Password, Dump Radius response:", p, p[1]); continue; } LOG(3, s, session[s].tunnel, " Radius reply Tunnel-Password:%d %s\n", tag, strtemp); if (strlen((char *) strtemp) > 63) { LOG(1, s, session[s].tunnel, "tunnel password is too long (>63)\n"); continue; } // Fill context lac_set_rad_tag_tunnel_password(tag, (char *) strtemp); } else if (*p == 82) { // Tunnel-Assignment-Id if (p[1] < 3) continue; tag = p[2]; //If the Tag field is greater than 0x1F, // it SHOULD be interpreted as the first byte of the following String field. memset(strtemp, 0, 256); if (tag > 0x1F) { tag = 0; memcpy(strtemp, (p + 2), p[1]-2); } else memcpy(strtemp, (p + 3), p[1]-3); LOG(3, s, session[s].tunnel, " Radius reply Tunnel-Assignment-Id:%d %s\n", tag, strtemp); // Fill context lac_set_rad_tag_tunnel_assignment_id(tag, (char *) strtemp); } } } else if (r_code == AccessReject) { LOG(2, s, session[s].tunnel, " Authentication rejected for %s\n", session[s].user); sessionkill(s, "Authentication rejected"); break; } if ((!config->disable_lac_func) && OpentunnelReq) { char assignment_id[256]; // Save radius tag context to conf lac_save_rad_tag_tunnels(s); memset(assignment_id, 0, 256); if (!lac_rad_select_assignment_id(s, assignment_id)) break; // Error no assignment_id LOG(3, s, session[s].tunnel, "Select Tunnel Remote LNS for assignment_id == %s\n", assignment_id); if (lac_rad_forwardtoremotelns(s, assignment_id, session[s].user)) { int ro; // Sanity check, no local IP to session forwarded session[s].ip = 0; for (ro = 0; r < MAXROUTE && session[s].route[ro].ip; r++) { session[s].route[ro].ip = 0; } break; } } // process auth response if (radius[r].chap) { // CHAP uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0); if (!p) return; // Abort! *p = (r_code == AccessAccept) ? 3 : 4; // ack/nak p[1] = radius[r].id; *(uint16_t *) (p + 2) = ntohs(4); // no message tunnelsend(b, (p - b) + 4, t); // send it LOG(3, s, session[s].tunnel, " CHAP User %s authentication %s.\n", session[s].user, (r_code == AccessAccept) ? "allowed" : "denied"); } else { // PAP uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP, 0, 0, 0); if (!p) return; // Abort! // ack/nak *p = r_code; p[1] = radius[r].id; *(uint16_t *) (p + 2) = ntohs(5); p[4] = 0; // no message tunnelsend(b, (p - b) + 5, t); // send it LOG(3, s, session[s].tunnel, " PAP User %s authentication %s.\n", session[s].user, (r_code == AccessAccept) ? "allowed" : "denied"); } if (!session[s].dns1 && config->default_dns1) { session[s].dns1 = ntohl(config->default_dns1); LOG(3, s, t, " Sending dns1 = %s\n", fmtaddr(config->default_dns1, 0)); } if (!session[s].dns2 && config->default_dns2) { session[s].dns2 = ntohl(config->default_dns2); LOG(3, s, t, " Sending dns2 = %s\n", fmtaddr(config->default_dns2, 0)); } // Valid Session, set it up session[s].unique_id = 0; sessionsetup(s, t); } else { // An ack for a stop or start record. LOG(3, s, t, " RADIUS accounting ack recv in state %s\n", radius_state(radius[r].state)); break; } } while (0); // finished with RADIUS radiusclear(r, s); } // Send a retry for RADIUS/CHAP message void radiusretry(uint16_t r) { sessionidt s = radius[r].session; tunnelidt t = 0; CSTAT(radiusretry); if (s) t = session[s].tunnel; switch (radius[r].state) { case RADIUSCHAP: // sending CHAP down PPP sendchap(s, t); break; case RADIUSAUTH: // sending auth to RADIUS server case RADIUSJUSTAUTH: // sending auth to RADIUS server case RADIUSSTART: // sending start accounting to RADIUS server case RADIUSSTOP: // sending stop accounting to RADIUS server case RADIUSINTERIM: // sending interim accounting to RADIUS server radiussend(r, radius[r].state); break; default: case RADIUSNULL: // Not in use case RADIUSWAIT: // waiting timeout before available, in case delayed reply from RADIUS server // free up RADIUS task radiusclear(r, s); LOG(3, s, session[s].tunnel, "Freeing up radius session %d\n", r); break; } } extern int daefd; void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local) { int i, r_code, r_id, length, attribute_length; uint8_t *packet, attribute; hasht hash; char username[MAXUSER] = ""; in_addr_t nas = 0; in_addr_t ip = 0; uint32_t port = 0; uint32_t error = 0; sessionidt s = 0; tunnelidt t; int fin = -1; int fout = -1; uint8_t *avpair[64]; int avpair_len[sizeof(avpair)/sizeof(*avpair)]; int avp = 0; int auth_only = 0; uint8_t *p; LOG(3, 0, 0, "DAE request from %s\n", fmtaddr(addr->sin_addr.s_addr, 0)); LOG_HEX(5, "DAE Request", buf, len); if (len < 20 || len < ntohs(*(uint16_t *) (buf + 2))) { LOG(1, 0, 0, "Duff DAE request length %d\n", len); return; } r_code = buf[0]; // request type r_id = buf[1]; // radius indentifier. if (r_code != DisconnectRequest && r_code != CoARequest) { LOG(1, 0, 0, "Unrecognised DAE request %s\n", radius_code(r_code)); return; } if (!config->cluster_iam_master) { master_forward_dae_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } len = ntohs(*(uint16_t *) (buf + 2)); LOG(3, 0, 0, "Received DAE %s, id %d\n", radius_code(r_code), r_id); // check authenticator calc_auth(buf, len, zero, hash); if (memcmp(hash, buf + 4, 16) != 0) { LOG(1, 0, 0, "Incorrect vector in DAE request (wrong secret in radius config?)\n"); return; } // unpack attributes packet = buf + 20; length = len - 20; while (length > 0) { attribute = *packet++; attribute_length = *packet++; if (attribute_length < 2) break; length -= attribute_length; attribute_length -= 2; switch (attribute) { case 1: /* username */ len = attribute_length < MAXUSER ? attribute_length : MAXUSER - 1; memcpy(username, packet, len); username[len] = 0; LOG(4, 0, 0, " Received DAE User-Name: %s\n", username); break; case 4: /* nas ip address */ nas = *(uint32_t *) packet; // net order if (nas != config->bind_address) error = 403; // NAS identification mismatch LOG(4, 0, 0, " Received DAE NAS-IP-Address: %s\n", fmtaddr(nas, 0)); break; case 5: /* nas port */ port = ntohl(*(uint32_t *) packet); if (port < 1 || port > MAXSESSION) error = 404; LOG(4, 0, 0, " Received DAE NAS-Port: %u\n", port); break; case 6: /* service type */ { uint32_t service_type = ntohl(*(uint32_t *) packet); auth_only = service_type == 8; // Authenticate only LOG(4, 0, 0, " Received DAE Service-Type: %u\n", service_type); } break; case 8: /* ip address */ ip = *(uint32_t *) packet; // net order LOG(4, 0, 0, " Received DAE Framed-IP-Address: %s\n", fmtaddr(ip, 0)); break; case 11: /* filter id */ LOG(4, 0, 0, " Received DAE Filter-Id: %.*s\n", attribute_length, packet); if (!(p = memchr(packet, '.', attribute_length))) { error = 404; // invalid request break; } len = p - packet; i = find_filter((char *) packet, len); if (i < 0 || !*ip_filters[i].name) { error = 404; break; } if (!memcmp(p, ".in", attribute_length - len)) fin = i + 1; else if (!memcmp(p, ".out", attribute_length - len)) fout = i + 1; else error = 404; break; case 26: /* vendor specific */ if (attribute_length >= 6 && ntohl(*(uint32_t *) packet) == 9 // Cisco && *(packet + 4) == 1 // Cisco-AVPair && *(packet + 5) >= 2) // length { int len = *(packet + 5) - 2; uint8_t *a = packet + 6; LOG(4, 0, 0, " Received DAE Cisco-AVPair: %.*s\n", len, a); if (avp < sizeof(avpair)/sizeof(*avpair) - 1) { avpair[avp] = a; avpair_len[avp++] = len; } } break; } packet += attribute_length; } if (!error && auth_only) { if (fin != -1 || fout != -1 || avp) error = 401; // unsupported attribute else error = 405; // unsupported service } if (!error && !(port || ip || *username)) error = 402; // missing attribute // exact match for SID if given if (!error && port) { s = port; if (!session[s].opened) error = 503; // not found } if (!error && ip) { // find/check session by IP i = sessionbyip(ip); if (!i || (s && s != i)) // not found or mismatching port error = 503; else s = i; } if (!error && *username) { if (s) { if (strcmp(session[s].user, username)) error = 503; } else if (!(s = sessionbyuser(username))) error = 503; } t = session[s].tunnel; switch (r_code) { case DisconnectRequest: // Packet of Disconnect/Death if (error) { r_code = DisconnectNAK; break; } LOG(3, s, t, " DAE Disconnect %d (%s)\n", s, session[s].user); r_code = DisconnectACK; sessionshutdown(s, "Requested by PoD", CDN_ADMIN_DISC, TERM_ADMIN_RESET); // disconnect session break; case CoARequest: // Change of Authorization if (error) { r_code = CoANAK; break; } LOG(3, s, t, " DAE Change %d (%s)\n", s, session[s].user); r_code = CoAACK; // reset { struct param_radius_reset p = { &tunnel[session[s].tunnel], &session[s] }; run_plugins(PLUGIN_RADIUS_RESET, &p); } // apply filters if (fin == -1) fin = 0; else LOG(3, s, t, " Filter in %d (%s)\n", fin, ip_filters[fin - 1].name); if (fout == -1) fout = 0; else LOG(3, s, t, " Filter out %d (%s)\n", fout, ip_filters[fout - 1].name); filter_session(s, fin, fout); // process cisco av-pair(s) for (i = 0; i < avp; i++) { LOG(3, s, t, " Cisco-AVPair: %.*s\n", avpair_len[i], avpair[i]); handle_avpair(s, avpair[i], avpair_len[i]); } cluster_send_session(s); break; } // send response packet = buf; *packet++ = r_code; *packet++ = r_id; // skip len + auth packet += 2 + 16; len = packet - buf; // add attributes if (error) { // add error cause *packet++ = 101; *packet++ = 6; *(uint32_t *) packet = htonl(error); len += 6; } *((uint16_t *)(buf + 2)) = htons(len); // make vector calc_auth(buf, len, hash, buf + 4); LOG(3, 0, 0, "Sending DAE %s, id=%d\n", radius_code(r_code), r_id); // send DAE response if (sendtofrom(daefd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) addr, alen, local) < 0) LOG(0, 0, 0, "Error sending DAE response packet: %s\n", strerror(errno)); } // Decrypte the encrypted Tunnel Password. // Defined in RFC-2868. // the pl2tpsecret buffer must set to 256 characters. // return 0 on decoding error else length of decoded l2tpsecret int rad_tunnel_pwdecode(uint8_t *pl2tpsecret, size_t *pl2tpsecretlen, const char *radiussecret, const uint8_t * auth) { MD5_CTX ctx, oldctx; hasht hash; int secretlen; unsigned i, n, len, decodedlen; /* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 6 7 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Salt | Salt | String .......... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/ len = *pl2tpsecretlen; if (len < 2) { LOG(1, 0, 0, "tunnel password is too short, We need at least a salt\n"); return 0; } if (len <= 3) { pl2tpsecret[0] = 0; *pl2tpsecretlen = 0; LOG(1, 0, 0, "tunnel passwd is empty !!!\n"); return 0; } len -= 2; /* discount the salt */ //Use the secret to setup the decryption secretlen = strlen(radiussecret); MD5_Init(&ctx); MD5_Update(&ctx, (void *) radiussecret, secretlen); oldctx = ctx; /* save intermediate work */ // Set up the initial key: // b(1) = MD5(radiussecret + auth + salt) MD5_Update(&ctx, (void *) auth, 16); MD5_Update(&ctx, pl2tpsecret, 2); decodedlen = 0; for (n = 0; n < len; n += 16) { int base = 0; if (n == 0) { MD5_Final(hash, &ctx); ctx = oldctx; // the first octet, it's the 'data_len' // Check is correct decodedlen = pl2tpsecret[2] ^ hash[0]; if (decodedlen >= len) { LOG(1, 0, 0, "tunnel password is too long !!!\n"); return 0; } MD5_Update(&ctx, pl2tpsecret + 2, 16); base = 1; } else { MD5_Final(hash, &ctx); ctx = oldctx; MD5_Update(&ctx, pl2tpsecret + n + 2, 16); } for (i = base; i < 16; i++) { pl2tpsecret[n + i - 1] = pl2tpsecret[n + i + 2] ^ hash[i]; } } if (decodedlen > 239) decodedlen = 239; *pl2tpsecretlen = decodedlen; pl2tpsecret[decodedlen] = 0; return decodedlen; }; l2tpns-2.3.3/scripts/000077500000000000000000000000001400724550600144335ustar00rootroot00000000000000l2tpns-2.3.3/scripts/l2tpns-capture000066400000000000000000000026401400724550600172430ustar00rootroot00000000000000#! /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; } } l2tpns-2.3.3/scripts/l2tpns-monitor000066400000000000000000000011711400724550600172650ustar00rootroot00000000000000#!/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 l2tpns-2.3.3/scripts/l2tpns.script000066400000000000000000000030261400724550600171040ustar00rootroot00000000000000#!/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 l2tpns-2.3.3/scripts/l2tpns.service000066400000000000000000000004531400724550600172410ustar00rootroot00000000000000[Unit] Description=Layer 2 tunneling protocol network server (LNS) After=network.target Documentation=man:l2tpns(8) man:startup-config(5) [Service] EnvironmentFile=-/etc/default/l2tpns ExecStart=/usr/sbin/l2tpns $L2TPNS_OPTS ExecReload=/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target l2tpns-2.3.3/sessionctl.c000066400000000000000000000032151400724550600152770ustar00rootroot00000000000000#include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" #include "control.h" /* session control */ 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; } l2tpns-2.3.3/setrxspeed.c000066400000000000000000000013361400724550600153010ustar00rootroot00000000000000#include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" /* fudge up session rx speed if not set */ 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; } l2tpns-2.3.3/snoopctl.c000066400000000000000000000046501400724550600147560ustar00rootroot00000000000000#include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" #include "control.h" /* snoop control */ 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; } l2tpns-2.3.3/stripdomain.c000066400000000000000000000012011400724550600154330ustar00rootroot00000000000000#include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" /* strip domain part of username before sending RADIUS requests */ 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; } l2tpns-2.3.3/tbf.c000066400000000000000000000201751400724550600136700ustar00rootroot00000000000000// L2TPNS: token bucket filters #include #include #include "dhcp6.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, const 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; } l2tpns-2.3.3/tbf.h000066400000000000000000000031051400724550600136670ustar00rootroot00000000000000#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, const char *command, char **argv, int argc); #endif /* __TBF_H__ */ l2tpns-2.3.3/test/000077500000000000000000000000001400724550600137235ustar00rootroot00000000000000l2tpns-2.3.3/test/Makefile000066400000000000000000000003041400724550600153600ustar00rootroot00000000000000CFLAGS = -g -Wall -W -std=c99 -pedantic TARGETS = bounce generateload radius all: $(TARGETS) clean: rm -f $(TARGETS) ../md5.o: ../md5.c ../md5.h $(MAKE) -C .. md5.o radius: radius.c ../md5.o l2tpns-2.3.3/test/README000066400000000000000000000004711400724550600146050ustar00rootroot00000000000000generateload, 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. l2tpns-2.3.3/test/bounce.c000066400000000000000000000035661400724550600153540ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; socklen_t alen = sizeof(addr); int 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 unusedg __attribute__ ((unused))) { 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); } l2tpns-2.3.3/test/generateload.c000066400000000000000000000717621400724550600165360ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #define __USE_XOPEN_EXTENDED #define __USE_MISC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PPPLCP 0xc021 #define PPPPAP 0xc023 #define PPPCHAP 0xc223 #define PPPIPCP 0x8021 #define PPPIP 0x0021 #define PPPCCP 0x80fd #define CONFREQ 1 #define CONFACK 2 #define CONFNAK 3 #define CONFREJ 4 #define TERMREQ 5 #define TERMACK 6 #define CODEREJ 7 #define PROTREJ 8 #define ECHOREQ 9 #define ECHOREP 10 #define DISCREQ 11 #define PACKET_LENGTH 1000 #define TARGET_PPS 5000 #define TARGET "211.29.131.33" #define GWADDR "211.29.131.30" #define NUM_SESSIONS 1 #define MAX_PACKETS 0 #define AVG_SIZE 5 typedef unsigned short u16; typedef unsigned int u32; typedef unsigned char u8; char *lcp_codes[] = { "reserved", "CONFREQ", "CONFACK", "CONFNAK", "CONFREJ", "TERMREQ", "TERMACK", "CODEREJ", "PROTREJ", "ECHOREQ", "ECHOREP", "DISCREQ", }; char *mtypes[] = { "reserved", "SCCRQ", "SCCRP", "SCCCN", "StopCCN", // 4 "reserved", "HELLO", "OCRQ", "OCRP", "OCCN", "ICRQ", // 10 "ICRP", "ICCN", "reserved", "CDN", "WEN", // 15 "SLI", }; char *attributes[] = { "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) "Reserved 20", // 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 }; char *result_codes[] = { "Reserved", "General request to clear control connection", "General error--Error Code indicates the problem", "Control channel already exists", "Requester is not authorized to establish a control channel", "The protocol version of the requester is not supported", "Requester is being shut down", "Finite State Machine error", }; char *error_codes[] = { "No general error", "No control connection exists yet for this LAC-LNS pair", "Length is wrong", "One of the field values was out of range or reserved field was non-zero", "Insufficient resources to handle this operation now", "The Session ID is invalid in this context", "A generic vendor-specific error occurred in the LAC", "Try another LNS", "Session or tunnel was shutdown due to receipt of an unknown AVP with the M-bit set", }; typedef struct { char buf[4096]; int length; } controlt; typedef struct avp_s { int length; int type; struct avp_s *next; char value[1024]; } avp; typedef struct { int length; u16 session; u16 tunnel; u16 ns; u16 nr; u16 mtype; char *buf; avp *first; avp *last; } control_message; typedef struct { long long send_count, recv_count; long long spkt, rpkt ; int dropped; long sbytes, rbytes ; int quitit; struct sessiont { short remote_session; char open; int ppp_state; unsigned char ppp_identifier; int addr; } sessions[65536]; int active_sessions ; } sharedt; sharedt * ss; void controlsend(controlt * c, short t, short s); void controlnull(short t); controlt *controlnew(u16 mtype); void controls(controlt * c, u16 avp, char *val, u8 m); void control16(controlt * c, u16 avp, u16 val, u8 m); void control32(controlt * c, u16 avp, u32 val, u8 m); void controlfree(controlt *c); control_message *parsecontrol(char *buf, int length); void dump_control_message(control_message *c); u32 avp_get_32(control_message *c, int id); u16 avp_get_16(control_message *c, int id); char *avp_get_s(control_message *c, int id); void reader_thread(void); void skip_zlb(); void cm_free(control_message *m); controlt *ppp_new(u16 session, int protocol); void ppp_free(controlt *packet); controlt *ppp_lcp(u16 s, unsigned char type, char identifier); controlt *ppp_ipcp(u16 s, unsigned char type, char identifier); void ppp_send(controlt *c); void ppp_add_16(controlt * c, u16 val); void ppp_add_32(controlt * c, u32 val); void ppp_add_s(controlt * c, char *val); void ppp_lcp_add_option(controlt *c, unsigned char option, unsigned char length, int data); void dump_ppp_packet(char *packet, int l); controlt *ppp_pap(u16 s, unsigned char type, char identifier, char *username, char *password); char *inet_toa(unsigned long addr); __u16 checksum(unsigned char *addr, int count); void sigalarm(int junk); void sigint(int signal); void clean_shutdown(); void print_report(); int ns = 0, nr = 0; int udpfd; int t = 0; struct sockaddr_in gatewayaddr; int numsessions = NUM_SESSIONS; int packet_length = PACKET_LENGTH; int target_pps = TARGET_PPS; char *target = TARGET; char *gwaddr = GWADDR; int max_packets = MAX_PACKETS; int ppsend; int do_init = 1; char **session_usernames; char *base_username = "dslloadtest"; char *base_password = "testing"; char *suffix = "@optusnet.com.au"; int main(int argc, char *argv[]) { int s; unsigned char *packet; ss = (sharedt*) mmap(NULL, sizeof(*ss), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); // Process Arguments {{{ while ((s = getopt(argc, argv, "?hs:g:l:p:m:t:nU:P:")) > 0) { switch (s) { case 's' : numsessions = atoi(optarg); if (numsessions <= 0) { printf("You must have at least 1 session\n"); return -1; } break; case 'l' : packet_length = atoi(optarg); if (packet_length < 64) { printf("You must have at least 64 byte packets\n"); return -1; } break; case 'n' : do_init = 0; break; case 'p' : target_pps = atoi(optarg); break; case 'm' : max_packets = atoi(optarg); if (max_packets < 50) { printf("You must send at least 50 packets.\n"); return -1; } break; case 't' : target = strdup(optarg); break; case 'g' : gwaddr = strdup(optarg); break; case 'U' : base_username = strdup(optarg); break; case 'P' : base_password = strdup(optarg); break; case 'h' : case '?' : printf("Options:\n"); printf("\t-s number of ss->sessions\n"); printf("\t-l packet length\n"); printf("\t-p target pps\n"); printf("\t-m maximum number of packets\n"); printf("\t-t target IP address\n"); printf("\t-g gateway IP address\n"); printf("\t-U username (or base if multiple)\n"); printf("\t-P password\n"); return(0); break; } } if (target_pps) ppsend = target_pps / 50; else ppsend = 0; packet = calloc(4096, 1); memset(ss->sessions, 0, sizeof(ss->sessions)); if (do_init) printf("Creating %d ss->sessions to %s\n", numsessions, gwaddr); printf("Targeting %d packets per second\n", target_pps); if (max_packets) printf("Sending a maximum of %d packets\n", max_packets); printf("Sending packets to %s\n", target); printf("Sending %d byte packets\n", packet_length); session_usernames = (char **)calloc(sizeof(char *), numsessions); if (numsessions > 1) { int sul = strlen(base_username) + 10; int i; for (i = 0; i < numsessions; i++) { session_usernames[i] = (char *)calloc(sul, 1); snprintf(session_usernames[i], sul, "%s%d", base_username, i+1); } } else { session_usernames[0] = strdup(base_username); } // }}} // Create socket/*{{{*/ { int on = 1; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(38001); udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (udpfd <= 0) { perror("socket"); return -1; } setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) { perror("bind"); return -1; } printf("Bound to port %d\n", htons(addr.sin_port)); }/*}}}*/ memset(&gatewayaddr, 0, sizeof(gatewayaddr)); gatewayaddr.sin_family = AF_INET; gatewayaddr.sin_port = htons(1701); inet_aton(gwaddr, &gatewayaddr.sin_addr); // Create tunnel/*{{{*/ if (do_init) { controlt *c; control_message *r; c = controlnew(1); // SCCRQ controls(c, 7, "loadtest", 0); // Tunnel Hostname controls(c, 8, "OIE", 0); // Vendor Name control16(c, 9, 1, 0); // Assigned Tunnel ID control16(c, 2, 256, 0); // Version 1.0 control16(c, 3, 1, 0); // Framing (Async) control16(c, 4, 1, 0); // Bearer (Digital) control16(c, 10, 20, 0); // Receive Window Size controlsend(c, 0, 0); controlfree(c); // Receive reply/*{{{*/ { struct sockaddr_in addr; int l; socklen_t alen = sizeof(addr); l = recvfrom(udpfd, packet, 4096, 0, (void *) &addr, &alen); if (l < 0) { printf("Error creating tunnel: %s\n", strerror(errno)); return -1; } printf("Received "); r = parsecontrol((char *) packet, l); if (!r->first) { printf("Invalid packet.. no first avp\n"); return -1; } printf("Assigned tunnel: %d\n", t = avp_get_16(r, 9)); cm_free(r); c = controlnew(3); // SCCCN controlsend(c, t, 0); controlfree(c); skip_zlb(); }/*}}}*/ }/*}}}*/ // Create ss->sessions/*{{{*/ if (do_init) { for (s = 1; s <= numsessions; s++) { controlt *c; c = controlnew(10); // ICRQ controls(c, 21, "12356", 0); // Called Number controls(c, 22, "000", 0); // Calling Number control16(c, 14, s, 0); // Assigned Session ID controlsend(c, t, 0); controlfree(c); usleep(15000); // 15 ms } } printf("All session create requests sent...\n");/*}}}*/ if ( fork() == 0) { reader_thread(); exit(0); } { char tmp[512]; fprintf(stderr, "Press enter to begin sending traffic\n"); fgets(tmp, 512, stdin); } fprintf(stderr, "Beginning sending traffic through %d ss->sessions\n", ss->active_sessions); printf(" TS: Total Packets Sent\n"); printf(" TL: Total Packets Lost\n"); printf(" PL: Packet Loss\n"); printf(" SS: Send Speed\n"); printf(" RS: Receive Speed\n"); printf(" SP: Packets/Second Sent\n"); printf(" RP: Packets/Second Received\n"); printf(" NS: Number of active ss->sessions\n"); signal(SIGALRM, sigalarm); signal(SIGINT, sigint); alarm(1); // Traffic generation loop {{{ { struct sockaddr_in to; struct iphdr *iph; struct udphdr *udph; char *data; int len = 0; unsigned int seq = 0; controlt *c; // Get address memset(&to, 0, sizeof(struct sockaddr_in)); to.sin_family = AF_INET; inet_aton(target, &to.sin_addr); c = ppp_new(1, PPPIP); iph = (struct iphdr *)(c->buf + c->length); udph = (struct udphdr *)(c->buf + c->length + sizeof(struct iphdr)); data = (char *)(c->buf + c->length + sizeof(struct iphdr) + sizeof(struct udphdr)); len = sizeof(struct iphdr) + sizeof(struct udphdr); c->length += len; //IP c->length += sizeof(struct iphdr); iph->tos = 0; iph->id = ntohs(1); iph->frag_off = ntohs(1 << 14); iph->ttl = 30; iph->check = 0; iph->version = 4; iph->ihl = 5; iph->protocol = 17; memcpy(&iph->daddr, &to.sin_addr, sizeof(iph->daddr)); // UDP udph->source = ntohs(39999); udph->dest = ntohs(39000); udph->check = 0; // Data memset(data, 64, 1500); udph->len = ntohs(sizeof(struct udphdr) + packet_length); iph->tot_len = ntohs(len + packet_length); c->length += packet_length; while (!ss->quitit && ss->active_sessions) { int i; for (i = 1; i <= numsessions && !ss->quitit; i++) { // Skip ss->sessions that aren't active yet if (!ss->sessions[i].open || ss->sessions[i].ppp_state != 2) continue; *(u16 *)(c->buf + 4) = htons(ss->sessions[i].remote_session); // Session ID iph->saddr = ss->sessions[i].addr; iph->check = 0; iph->check = ntohs(checksum((unsigned char *)iph, sizeof(struct iphdr))); *((unsigned int *) data) = seq++; ppp_send(c); ss->send_count++; ss->spkt++; ss->sbytes += c->length; if (ppsend && ss->send_count % ppsend == 0) { struct timespec req; req.tv_sec = 0; req.tv_nsec = 5 * 1000 * 1000; nanosleep(&req, NULL); } if (max_packets && ss->send_count >= max_packets) ss->quitit++; } } c->length -= packet_length; }/*}}}*/ clean_shutdown(); print_report(); close(udpfd); return 0; } void print_report() { float loss; loss = 100 - (((ss->recv_count * 1.0) / (ss->send_count * 1.0)) * 100.0); printf("\n"); printf("Total Packets Sent: %llu\n", ss->send_count); printf("Total Packets Received: %llu\n", ss->recv_count); printf("Overall Packet Loss: %0.2f%%", loss); printf("\n"); } void clean_shutdown()/*{{{*/ { int i; for (i = 0; i < numsessions; i++) { // Close Session controlt *c; if (!ss->sessions[i].open) continue; c = controlnew(14); // CDN control16(c, 14, i, 0); // Assigned Session ID control16(c, 1, 1, 0); // Result Code controlsend(c, t, ss->sessions[i].remote_session); controlfree(c); } // Close Tunnel { controlt *c; c = controlnew(4); // StopCCN control16(c, 9, 1, 0); // Assigned Tunnel ID control16(c, 1, 1, 0); // Result Code controlsend(c, t, 0); controlfree(c); } }/*}}}*/ void sigint(int unused __attribute__ ((unused))) { ss->quitit++; } void sigalarm(int unused __attribute__ ((unused))) { static unsigned long long last_rpkts[AVG_SIZE], last_spkts[AVG_SIZE]; static int last = 0, avg_count = 0; unsigned int avg_s = 0, avg_r = 0; int i; float loss; last_rpkts[last] = ss->rpkt; last_spkts[last] = ss->spkt; last = (last + 1) % AVG_SIZE; if (avg_count < AVG_SIZE) avg_count++; for (i = 0; i < avg_count; i++) { avg_s += last_spkts[i]; avg_r += last_rpkts[i]; } avg_s /= avg_count; avg_r /= avg_count; loss = 100 - (((avg_r * 1.0) / (avg_s * 1.0)) * 100.0); fprintf(stderr, "TS:%llu TL:%lld DR:%4d PL:%-3.2f%% SS:%0.1fMbits/s RS:%0.1fMbits/s NS:%u SP:%u RP:%u\n", ss->send_count, ss->send_count-ss->recv_count, ss->dropped, loss, (ss->sbytes/1024.0/1024.0*8), (ss->rbytes/1024.0/1024.0*8), ss->active_sessions, avg_s, avg_r); ss->spkt = ss->rpkt = 0; ss->sbytes = ss->rbytes = 0; alarm(1); } __u16 checksum(unsigned char *addr, int count) { register long sum = 0; for (; count > 1; count -= 2) { sum += ntohs(*(u16 *)addr); addr += 2; } if (count > 0) sum += *(unsigned char *)addr; // take only 16 bits out of the 32 bit sum and add up the carries if (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); // one's complement the result sum = ~sum; return ((u16) sum); } // Control Stuff {{{ void control16(controlt * c, u16 avp, u16 val, u8 m) { u16 l = (m ? 0x8008 : 0x0008); *(u16 *) (c->buf + c->length + 0) = htons(l); *(u16 *) (c->buf + c->length + 2) = htons(0); *(u16 *) (c->buf + c->length + 4) = htons(avp); *(u16 *) (c->buf + c->length + 6) = htons(val); c->length += 8; } // add an AVP (32 bit) void control32(controlt * c, u16 avp, u32 val, u8 m) { u16 l = (m ? 0x800A : 0x000A); *(u16 *) (c->buf + c->length + 0) = htons(l); *(u16 *) (c->buf + c->length + 2) = htons(0); *(u16 *) (c->buf + c->length + 4) = htons(avp); *(u32 *) (c->buf + c->length + 6) = htonl(val); c->length += 10; } // add an AVP (32 bit) void controls(controlt * c, u16 avp, char *val, u8 m) { u16 l = ((m ? 0x8000 : 0) + strlen(val) + 6); *(u16 *) (c->buf + c->length + 0) = htons(l); *(u16 *) (c->buf + c->length + 2) = htons(0); *(u16 *) (c->buf + c->length + 4) = htons(avp); memcpy(c->buf + c->length + 6, val, strlen(val)); c->length += 6 + strlen(val); } // new control connection controlt *controlnew(u16 mtype) { controlt *c; c = calloc(sizeof(controlt), 1); c->length = 12; control16(c, 0, mtype, 1); return c; } void controlnull(short t) { controlt *c; c = calloc(sizeof(controlt), 1); c->length = 12; controlsend(c, t, 0); controlfree(c); ns--; } // add a control message to a tunnel, and send if within window void controlsend(controlt * c, short t, short s) { *(u16 *) (c->buf + 0) = htons(0xC802); // flags/ver *(u16 *) (c->buf + 2) = htons(c->length); // length *(u16 *) (c->buf + 4) = htons(t); // tunnel *(u16 *) (c->buf + 6) = htons(s); // session *(u16 *) (c->buf + 8) = htons(ns++); // sequence *(u16 *) (c->buf + 10) = htons(nr); // sequence // printf("Sending "); // cm_free(parsecontrol(c->buf, c->length)); sendto(udpfd, c->buf, c->length, 0, (struct sockaddr *)&gatewayaddr, sizeof(gatewayaddr)); } void controlfree(controlt *c) { if (!c) return; free(c); } control_message *parsecontrol(char *buf, int length) { char *p = buf; control_message *c; c = calloc(sizeof(control_message), 1); c->buf = buf; c->length = length; c->tunnel = ntohs(*(u16 *)(buf + 4)); c->session = ntohs(*(u16 *)(buf + 6)); c->ns = ntohs(*(u16 *)(buf + 8)); c->nr = nr = ntohs(*(u16 *)(buf + 10)); p += 12; while ((p - buf) < length) { avp *a = calloc(sizeof(avp), 1); a->length = ntohs(*(short *)(p)) & 0x3FF; a->type = ntohs(*(short *)(p + 4)); memcpy(a->value, p + 6, a->length - 6); if (a->type == 0) c->mtype = ntohs(*(short *)a->value); p += a->length; if (c->last) c->last->next = a; else c->first = a; c->last = a; } if (c->first) dump_control_message(c); return c; } void dump_control_message(control_message *c) { avp *a; printf("Control Message (type=%u s=%u t=%d ns=%d nr=%d)\n", c->mtype, c->session, c->tunnel, c->ns, c->nr); for (a = c->first; a; a = a->next) { printf(" avp: %s, len: %d", attributes[a->type], a->length - 6); switch (a->type) { // Short case 6 : case 9 : case 10 : case 39 : case 14 : printf(", value: %u\n", ntohs(*(short *)a->value)); break; // Integer case 16 : case 17 : case 24 : case 25 : case 38 : case 15 : printf(", value: %u\n", ntohl(*(u32 *)a->value)); break; // String case 7 : case 21 : case 22 : case 23 : case 37 : case 8 : printf(", value: \"%s\"\n", a->value); break; case 2 : printf(", value: %d.%d\n", *(char *)a->value, *(char *)a->value + 1); break; case 0 : printf(", value: %s\n", mtypes[ntohs(*(short *)a->value)]); break; case 19 : case 3 : printf(", value: (%d) %s %s\n", ntohl(*(u32 *)a->value), (ntohl(*(u32 *)a->value) & 0x01) ? "synchronous" : "", (ntohl(*(u32 *)a->value) & 0x02) ? "asynchronous" : ""); break; case 18 : case 4 : printf(", value: (%d) %s %s\n", ntohl(*(u32 *)a->value), (ntohl(*(u32 *)a->value) & 0x01) ? "digital" : "", (ntohl(*(u32 *)a->value) & 0x02) ? "analog" : ""); break; default : printf("\n"); break; } } printf("\n"); } u16 avp_get_16(control_message *c, int id) { avp *a; for (a = c->first; a; a = a->next) if (a->type == id) return ntohs(*(short *)a->value); return 0; } u32 avp_get_32(control_message *c, int id) { avp *a; for (a = c->first; a; a = a->next) if (a->type == id) return ntohl(*(u32 *)a->value); return 0; } char *avp_get_s(control_message *c, int id) { avp *a; for (a = c->first; a; a = a->next) if (a->type == id) return (char *)a->value; return 0; } void cm_free(control_message *m) { avp *a, *n; for (a = m->first; a; ) { n = a->next; free(a); a = n; } free(m); } // }}} void reader_thread()/*{{{*/ { unsigned char *packet; unsigned int seq = 0; printf("Starting reader thread\n"); packet = malloc(4096); while (!ss->quitit) { struct sockaddr_in addr; socklen_t alen = sizeof(addr); control_message *m; int l; int s; int pfc = 0; // memset(packet, 0, 4096); if ((l = recvfrom(udpfd, packet, 4096, 0, (void *) &addr, &alen)) < 0) break; ss->rbytes += l; if (!do_init) { ss->recv_count++; ss->rpkt++; continue; } if (l < 12) { printf("Short packet received: %d bytes\n", l); } s = ntohs(*(u16 *)(packet + 4)); if (!s) { printf("Invalid session ID\n"); continue; } if (packet[0] == 0xc8) { // Control Packet printf("Reader Received "); m = parsecontrol((char *) packet, l); printf("\n"); s = m->session; switch (m->mtype) { case 4 : printf("StopCCN\n"); printf("Killing tunnel %d\n", avp_get_16(m, 9)); ss->quitit++; break; case 6 : printf("HELLO, sending ZLB ACK\n"); controlnull(t); break; case 11 : { controlt *c; printf("Received ICRP. Responding with CONFREQ\n"); ss->sessions[s].remote_session = avp_get_16(m, 14); ss->sessions[s].open = 1; ss->sessions[s].ppp_state = 1; c = controlnew(12); // ICCN controlsend(c, t, ss->sessions[s].remote_session); controlfree(c); c = ppp_lcp(s, CONFREQ, 0); ppp_lcp_add_option(c, 1, 2, htons(1500)); // MRU = 1400 ppp_lcp_add_option(c, 3, 2, htons(0xC023)); // Authentication Protocol - PAP ppp_send(c); controlfree(c); break; } case 14 : { int s; printf("CDN\n"); s = avp_get_16(m, 14); printf("Killing session %d\n", s); ss->sessions[s].open = 0; ss->sessions[s].ppp_state = 0; ss->active_sessions--; controlnull(t); break; } } if (m->mtype == 4) { printf("StopCCN Received.. Dieing\n"); ss->quitit++; break; } cm_free(m); } else { // Data Packet unsigned short protocol = ntohs(*(u16 *)(packet + 6)); if (protocol == 0xff03) { pfc = 2; packet += 2; protocol = ntohs(*(u16 *)(packet + 6)); } if (protocol != PPPIP) { printf("Received "); dump_ppp_packet((char *) (packet + 6), l - 6); } if (protocol == PPPLCP) { controlt *r; unsigned char ppp_id = *(char *)(packet + 9); switch (*(char *)(packet + 8)) { case CONFREQ : r = ppp_lcp(s, CONFACK, ppp_id); ppp_send(r); break; case CONFACK : r = ppp_pap(s, CONFREQ, 0, session_usernames[s-1], base_password); ppp_send(r); break; case TERMREQ : r = ppp_lcp(s, TERMACK, ppp_id); ppp_send(r); break; case ECHOREQ : r = ppp_lcp(s, ECHOREP, ppp_id); ppp_add_32(r, 0); ppp_send(r); break; } } else if (protocol == PPPIPCP) { controlt *r; int taddr = 0; u32 address = *(u32 *)(packet + 14); switch (*(char *)(packet + 8)) { case CONFREQ : r = ppp_ipcp(s, CONFREQ, time(NULL) % 255); ppp_lcp_add_option(r, 3, 4, htonl(taddr)); // Request 0.0.0.0 ppp_send(r); controlfree(r); r = ppp_ipcp(s, CONFACK, time(NULL) % 255); ppp_lcp_add_option(r, 3, 4, address); // ACK gateway IP ppp_send(r); controlfree(r); break; case CONFNAK : // Request whatever address we are given - it's ours r = ppp_ipcp(s, CONFREQ, time(NULL) % 255); ppp_lcp_add_option(r, 3, 4, address); ppp_send(r); controlfree(r); printf("Session %d: %s\n", s, inet_toa(address)); ss->sessions[s].ppp_state = 2; ss->sessions[s].addr = address; ss->active_sessions++; break; case CONFACK : printf("Conf-Ack Received\n"); break; case TERMREQ : printf("Term-Req Received\n"); break; case ECHOREQ : printf("Echo-Req Received\n"); break; case ECHOREP : printf("Echo-Rep Received\n"); break; } } else if (protocol == PPPPAP) { if (*(u16 *)(packet + 8) == 3) { controlt *c; printf("Closing Connection\n"); c = controlnew(14); // CDN control16(c, 14, ss->sessions[s].remote_session, 0); // Assigned Session ID controlsend(c, t, 0); controlfree(c); ss->sessions[s].open = 0; } } else if (protocol == PPPIP) { struct iphdr *iph = (struct iphdr *)(packet + 8); char * data = (char*) (packet + 8 + sizeof(struct iphdr) + sizeof(struct udphdr)); if (!ss->sessions[s].open) { printf("Packet for closed session %d\n", s); continue; } if (iph->protocol == 17) { unsigned int iseq; ss->recv_count++; ss->rpkt++; iseq = *((unsigned int *) data); if (seq != iseq) ss->dropped += (iseq - seq) ; seq = iseq + 1; // Next sequence number to expect. } } } packet -= pfc; } free(packet); printf("Closing reader thread\n"); }/*}}}*/ void skip_zlb() /*{{{*/ { struct sockaddr_in addr; socklen_t alen = sizeof(addr); char buf[1024]; int l; l = recvfrom(udpfd, buf, 1024, MSG_PEEK, (void *) &addr, &alen); if (l < 0) { printf("recvfrom: %s\n", strerror(errno)); return; } if (l <= 12) { printf("Skipping ZLB (l=%d)\n", l); recvfrom(udpfd, buf, 1024, 0, (void *) &addr, &alen); } } /*}}}*/ // PPP Stuff {{{ controlt *ppp_new(u16 session, int protocol) { controlt *c = calloc(sizeof(controlt), 1); *(u16 *)(c->buf + 4) = htons(ss->sessions[session].remote_session); // Tunnel *(u16 *)(c->buf + 6) = htons(protocol); c->length += 8; return c; } void ppp_free(controlt *c) { free(c); } controlt *ppp_lcp(u16 s, unsigned char type, char identifier) { controlt *c; if (!identifier) identifier = ss->sessions[s].ppp_identifier++; c = ppp_new(s, PPPLCP); *(char *)(c->buf + c->length + 0) = type; *(char *)(c->buf + c->length + 1) = identifier; *(u16 *)(c->buf + c->length + 2) = ntohs(4); c->length += 4; return c; } controlt *ppp_ipcp(u16 s, unsigned char type, char identifier) { controlt *c; if (!identifier) identifier = ss->sessions[s].ppp_identifier++; c = ppp_new(s, PPPIPCP); *(char *)(c->buf + c->length + 0) = type; *(char *)(c->buf + c->length + 1) = identifier; *(u16 *)(c->buf + c->length + 2) = ntohs(4); c->length += 4; return c; } controlt *ppp_pap(u16 s, unsigned char type, char identifier, char *username, char *password) { controlt *c; if (!identifier) identifier = ss->sessions[s].ppp_identifier++; c = ppp_new(s, PPPPAP); *(char *)(c->buf + c->length + 0) = type; *(char *)(c->buf + c->length + 1) = identifier; *(u16 *)(c->buf + c->length + 2) = ntohs(4); c->length += 4; *(char *)(c->buf + c->length) = strlen(username) + strlen(suffix); memcpy((c->buf + c->length + 1), username, strlen(username)); memcpy((c->buf + c->length + 1 + strlen(username)), suffix, strlen(suffix)); c->length += strlen(username) + 1 + strlen(suffix); *(char *)(c->buf + c->length) = strlen(password); memcpy((c->buf + c->length + 1), password, strlen(password)); c->length += strlen(password) + 1; return c; } void ppp_send(controlt *c) { *(u16 *)(c->buf + 0) = htons(0x0002); // flags/ver *(u16 *)(c->buf + 2) = htons(t); // tunnel *(u16 *)(c->buf + 10) = ntohs(c->length - 8); if (sendto(udpfd, c->buf, c->length, 0, (struct sockaddr *)&gatewayaddr, sizeof(gatewayaddr)) < 0) perror("sendto"); if (htons(*(u16 *)(c->buf + 6)) != PPPIP) { printf("PPP Sending "); dump_ppp_packet(c->buf + 6, c->length - 6); } } void ppp_add_16(controlt *c, u16 val) { *(u16 *) (c->buf + c->length) = htons(val); c->length += 2; } void ppp_add_32(controlt *c, u32 val) { *(u32 *) (c->buf + c->length) = htons(val); c->length += 4; } void ppp_add_s(controlt *c, char *val) { memcpy(c->buf + c->length, val, strlen(val)); c->length += strlen(val); } void ppp_lcp_add_option(controlt *c, unsigned char option, unsigned char length, int data) { *(char *)(c->buf + c->length + 0) = option; *(char *)(c->buf + c->length + 1) = length + 2; memcpy(c->buf + c->length + 2, &data, length); c->length += 2 + length; } void dump_ppp_packet(char *packet, int l) { char *p = packet; int protocol ; if (*(unsigned char *)p == 0xff) p += 2; protocol = ntohs(*(u16 *)(p)); printf("PPP Packet\n"); switch (protocol) { case PPPCCP : printf(" Protocol: PPPCCP\n"); break; } if (protocol == PPPLCP) { printf(" Protocol: PPPLCP\n"); printf(" LCP Code: %s\n", lcp_codes[*(u8 *)(p + 2)]); } else if (protocol == PPPPAP) { printf(" Protocol: PPPPAP\n"); if (*(char *)(p + 2) == 2) { printf(" Authentication accepted\n"); } else if (*(char *)(p + 2) == 3) { printf(" Authentication denied\n"); } } else if (protocol == PPPIPCP) { printf(" Protocol: PPPIPCP\n"); printf(" IPCP Code: %s\n", lcp_codes[*(u8 *)(p + 2)]); printf(" Address: %s\n", inet_toa(*(u32 *)(p + 8))); } else if (protocol == PPPIP) { struct iphdr *iph; struct protoent *pr; iph = (struct iphdr *)(p + 2); printf(" Protocol: PPPIP\n"); printf(" Length: %d\n", l); printf(" IP Version: %d\n", iph->version); if (iph->version != 4) return; pr = getprotobynumber(iph->protocol); printf(" IP Header Length: %d\n", iph->ihl); printf(" IP TTL: %d\n", iph->ttl); printf(" IP Protocol: %s (%d)\n", (pr ? pr->p_name : "unknown"), iph->protocol); printf(" IP Checksum: %x\n", ntohs(iph->check)); } else { printf(" Protocol: unknown 0x%x\n", protocol); } printf("\n"); } char *inet_toa(unsigned long addr) { struct in_addr in; memcpy(&in, &addr, sizeof(unsigned long)); return inet_ntoa(in); } // }}} l2tpns-2.3.3/test/ping-sweep000066400000000000000000000052311400724550600157250ustar00rootroot00000000000000#! /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; l2tpns-2.3.3/test/radius.c000066400000000000000000000333071400724550600153640ustar00rootroot00000000000000/* RADIUS authentication load test */ #define _SVID_SOURCE #define _POSIX_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); } l2tpns-2.3.3/throttlectl.c000066400000000000000000000055011400724550600154610ustar00rootroot00000000000000#include #include #include "dhcp6.h" #include "l2tpns.h" #include "plugin.h" #include "control.h" /* throttle control */ 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; } l2tpns-2.3.3/util.c000066400000000000000000000102771400724550600140740ustar00rootroot00000000000000/* Misc util functions */ #include #include #include #include #include #include #include #include #include "dhcp6.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)); } char *fmtMacAddr(uint8_t *pMacAddr) { static char strMAC[2*ETH_ALEN]; sprintf(strMAC, "%02X:%02X:%02X:%02X:%02X:%02X", pMacAddr[0], pMacAddr[1], pMacAddr[2], pMacAddr[3], pMacAddr[4], pMacAddr[5]); return strMAC; } 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, controlfd, daefd, snoopfd, ifrfd, ifr6fd, rand_fd; extern int pppoediscfd, pppoesessfd; extern int *radfds; extern int udpfd[MAX_UDPFD + 1]; pid_t fork_and_close() { pid_t pid = fork(); int i; if (pid) return pid; forked++; if (config->scheduler_fifo) { struct sched_param params = {0}; params.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, ¶ms)) { LOG(0, 0, 0, "Error setting scheduler to OTHER after fork: %s\n", strerror(errno)); LOG(0, 0, 0, "This is probably really really bad.\n"); } } signal(SIGPIPE, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGUSR1, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGKILL, SIG_DFL); signal(SIGTERM, SIG_DFL); // Close sockets if (clifd != -1) close(clifd); if (cluster_sockfd != -1) close(cluster_sockfd); if (tunfd != -1) close(tunfd); for (i = 0; i < config->nbudpfd; i++) { if (udpfd[i] != -1) close(udpfd[i]); } if (pppoediscfd != -1) close(pppoediscfd); if (pppoediscfd != -1) close(pppoediscfd); if (controlfd != -1) close(controlfd); if (daefd != -1) close(daefd); if (snoopfd != -1) close(snoopfd); 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); } l2tpns-2.3.3/util.h000066400000000000000000000007431400724550600140760ustar00rootroot00000000000000#ifndef __UTIL_H__ #define __UTIL_H__ char *fmtaddr(in_addr_t addr, int n); char *fmtMacAddr(uint8_t *pMacAddr); 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__ */

˪Q2S#ew[oػ{vyv>7=ӎmn~1Ǯ^=6o{lxmOxIVj67|ֳGG[mމ.dλL¡dI3NBǿɩ|;މyQ1N;rI/ZY]p;wqm=X}wEꒃaw˘լK-k}w}|{ES@/%}o}q'xn䠧=m=o0 >ˊy ?s=EIyD"GI.uvwv߳֋;5^{E߳/MFgg6-@:XV[=ejtВkw=W:^'/vw~S<ꨵSSVz>?m+a߅<yC2Uan3l;λrq?o޹^ap𡇉s!xA,\DQQe1-e<-[ܩX/H sT. 7RtH! %SLd=l$H#&/uR:t l+}Nj+IgSEU9TQj1c(%ӲP<lLTI!0#=l8%ec\O!RJe3w CwX39odu[yh1ٜʙF ȏ1R6m'AڤRJV˔',)sev'LWMV!Э|y|397==ݞeU16œu}M7mV=җs܎;4$9%_zǭ0 a<K(˲Q]XD!S`fQu)aZ 2C͉4 vͳeFZ-0  7I+&G+RU0FaqT z{fÌh4J"b VhaqGa?ojjK^y|ނ:F zj5UUo|||͚5fzDB=u:3Dh4e>`2YyV9jbSU3d#0li/R&QQ1UӽX CQaXtucE?+l?~eÁQEU *b?<8R(N4wkW"@S#zET M DàNd"jzx`ȢE6*s̫O}IF $B H]{6$Y7#ull*oaDPslH:N=*Cm0R eׅ`6Du7lReY+aXE B]Dļ&{td;2̜"y67R~ݩMI! Cj6/YH9pJI@s&),ʲ;x{کX{jjjrtѲEZAU'{ιfߔei8%kMSm62ubk+%o*y)Fap[j2Q@:% UZ=9[3 Ra#c3^ VDUPZB_j@@s[q@>!:Suc%FTPT+BY!0$b60P)6ab*( _QEsXA9ՐRY̛h{r! ("\U 8\h,|Bb|ñ{ϑsE#@]U)Ke}+,FUY9TGh~VbJ!(lSΒ 0 ,dYf)D 7Y$({罪ƪ&Dɓ2~"RU516MD !˪!BJ\} ЇRdN9s[ًu{B ޛ+ϲ,aP1Ř@f*F^'1G[=9=ݭ=z)tD|Ơa@@ЦJb\DPVTGeEXeaRP`ET@ H`F GJ8R"€ H@ wZcۢ*xܹ LDVV(<-"VcqX{} QAr'hTISuFT}# []PXWII 5A bT`ia(q4EEDxx""*8^[CFtpj^vN. J>]F@]*7wMj eY[?:* 9@aRVm3&uA@lX(4` 5co1bCQQ, . HN{˽wv%{~=;}H:QQ5WdRJ)/NB$M`U99gj1 $0Ċalc9gP ARF*QI@mi׊8ife7Ps;&d&:#!䨄jNZ`be)01hbI*kX@ dF#24@Ʀ#@vHhICl$md /[QڐtDuw95p.!1HÈlaQvzp.m[Rۮh 1AAP,9O][[L 544RʂRK tc$"Pk@s9DQs>gΜ+ʈHkm{:2D' 4[-(2 56le˖IQ!+9Ҩ҆3*DEO:NA@D|K죏>zw6iEl_|Ў"Jq\,-rm hJI99?L#%Cp p nO}.L@H$Ѻx&2:!41#CMDqB-GȀ'Rؗ`sDڶIpTVtD! RMDhN 52"Hqd 9đ555aT쳅;wkպe&]溮j/MziE]d8c qI'o۷Sǎ;vxgWXѥK,dƌ͜kC Z{'x\rЃҳOϙIax/̝=m+/4W_&ΧA߷swOS_1_X.TuZfjׯ\:w~sڿޘټYٺrUvOi_;g4AA_vfrщzV ,`o4::LBD$8K.ꪫ3Iv|u"?w_O=To_ ׯ+Mq=S3fծxg?8c++!W⊗^|s׮^D+?wQk~ri1kg#/=E?\~?<⋗\rIPPV}!r뭷޽O_jjh²^׬Ys*6o|)ܹsٲeg}v6LDj5Gs4md*3`4V7]F΃7q!!"hNFvD/Mi$2<"R-`m!ʡ,\2|jlC{W(䳵[Q9b˾Xmݮ};j_WwS}5;++;v'ɶm_5ݫGRlH I%;ui#Uld3\p\&ț'J %g)-vh{ r$`eOr&_) IDAT{TN;mͷryc  .ܳnƌ7rO??N:L&s啿 27xôiu1g2_zW|޻[vjys6ob%q̤?W_}O}u]t:M6~wm۶wpp'G1W^ٳR;ཙ={ܸ~݇~(]w0u&.wqgݻRo:e_;wR#4OW`„ ^ Ѝ7HDX!?)3afOYfo߾wߵpǪNarHƜ} gxĵۦL~[nZw?|{6{m** ܊+=}tPJ=uA'ho\.g1(7oTmч* ߼ګ۶Rr͛7{}$ǏRjUvQ#ih!R K[V30 81  Đlgҥ8GAD$$,?XRׯ5D8)R%ŭdҩu7~ѭ 'H _\6?{7Me@M6/W.a',劅L*Cqn⚞]@GeX ( q]F F0(W:(98+tM6|A߾} 6l؂ |!C?~L̳2dȈ#{'2d<Bjʢڴicş8xhu]v{ERjN2T_zC xSqqŢ`|gynZkݳ']Vf[(ڱmļ9sw R:c-K.4hH)3)bh!f#ʊB4kpB("qf-}sDyДi;VC`/ & M\\13:,ZdI\6ڵTز}t[W/ڸnmUVv&}G qk$I%tC %Tnׯ߻$ݻNo_^2jصˡ(w]eVݸ]VLG"G\s(UaL)Nha)ut߮8p$ 644Ls\DBTTTX&J TB;?6lFDZUT ~K.X瞟:g}'^K/K/ Tʨ$JG'Js8¶$^xa)SNBya[aT*uW^:ct:h+uWY^}nA6{F4pdX@q(4Z46mbbJ TY>2#@]vR8\C64xi 6tyyӧϋ/DZz*N z\$ܖ;]Oql]/^`%KFϞ0`P>Oe, f95~8/AFhYR1^w#Edhb8bpQC9#ӲEeyYRʣIX\Mܳg)fݵ.ӌ1?QĹ)* 9߷sy1TV ^ʕB0l81ap^l S^pb;ty?:%le:fF=(Cп3ckVw7%"cLd.U}V`,r/<{+++cqwT߾3f̰h{QN5kVeednذV^}_s5SN7o^˗/ׯ_?iҤվ[`J)hCx;Yv zOp$ Kѻw> lٲ}.Y^r9`Twl6[(v @B~CL: A֯_QG^tEo3cެYVڶ'0trJ5K"[Yc}tZJy0Ƭ 7բ>#I |/Bb`4??RqZxicYx)`a藗VWW["Ι3N~{m2.]tرCmݺu]]5)ljX1xذ~ave˖UUU=Kkjj,M@ߓhK$ ,TkC+{jpPI~CYI `L\ ¿ޞ1yTitO)'|ĉG})TVV6,/ /ZO2eʕ&L3fGfr!3g<3Ə?bĈO<#\jȑ#?ûvjժ/8]8,;K/cYީ-d2\CVq7_sݵ.)//3gΩ#~ҵGw)e:]wrPtX%{}O?Oo;sͷifܹIycƌ7W^@9EiٳF6b={}Ÿ_'7ny8pp·~\еˡK.)]׵dKŴuPɩ=p7tS6{ !R~*A}za\w{ι,99k̸k~ڴj=w(ug'N9r䩧Z]]]^^ޯ_?ˢ(0`)"a_|RjÆ ,ˉuݣꪫz}uqcǎ]sug͚uj]p9ch}S*;*͖r96Gs4g:+]`c82۳ 3/JhƵ7I{0+@$0B$bd$-¹Wޓrm;wl&ū}WGGD[7o,FIDȠ,7}9_ѦN oXY^wBi_9"O;kwulwtNTZQ1#mR-QJkmA@`!(ľ6') Ew$ <?AG9{to׾C*n:uܹi/gvzO~CN#O?}׮]Lo?[ /ٞ={ y^'*QW uYges9`lɧ\s͵3`̵Ɯ>|quҵKu$>a;vyԨl>˞=ܟDH7rp@! kݦ_}6&y7f袟]vK0oQ: OHΔe|=o٦y睷swxs;TY$rS271uԩG9OVmء##Gf+c?>"ڵo߽G(ٳgU1cۿO8 w2<|V QRQwuDHQUչcNZ jkIG^p\6mޜ[ou^wup9cr;G~oo0־Cnݻ?c+*$9SڵpnR{ƙgCکcgC3ޱkk1ٱc6x1j' t}vhꮻҥKIUpbKߓhio{ZsQvشmHOΥqBl&HK. 6GPZpkBK/+̘*+o᧔M9 a6.goͯ{{]̵^keAFDD"Zq )=%1M&J#H(+lePUt&*qtq&q? S#WH$%FK)t86uք $g xk $QY,w5m ۷1t]r + g:1\2BDrǢ4 c C Mh6 BHƛY0gM Z3-Swbhe #p"Pr'V#\&5'$/b NI󒞷 GXftEs4O06FC/ØԎ0NqĤDBI\5&pldInD )CO2/p;/,_#mؔ 8գ @DM&i嘐bcDxA* C\i#'0pB 4R1LkM\Wb$Z1l5q8RJ.$ʼn)p*R6U2$뾡h\׵iݗ8 bGq9.Y3$`Lh[f"I{g N?ZpD|~6T*QQ8\*ȐRpF`gcB M('W,f2y /ZZiN SAmhCU)A=sgn*khRR}@@78bABBByߪXz7"T1/]BJA 0I8AP+4C!K2Ͷl:W,lBHKMȹ`SJ)[N$`J;Z戔2-atmx_S[ʥ1%r9c )P1X!)Se|X,zc}7lDZ-X͉&s&[F[}R- dn䃈Rq{ґKDI9&ngnpk*UWdY'I"$}Py'HJצXԴۢjrl&) IDAƞg^rAt`{#dDC Xuu3MGV-vرU+"l]VֽS;v?!"JM rWv-nx \ `y޴iؽ{v[׿nٲe*r]ک7,TJ12JFMq,X|c>T́!IGDadҩQdP;Rr8ca"Cy^>&0,-$IBt:^Yx j ?GcDāA%Dm0DmW^^neKv$%xiڟ- F`4isIIߥ Ai^np(Qq)e1s`hLq4 T,`<:b8La;" 01imtU&!#:<ΌёQ!SN90`\@(Ѹ IDAT#ܔ*c2$gL Cbdh@@_DMd ! xZ]D0fgT*$$ l[JIQy :4щI}kojcN Ea>=u\CVr)߫,/sâ`|Gpϑ*2*)KPl#5dQ!A␑9Άm Em.Fgl쳚\3.<2˺R\kJ}}sT?_&4iݗk̛[_}__7qXDD,N ߐ;oGsA%c:!YF%q9:) yW ҚsBdy9֋k@(!'tYOGr9-@Frhղ}'q(ƛ~tTﺅ\7J,. :I(f` }4Gs4N|_xb%`&'$` #[ 2IMk$܆Ra`w;`DL%O !M 7\Rp2ʓt]WI2iCԈ2,+VtJv?l0u>l[?gΜ-[t .'W^K.)++s]%B:6mںu߷?ᰵD}.ch!-Zx'zhoM:w<|v:H)7mos<#իgϞE'|G6 ͙3gԨQ=z`va?$yիW/Zhذa0oQN/_pBtI ZI&IYgiuH"gz;'3N| P._os֘Ï< R=˗weԨQԣ>zO2SÇߺyK_nY̳jբ%>q{:}$=C  ~-UR9n>_rμ|!֬Z遼!ڶh裏dqLs4Gsv7 ԆsU,8Td2$P DGҒ)hs`;:G2TQ+32IJP+2Z00*!ԀFX0@"Q@81@PkK` 8 q8#@#4F'Ԓg@IƁq$J c JڀѤ{0M)`@U:V,(xi?gp=袋v9{%[_2v ~wg@&9˒x?c$ڔW `7o1g'>SaX}v_(/9O'Gcg"zWϟ$믿~UW٩8?묳jkk7mtg%Qc̯~ӧ]w"wBNݗ+N61Zg_\CvMNk՜s7mV[;jHBmϞ=g̘tҦ= زeˏ~ÇO>b$]׍~R/о7FHCqcv|rNb susk?u8_=77/>d.8ܵWi m u]㏡Nv?F]xюm`ٲe>jiSO>ˣ6^xqޜW^+2J;BҾ1Xr_^ysvs.Y<ٸ~Mz\qŢEn<`h?lP#28YM4 p?_rǞ S!шdh寠$(y55vMN?%ݴś^z p"2_wJSr'gG~{/Ib`)h!+ق7 y10ĉ! ͐9c܍sN>̤8`#Q">[׍6kִhfΝ۾s…z-}Q$#ڶݼsB\0_ 7so9Y&N0᭷JIlܸq޽o=9>|ŋԦͶ={[Xccv[ogUr 8e_z'tR[qCMMͿ\)UWWwɧ~d"С25:mqvq֮]{=߲k1R)ť;v?QUUo{y3F +V8r\*e%;x[nݿ.o߾)SYc$}]tp8RnXot9GÏ(~{x"JǡruyyrUVM2֞~K.}#_XW^} $Yp]w-[lhhkwH:ٸQѣSZ [o=ꨣ.Rӈab㫯 }kkm' k'fRmֶmq]r%կ~GQ'Ɖi-ec?Yus4GsɀfFXg +SCvfEܞi$""P7v D}H<'"*QRZX!zvI5H hb{tC@V@"bvGJkE8l02ĈGFr9_(i_~| @IhѺCv$|R}IXQQ#"jrܒ y nTjܹC}DqG=C'c (ED|**-ZB!'O~}d`GqLDضm[ڥK={H)c|JcvL>۰z I7v[>߲e /0͛7S[߾M;l NׂԘU|~_O` keM%~A914H)5ipPјFI9B00DI  [%-HWp&Moa#kD縎`**em9#<գ[{EOq$Hn P:v}1J|G\NI/a?4iR];kEQye#Ǎ{ 鴥N0p=PRRV1 .C+;w>ʪ` 4}JxwWu]D,IRu[-++Sa FPm{&INaC5j'N|z-۶Y<"I4"RVZj`q̹ 2J+IRC=z4|ƌUUU >x'R %h=mڴ;/d]?UbT*-D~}}#u#}u]o[c%XqY8++ZE?y!DeeeV 58ʤUXrֲtN7ܧO'VUUٳ$B| PXԄIg?rGARJ%pƅ=_V8j4R4GsU48 1&08u*Kyŕ-̾ 6r<CMV H1$f!P6!0 02I9~Ȁ7Jֵ!Fd*1o )1̷@a# ņ?v%uѐ˂J+\xڭG9s-Fa՗8x#a 62pXT*yW=ہ %s:t%x^px2f^:8RQe*$R~A% 5zAjKO:iUW]\hIX7tY,u͛7c [,?wT?޶˔s A:T(Wc$7h, DSL(" JÙ~=5=g?ٳg̻Z|ᇄJ)a}\ 5HCвug[ vl۷뎝+]W$IӌXjX3=L[)@pm P17/{ #3lt;Y'l&TS:p`ߠ@0>\3z|X*M[>){޿[zw|'`%Tjj ];`G25lX͛%hk ,7211377ĂeBR`XJ`ݻt|QPuoΎN&E7JXb)[ZĂsRrĠ5VT )9X!Pi)3dؒ X*~B͌e\`ZbQmX %i@,I =iW\V۬jS=^q/O@zo+ZnTUMR$1c{e` V7m2qġbկ~5f̘#GXbӦM۷ѣ'Nk׮?=={$9iӦ͜9c/:1R7/Ν;{ݺu;9@HP6lر;vQFܹs…7nw!ͷ {?1[ ·P(q:k׮0yd'?ɹ;uTu,Xpy߼ꫯ~'Ov]裏hN}|mڴ9/|„ y^mm-lٲCg~7.]8Cˍ1:t0(e!k\?uc'|,ZK0F)SU豊3pt:=eʔO>yŊ#F6mb8zϧ~Ƿ8K|? 6+.kaŪ` jslߓڦ45s3(tu?ߜry{v~I ,4Fc4ƿI|BكX .X K B)YU$Hh ٣vI@ X`z;X('VE$&Z`5A1PdkMOc͌5$CXbAM)}te]kR a P(d3Yb QQv;Yy`#eķ|8/Q(bEZ< ΐRim~vM LM(=sqOot|SUΨz?y䚚Ç/qaqh&xƊ":*, (Zs8v@ IDATӓ8V1RJ8VT'F[J>>rq*.ι#8qW4JV4Fc|}+bi˨/_vgS)m' *9b%H ab؅T}XJN06@IXTLTҗQ"j@ԡ"G&~*NLs$*xsyctITrf i5Z(vdiV8-(jjj0’84j"f njUa~.λI Rے9KO0@e&Ge*9Lnls/Z#۳t(lFH0?)"yNBP]]_< RjpbJk9G1Me> ce48T 3Qe0B@8N%gL R}8&IҤI(v-9ARm8oRJaXSSc C1[f+BQk]]㘖{=:Nj_J/_! ϡ뺹\se(hh4R b+Ks8y[w}@xԖ2 aQc HckЄ1`-hjA%j E&E _Xa͈Ԯ\`JŎTϯc!pDR,%NXPJŴr+=ΔRĚ0svPaR]D&VQb u_p&lWFM⁎} qVlCK)d `dD -**%2N5xU좗_@N9u,QR0RqG0B5*LpNYC>W*L8eX`\NXy¼ ~*uqX#TMJl,a4 AB[cQF;( R5o!dĈQZDJkIL&JDZBcT@&Si}}C&R(#]qaTB@ǕAZg2(҅ZN0gTJebX@$8d-! -(ZGQOܹs}5JZ;@)ˤiM({@Qt?!U~폿c8*"! x}gZ6# qP+)Xb1F&sbV ƅ`Z)@#hbZQ`-5Lܨ7ʒ#-Bk(P_IK!$n#H(>ՔP!`%+DgRǜSZ*C tT{|)#AX9w\2MQ}sCLa2C *aq'ز-in-Qhd0@+j@c_+̌Ru6rG~Nz"ϭ˛ %nd`: *֩CԵoݪC- +boƘnE'E*SxTk)mQ)=k*Hxc S2M& X0DB{;cW\qyH[CNJ=b(= {q-ȯGp*cR෈68Uʤ"c1P wuWe J:!Qo ڵ'pB}}>yݻc\K^]3s ]t=/(fM;>;uĈ7-vmW\{֭ZYcVXgm~k۶=l&ݴI͟׬~'3L0'0XVܻgOm֌oݺvm=ϣݺk.s]__slٲefGV-}I ּW^~i:27lCӼlm֔4uGaf͞fJ&{wꩧr_] \ǝsw =G~kRB!ф4U4Fc|}XB%H<ԡTFEJj6ؾ!< awx[1DSXk6<E 5po~+/oKʒ^GT˻Lϒ#zS$ YAT{L+$Iy];=9/>?|_Yaw{):Yϝ;wذa+R)ճgn߿ׯ_q1cl޼yʕ#FXk.]*9ro / E!M:u[6|~dI o_ti>}>%K444 >|Vo)uuusٸqi6m4uԳ>;=3W\q1f͚5B#F\1ֿB1qyGWWWw><={{ .ԉsd~PJʕ+)}qwId瞥K>ɓ'널Sh߾};vxg/\b׮]1LAt:{G7nؾyss{뮺gLFzWZj!*!GmMD%L֭[ 1c/?pAxݻwϜ9KFKӈ[.]:p:,\pÆ ;v7o޳>OիW?zܹI^ybxE}[:pO?lٲT*RzEQCC5\k!{xߝw.^xѯ믿gϾÇqڵk"u]I_We ֚yJ8DiKTJ#6TEk Xɩ:T0cUtCCo~mPaee2H4%FJ >H$&"%=}5knꫯjM6au~ 8Pk}WSJ9ȑ#9֩SEYkر7|3W^zY)u7Ι1cF%q1cݳf?RXj˧[-ZD-\kƌ>}UO>uukoh۶m}CC^6<11U)LG+pow,b cD1!sxP(f2J):2Ƅp Hg `4PFa`h x E5) quRu@&p8_Df#@ fa a y_*Nn2b*r {֯_19Sg5y7l:_qcF֮[y{9t׭Y3d<ܹ &Q5mYnjժx'(ydLB܉nٲ%!}HZ)e:"|̛SO` Vo֮];|?c Hb#~4d2V Rj]__$Ā-B,1Hp ֭KRC TRڿ{/ %4i$I&0  r5CK("Ho# ,\9cFPs!X}B 2_aöo~ '3]u=5os=Zk?1iu놬 k-RR8Ո袒d,6kjjcǎٹ=6QsBXaq,}?] 2ԵtTHK맴Di& e,qeb4cqnZ?oC]| S^>XEƌ[Oo0`9߲e !W~-BI 6,߿ Rʆ$M 8^C$Jmcm#@ )H!o߾K.EF %kv}뭷'`My'|ǃ-(bVqWWWc~yByqCCcL)jժO|;o&^ѢEmmu-4hPXQAa8H5R4Fc|_[/]'_yǴkZo_ >!lRDi5kf p^\ʫG<w M ` ,JZc |m?JkO*0%&s/=qK,MHI8Ko_g #<ҫW?~ƍ۸q_t۶g'O~wc?_1cƌ?>ͮ_~֭g}63jjjpBuݺu\p秳U5 FÐaL4)zwlRש񼔔S|CuuuŌ3:kUUU|{{e,k:w՝t0hPڴ8yAy睺NNo՚5aF(IK TjZ:(aXE)|W썦M.^ȑ#r9ƘyK.[NW^=|pk祊a\UӴPY^^ /B>cN<:}̸6nK/mܸqs1eĈ)r5#<̰\~IN=ÇC4!GKs ƘV0'L?ӊar|0c Ӯ{rm'M:{kzyVB 8l6a- ׍)Ec4,|) Fl[G)0`3Ϡ 6zԨϜi~D맍q,LbJ'b J$VgXk#%cE[|k 7v̈~ [iJg<uޔ^[lY q$ZB-- 8a}w}B%SyX+[~ؔi9Lq,gϙcsQրT16]ٵEfjѢtܸqmڴپ}{Νg͞N6mzlٲunUVTjر7n2e[lYWW9o޼y]]p:묎;~z{(ݔ`РAIp%Ak=d :Բeٷߞf;u4x?p„ &MjѢEv ܭkmڴi3xM6]tEC iӦM[nݶm[ ֶnڴiJ86Xk5k:|m~zgO0]vmkYx'̜9ks0a6 IDAT=ԩS֭Ѧmvt*kN?۶jkk۴ic,@vjkk[lqYg߿3/(,qgcLm6۷o۶8[ҥ$IrijkkoqLYݢE Jٸqڴn8g=8ƍ:u*r۷ӧ϶mzU]]mf{nUUաCcN}B(9rdE fƘcٴsUW]x x}~r1ÿ~iӶ~b:\(>kձ<|=*ptlr;ZjcU1`7qwuPww]Q3J&I`Pw\~*/#"*H品`Jq?i(,lSJYSSUdAAHL@* B|bc_ŶTV8ƍQ[y.WJb?A)- T6} &0A mQ% UKcPi4'#nEcnc,Bzm9f޽Xj?BF 68b Lqr)-?/E!~d ?MB$:( c6%oA/ O~~eZe5Z(WZ\.WSSe44cKC0 lEsXsc4Fc|MKR|lZkѓPg%a.K)Pb)Ck ERY ֣rfQrF,`√;R2yRsO6q9@y@& RJ)'(e(b8Rb&J2BlL8xsL*[ʬ"RZ0\F ,crV,AѶ >,lF(WU5eٮtJ)KaqB)•R+erȹS, 8P (] Øhm0ʓ$aRi  (f~R (Ri`>=fTa"}>QKz|S7tG+&M1^?>|8NHa+)XZHRJ9Oaϝ;w߾}F%q{ڽUVXjucJDpY~ŏ<Џ~͚5zo}[sfWWW?_veXBX bQݯ _62T1-5A s7t֒(JcI'I9[B֚Zic"lP|spO&j&QytDž0 ֤iTWW:tH+[fZK(rq?jh.%7w"8Jp+\WںLM?khhN6iҥ[OJi"5Oe;ЪMk,s`[k:" Cu8J7^pM7oZN:iL9yac\ [ymwc 8` Pskt9I K{s7o$ICCCMMMCR0 bQ&1sB\,#OY}ߗR ΍VtM:!X.w8H@(rȘ-Y n*Z}QJqF9aj&|c}IJ(ڦHEι'8h΃0RM IL t:Xap!l?ܤA\x8JMWoh~R`H12~:#1J'xG}޺l2u< 8.>^)JCߒXΘ0$$ fM a4Ƹ R]*8FyMBH.CLTΝ ٳg !cҴiSJNDZAP,1aXkAUJ$RRk2(%0d28g MԬ\rٲI0Ƭ5 F|_8}Ow}ς;eDHҩL_xa֦P,b}ict]Q"(b a J)بX`}U 8\YMq2 2'81R[?fv= 9e68Tew溎#x`V2庞@\]jELG0՜FctZ˘#Jy'2HyN*7m)1RZ|(YhhȦS?яL< V˰.!VkĕnR]n(.qnh8dWD rlV[SB1E<7cSJ9a6i$QJ9a[ RJ赭F*&: sq\JJ)ԁGB_]zG>lYk9x7F3qG<'򿯸2R ]+Ç}Rw@H_'$It٧z$2|}g}=+0=tZ -ə{fzO>ޟkoS-,-?EG59Eߺw<;C~ء]rviǞ$¬ۻ'x;~ {ebŊ#>lٻ<g ^߻Fm1O'o)"1&-Pbv*88HHc/7' %P(Pd/"AID8β, C#:QҾ x5! |Ϛ5묯7QŽ[.j13}N:/c"Q_h96C\.}kfβ R/_~i.x{wa l9+.ѨeY{Eoȃ>8y|;7| g~..lQ}_sd+Vx'O|wZnz˗/vm"{E@"E }ذaf'>s뭷^jM{%66:R(EA!VZ)aDˆλ\.q0Iu-BA˘(AH#hdOx-81s>"͵&@G`Z*Yfp Aѣ7`7ݴg2"3feM7 pCSDqP0}3tA<@Qz!TJ >=+qeooo___0o wEkIz>s̞=;IcO8ŋxᄏ`ɒ,J""ڢ(B2Jp_BQJBEQ{w.<_GCnh.hM8C#^7|{7.<R$!G -ALnO<<3q( к/"Tx^uU'NY_8===( J%`vERm,2.ҧTOOOEQdy{mRE])l;[(MXr+;JI,EJuU$y}{hEFc! Q(J샘q^}<ϙ="T*ΡC, m|$;,X`Ĉ{i_=co~pޱ_~Eh]R\*/yg0&qBYZc AjD&! YZAf|yʴJGʕ+!@D'D J;yAjdvL_(z{BTzM6}ٹu (FQܺ() cy?]g6f-5UGfHQRd䩧~y}(-<)esQ 5Fm K)]׿w}o7X {D4}P EXR$TJB]~eYkl_==R}\.V}<ϙy7~CʤVA0UhIPJc  L0!bPj\boG{cnmOӃ:hڴiܲNGݪ 7=MSКPyi~#=\c{[ECa$h? (k0!V3>_>'SzsDQӟ_  T4s#8{ꫯΛ7CeJ̠ueaV{q;\.!(J& 7<_ӾmFMO_6Kk;c.~m ?>;rI5hE "aEAxW<3+vyff93m4 )OJʫNnWZ?rJ$:|RǍ?v8Rz?vq&xeOOvQGOMJfGg 0q;n;˔,cϙp[mvLIaeTͨGoFF9sINJcL_HX#5sF!H;޷6h |uɺ47GQ 1s}vFy:=\܅^Ox̱rJ 7bZYk-azUj;;;gzTJ4I$Iz{{C{q+DD5r`,*@H3MS"袋(:3YR*Ͳ$IÃL+3P"0ijsE~Gq m>!r' $z\[i"bR8A- 66:R4-1bGquټ\L ^9?eޝ#{7~6Ɗ}Ԭ+/oy9~CN8=,E $Վ2 q//T[Xۋys;rD Yf?'{r@YSO^+oR)`AhDqRH,/LYJ5!Ck+{Rٳ ),t|J4Q,γu^i¡?e֬$RJLY @HsXDuUX_X_tzhb$EJʕ:m4{м&L7Dqڡ,yaPû:ʼnҦf4vt+U[U IDATGQV& ʕZ=2qhV~kFm KJ:xO/aC3s=w?yy}XBAhy.adFHT620DBAَOJ0A $IRՌ1giR.yՑy{d0C󞞞PN2YRX;K&s&2|X* ƒ7ӟD5 E3swWG^(ղ.$-r̎kZ'(P3KTŵZ-uiZr(\ z?Iv?Ǫ_VmnC,:;;A$uY߹M6٤( -s;2e]>#("Zŋ2D)^xz Ղm^([Cb8!g1%B3(5x#FR/@Qeu,( R\Nki9)gYFh$)\NPRٲCB $-/VfZY AA`5*55Bʗ1b"ʳT+b84ׄ6T@.+IPi# N 󂀀‘PPBPi6Ҵۼ9 6u̴BgsDLbS"ckT<8.9ZG J DyAh0.yaD"rEPx  H&)- ,$Fa^ۻ%v? k kmZ!VcWKJk/m+m1<~w 7{;R{Ԩ?|Q /oHHK8cRרT YZ+HQ1Ur3 MW=z @@  BsBn  [D,}W ,lڟAAȯڙX½h4`߷쬀_Gp`T9N*JGJ)f#wT+ +( *BzpKeWՅe? kyho|坞uh0  \\6zC>}o.U+<(ό^'& A!uwWJƨ"JiRq=/h#* E Q*RA`abSr(lfbDPFݝ;ɦRbtlCddi-xBB y@@ &tykA-5N}=i?r6މ@R< N0$Sa $ªXX ۶ =7^? L:4c7/"GV0bAZ?yvMI-fΗG5\.kF+RaE"g 2M5˚Z 1u>n2=?5h,9"qT*Pao åR7'B]\pp]wuƌw}'AB(,:2ArDq9F_KNgK2T;;2gȍӢ6thgߙ(6*7jGӳ\`dfRqmfwIW&|єM\jI,> 4EHJ #;DdYqąW&.XK127{\ADnU+ !s H{lW @XVB0@C8ap3z&ӢlfED :LE4OEDYy rV/๗;vǶ̄JG MRd_/Sm?y" 4:the`J ӟ$Ɍ3&O ! B@ g+dX-SBSZK[Lx@FX+!*6u)HNE QՆt(rM3Gk#xSWT_Y~{H\4!*LE 2H@)ԙzʂ%돎Ũa2ճ4@ &j-f4o׈Qh!vj >w%whޚfYP ;Z /+ }gf&Y-[ ME Zu.uX[|XṊxhձC &lg" :IdYaPn2l E)ړCTWT Xh[NtwVl<rKgF ൡ<ύ trQ ώsUg/=Ѣ=܃>]+!4x(a % % 2|bC7Ғ*!qu&IF[od * @-.9(=.llhwa "yq0d+B388H{ YiDdq JD7LBv^ vymݓ&ND#-5'\Y0D0Rj^{C ) mFd]$N{mb5#bcfc K\A{9[x1Jy+%};HZ'zOjm-z"JT ="PJn~0#%R{b;<_$| ,ZGB S`rεTZ[_sYpP#@ꍢQ:JPys> ;dX=nXZD )( bہĶ@E^(YNDF5R9/F/5pYU$ԒC:F 5'Ju4g,H!Ζ$+r x06c5Ҥ\BPNy."A(=N|a$ "J` /"-5_p T5-4M͛VD¡RZ` Π>=K˭>4,45&(Jkß<<ϛyVp m6XBX1pX<S8 L:fqR INhޒ{-Z$G-R[ {UVi͈\wdtȱGw @A3 :@0AJX;b1t"@ةذ=(e/i-  @^ I ڿdA"T况NTB~Y d/z4|`;TX@*QH-.s:ncB G%<6*T*sεN!p%IdIC;^hR(0o}'7q…,rh"+N8!C6EFDF^jpicZ)vHFkACSo4X# d`]BBGQL{f f uD;=3t*c) m1^"MqZ#)ѲW 9`!PJHbf#Bƣ̚}dlwmk¸^_WXس}1&/0Kf)! 3 `}p@ *r(ed a:a/?&.Og{ohsYyhMkv\a-Q":*@ekh68lhY1,wyo(SO=5mrgȘЊ m3P, _)UK@콳J٢ ĕ+VD802轃k{[3kz543xUj` {Xl"sy.ޢxFZi+(0Z)AX؂8M5YҴ^+,LH(cZ5Ֆp^<PyQ}(yO-﯃Ď30 Q(5Qtιt?я&}ɥfy>dPp ;bf'6΢>&|{H uqk#M$2Dyq嗟{?z, 眳I;m 2sݶryFGiM?<>`{M7\ww/;?0$N;c_# '0uT":cC9wޙ8j+z(+bҥ=<3q\rɅ^"Jss=g}'p$β쬳Ϊӿ*"O?O~ &NzsR rA8odӍ{R IDATƩvYmRpdqW\y#>Yyw}ӧ:m.c[m i?8}iSviSw1L5`O ===!h4{ܹsoYxqrP#n3ؓ lZ8_^bF74*D" JG "@Ĩ!iZ)jO0s!k0z^{ ::Y~xMﴣR#y 'A"PHss IA@YzT |@1DI TIC LZz 3 85?8O~uL|QG?n[lm1^|qܹ?93W;ju[l-RYuFfK3l &#"Ƙ ۚE otzƕhPFD!˲`ȘP$Y3xow!$$ U .cN8$EʘZTǡoTJ!EB%Kv1cm}nj-"M r<ѣG[kj.\%1r,6d)Fe˖|Fz[DHue+ " A9-zO7?^\sMyVx)Fm @nkƯmsP1 3)7 < !`e62 z2J#pG_˵mvÇ7g_*'0m,-jVڹ;KsHۓ|JbE bX93S@,,!hy=+-ZztXL}Hƍso7Cm); BGiNo?ڧMkƌ7|q9KIaqZ2DEJPk@Oa7RH:{c hi@@ ڞ왂A$ L\zF͜\"g DxApIHYRYU*gLDILfZS5$J#6hN: &17uQiEG lᇌ\#??l05*j/Qu,MVQǼ:i&HWn0a.p̡Y}=.BD8kQiQhz)NF\`1cJqh᢮#HQ:wyҐ"aФF(QeW\>gΜQFivõ RR ?駟~gjSN9ʉe 녴"`^Ǐ6'?3V"V$"؃g$o+`}^#yT Ɖg n͝kڀI}x~ݚ?>rdh dFmwSsÜwc˝cL}r\_kQ yP^"^W "A"(i KC!Y.@D@K_җn%KE+/Mp1zWWWǓ'O~WBV"-rQds^Ġ^CfPqɁ @-{qV8n^SAbQvn[_)iFlyL]tMYNm7t0A쀋Hmo6y-9jtV8 \, K/E< 5 5 \. 9PVVO{z: "dd]GQDZ"qh4Qwv.\!"JOkק>y]wƘu1QuCI\Z_<;~ۻg[ocJ;7>wuR4wSmw#Gg2L DJRTZ|s~8viuG/ѵF?s嗭Yo]t!#s[(W+!?Qxȳ(R,^$˲ @PTOn"I48uyu\kZs.DVUfrZxN%lqSַ} .-_~+ިvcp%7)f__}*f(MW5o髋\UŘZR'ͼcAZECՆ`DA#YP" 6 F(¡Ç˱|0(M4|Ȱjg˖jgNJb eA_?_iZxMQBVȂ),BchځBVxhF-F [ɫ<ՊB "zzl?f[n'yZ9"ŋW_{٧*'IT Bb^ 0`x"| Q:+^wڬ_~\APQ8;^zu?7^|%ƘzJ(Kc9we]m4Ns@?j̿O&ugY昵6iy%I"(ߺ`ܸqӧO&|F0ai37?駟<ϫ79GÇ?.]q?σXQ>Dh[7pz=: .p=fNR1tt AeNʉJ;4c='rZ[P {7*D#sܪEj$v`6lƌ myF 6Hy<,#c B BB ZVVk̻s"qJ-$l[UޛY'noZv:Pv6s|?+Byf@z?=N93fּ_WŢ(yW\Sਃ7IX%'Fʉ@jQDIAUQEEu|ҟ~;Rk/\fS{v3743_ֽ%8:Ggo޹!-8DܢFje#|2")P;Kv]qU6 OFI+-)>ֿokO_/|3w?) w~sYRJ^hn!? 0RyvjQ@jBI=,S `EROJ|l;#s)w_ހBFLEK/yםR_zZˤѨכ%/͊Nm`ZeAjLAޠ?7{&7"<&6/K!0["pmtFUel)"SqYe]WEQ@`FW:=;`ȪjePU&k~KpZK|UD'F5|:&- EbU! 8Qc@lsyAmO;wEvoo脝u ]k?uݞfn2 XяjfĒjWJstv|Du9%,L* \tq8~}0 賞>X5uӉd_FI(ƕmԾT5jZd$&NjdDU"&aŧJWes#0 ëC 1L{}Hh,<t["4MN?cw55Ũ]^xˁLUUֹ,k]=#R8b{M#I| UQEG!}wng˼PD$Qq\WE^u+}pְĺmLqeEQ|hE,˦mtsd,!JVE@\7mfQɨ*w?ʲ?׿x{@ʋRD#k-;Zu,Nڷ8TEe`\&~CE""*,9DVE$-:ZQ֓5XY̆hvnnڵ H*08+`^@k66XulX#3 !LL,AA1$TQjDC[ᖍ*9zpi  K;ppLF1/ 3:E<: ^_mqv6ܾo 0'd"#D)]Rp)7,Ts9R"sx M^w|޽`C68Ng Vae#t("ID A1isz$2r# ?OabJD,'䣏فdnݻwebl1nC9ѸޏF#Q4m۶m"'Qcډw:w]#mRTHq\PESղ,"( EFMVDvsm8i"oIGY|3%(1m>$p8T8˲` y/--x=B#*d˺)% "@!e y&5!JbH$Ӆ @UAq#u1jMSX[r_b@4}/җl_׼5BD"H%& EV(j "" 02$5MU((d@"4o>ְ'PƾauE j3vy`_wv{o0zywPGmYSE:<L¦iIa49xcTs* iFbUU)t:Tyx?ѶeYG9uDV+> zȲlqqJUUEElFR3.2OUvu]=^8Rx( fZ@LN,C{2S׵V$~ *I= NTګrM%u],IB%4MeUU%GDEQux{W`'Ѭs@Dej S Bِј(V=nlIŴ3 q;ھ}YaA7G17)7`ȆA1'FO@"FU%MB (Hyy!"pMn;tyN,BРI"RQ"J+ ̲E8 Ӎ2clz$kk:s )LUqR@NnK#ȤoPETUgsaIF/iڌmn޼Βj 6;ӭ Gmky/ Ṳ:s\f\V(Ecud 9W׵#"&i5zwV\,d3iA`NnҶ֑Bj#Qh:"j5.BD+DZ S ff]HqildNJidbì**$%7M !m;*3A3c۶N^}d9DDڦ)iuq(eY*刈 GM1f/tGKɑ!@R|N.^@hꡊ$J`HTb9d:9~M2;e;6`ZR [qN̺el2o"Nz&iQ5Q-%AN m8dXE "/NTۤIRg {q%FR;AԵ!jڶ5*H(FQU`0^YpɎ|æ8`, ?ix<;l햣 QQu U [w, 1F wyV!4m,--Ml"'3Gc|ەBeXMq5h*XcXSGb u~*UM)3*1Y'.gR/=- 8b(ʊ4j26J 7DXcPH"σoBQ# 'NkVD?˱pIGt)'D|H QmFPcmj鋈ƣaGCgz`ёi,K/-fYg,i2Omƣ~E̠_fntDbIp8uDT5`4>GK֯=vW׿5fpV9:k]戨7, u\& IDAT jTƗ걄h@]nܼs9$QAE|ӒE:836"чțSSx3םN5Yr)VPc$XEUS11QN<T**j`;¹ÒykhD1("z]M0-B? AB*2gbL0ER&bCY(i}&=c MڋGoQI>Sn(p\쀤BGLD*,5Jҵ8QɺǨ3zlaǏVL8b%DdQ [t5 ,}6AZ^54{ 7p("w"3BC(>J#*ʏV#wB۶Id|׻׼I\pzk_ vwuI'kk?礓N8餓ei}C'ٰĮ]=o~NZ$"'G>c7m'?齿 Nܱ .[ccM7gNھ]N=|;<]v]y1cOct֞mۧ.l׮]gqWUPtm{w}k+|y3e{EoE^׾6~<Eo?~1;_wӟt;_`0K.m^w׾Wk/=9:kAI7}[ܱ};w7>i G/bD߼SԎ;>O/gT*m~7^8 j:[U51Fa&.*Vq$~rJ UAQ!"UTRLS8~XRB {]eG6W*sg3hunbNa8D3*Z4 ѐRBQa@Y+*ʄ4?cjxR( "(s@<9)3x o~6۷9//v,?_W/ x;袋.җ}v[ .o0_rQ1sh(>ϲ֭s_k0Mozm Ƽ oޟ{ж]tQeO;UUGڶ{cQ[@F%cѢm*md~5^oݺ o{;fn[mbD/B]6l z)g}vr{oe ˲Uo'|I);qI32&G"f#тiL"K YA|Fc:*gg`rgK/=nwm?w|HtG :18$D! IjQMD2 4`FsXK02S p-#G`eX7#R|8Oy ݲe}ΊDA "]A$xd6mٚYB?=Cݞz9jq˲gHf!CR'J+FA!Ld`XH V?fO|x5lXyM(*QFRD,z13?{um:!R@r4s UUAⓒ1@"YIU꽵__m۶UM㡶mgffBƘO<1uD"8O}SzWUy󭷌~S5U2ȥuCQoAٴ.ŝ;i|KD*cp5MUE 'U#K0q,c+n& { Q@\عfǥRM-VvnoWxMKF8}|ås,쳾v@-JDpZ)TD-d>\{(p!c=կ]и_w5bAxKx4.wʯ+;:Y튯~4ݳ)6T0xu03|@dAHWO u.ue3.Ңc#Ȝr$ 6tM@VѼ?/|`ݻ\$1u۳c+2%$~{hۀ Qmx/\p _oO]es$Ch U'O{fJ$$Jl  H2*HVl-ڼf)o|l3;-:*{bqC/׮:wOs]#݊j,+ ;L @ʽ`DU<@҅QqBPMeiE4MB 8B`flbdU4(`gjZ;._ʾo{Mь)qo5WWHܿ7k߿+qUW~ [n[kH/}۽obo]x;~+~wlGK7~=㶛۾7W|q5k~3~I۷"ʲ%/ݱaO}Su]g /p޽o~6Zkt0o߱c3̟:K.OFBYZZeUb+[+4 ~ߗ?B@ ]&0#UtD &#^D2E>cXv -nZ?~uڙ'o̟vf;yϭg>s|}>cӹ&(Pd%P$1î9WϺ-H^Us1oeD놵YY !He#m QBP!*'ۯΝ7ս+EnJ7\_n=+Cܟ}ގM o8lz XOgڵkx05Y)C_۴\;>_"o|vԟvC܇}-{i5EQ,>tuwMǞp ^6|r%Lms޷MG}-?Mxi߿?9:{m}־o?UU?n<06 bk @gY;:x酛. |hZJfYa'$Ac*J)iEQz1&gH"E,#"¨wɬ[YG$P7n_Q}ukl7+A Mfs_;5vz(h3  S3Smyhb5aSSEC^g<2IYCL brJy>qD]1E1^ɡQe(l4DfE䔯FC|۶,!v\n9tP[8E' @ͬ]w駝qYɸ\~i娭*PS} '[uT'.! B}-7p lNlm\}7p /BD sc +0"-s2Gk֯yqF@UC %dl?zQ;v0mkE$"U >JPUQdQTTbuӎPDBrk턆BY"s۶)aDUcF1/^#x8᨝;9x ȝkk/Q{RH4(ri p QrA)݃"*D2 U@HUc;uUa1[GU)%D9}[s̭}+"BHDPPdjߒB)I!8Um#mPƲg(ELDt!D@Sc}!h0$rA@kRI;N۶1%+ɳs6,snjq] uU:*V0ٝ>NB4i4J$bcq*@!&&ZVHlon-ꍃ܊@F8QNt Hb0ŗ DI[S0. 1`x\n ^wߑrPU4R0XbDGD@زu ,囎ƭ?zΓ|ڮ"{YSUY)FŲׯDet 6+&xV$[u " l4)6!u>)oR:q @ІAչa,>LϬmb<4?t!Dt"@QmM 1F,4i폂I (\8ҶzsʠLխ/fn뚜qS& ""Ѳ,N̩UYY "mYELE$gYQx\1yY㪩a^mY},ȨѸ@CUՌ1U~8h"95uEd*eYbPťQeiTQc 9Dn<cҏtE4̜(3ާE(peY]gNQ^?'T ( ͒d4/..:ʲLYSSS)n49['*fzpUUeYy̡CSPu=8'>uQ7>%B陵ȹ[$+xkŵk׆i`]ywUU#㖆N[*Va^O3Pb)t$ w[ YJ^41UR0Sl*LJ(\uŶeo(T= ?;<:SSk79Mh. 1R$$ d-I,3AAI/.{LV϶d^>AFB SS,#sLɺ a?t&DC'wB[:;e#~ֲ_~-[wwc^~[6k7lRA2~fm m=nÚ;,!t:M[@u6b9 35o&k7n`3]hv~eoz ¹,znQ7nfج?5sqSSdi;Pw ֨M]IF F#$_Y!J"st](O٤Iί&aZDFut+-iOIu]7Z{3TսylgdpȐ1(A 8Ĉ qJ1/D "(" b3cd{oUacUUߦ}[η볻9sV"#4dTh$1XUhM|svzj2o微d|/`jZ5J @@8`P2h Adeշdٵ=V[oؤ,ι^oz=$Y} iCqtCD@c2"T%(%;NYn?*و+܉C6hvg|8]ܻpC"ar# @UV(ҊFD2ez^i/gռ~ #bR#h[fu9)#D)ɮaQz6[mhO潼$}rQ Ձ˞◿NAl$DC!X2 "DÕhPI (pmE EA4A% ,8BD08sTL @"``(`4^@, u4̜V4 !D "*@!Ϡ)IUҺg3Y*<;&LYf W%` "b QuH& z4JҴmeA#d R 6Vv\sl2Af(h/9}D e1Q  @aBB2FE0D2Q׺BZD! ` E1@%2 +(F"E C @$b@@@`&` ق G CfXUZ.-_<_g^'h(3,F@o!"pB 5E?>1,4)},G .u1,hbiTEs!%QX"!:*!KTU:PznCv.ݧq\Oso'Nu1Oӏ==_\~uُsv{ /jmPw]~[{gr* `=?KϋuG.ڿ+[}~? /0x pQdlwM" w^ F Llmmv@FK" B"0Im K@`9j!ADHeX}c/D(6E%C{K L-A}0"18F2L8=4>D1@ ,dʵj@:'( L (s!c<[tjmu|'| 1F%C$"HQw)-Qt}o`a9MOWQu"FSZ# y xAȣڴ8"`D D0,hDbD$DQ"+F#@+HB D@ vֺGU{@B=ޜ`9z&2H}lx^@D## }SN9哟8kyG,Yr}s;蠃.we]f,}_җ6h;?-~EmW^{}/ymW~''^W]}O?]ݿUUUu:ㅟG}tѭu .G? iZwyW|9gk<;ӠA kIAR$cln9k ]c*BM2$"ZsCţȂV-^͞|W .Bha㲚`т*=WF" 0G`ML]0AHV SAqKn$K6#$6B`@48U!Œ"oD<@=PnT GFLa&!_ @DP]Ut` aPA3 .G V SgYsF`"B㝜7NΪK3>?n+2`"ʥPvu]wtЫ1}!o|ܫ-xݑW늪G6݂{n7ɲC?|vzաy]ӽ(+C'?=5;yl$Y+Iswk\:{\Uuꩧz9찧O<!K$ AXM mb*kfgC„AD2uk:  8M p(alݢjemx(5 2Ql!bbd iꦪ#Qi <9B#R<p(7A<HK$Z{;^<],O[Y^ uX'Z,(€i 2@Apq/"SA<)~)@!"Z3\J 0dHdXYBd (&(( @Vbj##cF0!O.֛mXE_ #""RJ&w߽٦cccEQ<묳 C_-^X)ci+vD0>>n)˲(y~d,yYNHD-V !dzzZ!qw[ulŏ=KӲ, pY;ccEQyIlŃ}^{=Q$mt^TnKNM'YI.'V!~Ym_.H,!۝Ol֙`Enw@$M~n#de|̥/cky~3it*`A1чA9qXV{,ò5s0A({ߛu:^I cP!hAdѫRՑ0D ѨIBDF[ri^3iݥK|_o~gӗ-h'0\a}]nְH)Y7GatK!2DFgbX|#0# J馨bߏd0a;言E8j1M""˂%Q3<G6TG?Wm>.@s^_:u!`~oW^{^{^{ 7կV#EӉ4!GqG=X>W]uU׿e9wwhv*k99昏|#K.}ş7w, ,`XGvu=DLdTdJzvk)rX!b`֞Qr:K20`! ff5HQ @XV%C}w(H,Nب;[HX ɓ *ZlYTwu)z8I&Iޯׯvm[@@]Zx|9g/^O~Upyyt(C!65t {?y!F$1ԙkk[m?cO=䉧l (L(i AҀa ̓AE RXQ(-Čp&P#;Պ  c2{ "D")An'ɱN&AOD$%)`| /ԻrO|뭷#ȃwܱv]t&LU" @EqU MYֆlц_;ջMyzGzm]nl?|Q$-bD ~'??t1> Hu Il5-fLXݶA?G/{50)}I1KG,+w<>+?r5ZJI ?@uub3>DmJzz4MC\󜙛< ^L8&O/f$ ʪ ˜=j@$g}s.˳dfA-l^ bVjʲ "xr!I\qi+/2m95t*[g/SOnv؟[~_\ >,I XU{C*AkA%|@Ĕ rNgNV3WP}b\>ݤxF85֕;lG.{*0h5{Y?i J>E_0{oMϱ$,c!Ȝ>Q~p1,"1ZXdz̲,Khb1$D(4ss~{`)k-k{PHY̼:TU MӥK.XylॄZb8Y"V+/ggO~j s];s뭷ޒyu gqF$eYW_}]wuu}߼>{矗!Ƙ4lZ " q%1j@֬|<8Sirƹxrf|f_163i,# dqݮ%9A  *HZ\ jvh%tuD)B@ -h4MկPZz)EU-CM PwŋǙy޼y~EQd޽ ^j V |,+:{l3-W33gYBP E}đ5q+YS8nv|A{ŽŠB:ッKBd(Q,r\c`fU 3+Y ch(QEN (1[~wb|t\f%)',u(hAYj;jPabb"ƨ6GQu]QUUejH*eXd-tK,]Zŋ966ǻ."j e޼y]7AzYChY`U i9͛gYQVJriWmT=ﯳfsn-ݨ堳zc4HY  Q%\YUjA,XyJVX/Â)ECV}HdY8|cJP%IBdLMڇWcFDKu0mBs5;ԋTj1fMa!ځ@e4Peb IDATjyvUU!5Gs!v=55:Tb~_F_/-Q ]8DTUe!]$I_ݪ$,˪2 FD*(RU/c G)Qr,/!]i7JADe]UZkڧjje~$ڤ kM^DȠZV|0ahHE:~1oiv}w>?˂xp2eFjB9"Z,Z\PàjƌVK- 0oŁ>}^PXҞV98/.or #s&@|0tB-u6 AC9P&k`74  @hh a8 YUFQZ)̈[+j~Wk'ql$q(KKQ6sN -G$9YޣlJD42,41,I~֛FQ@DT׵:Mx^ya9ʲ,˘!I2km gh K,Sz\uZq1,3,g}DD*_'qW 7\{sOW[>_5@_hd52Áe"Z#Z/O =c̒TD8FI !XkUh0YK^_f)aZ[*1eLdZ.HCy( 5pf2R`ZDEQdYj],S6ԎVm4MhгFuNNN4IV|vr[ȪWM~EԲꅨxjDY(1FccŔERor-vkX>-mx!Cq󧧧!,t}omR ` K`4kB &itjjj||*,˺nݞ^wuc54n2Hdg$sbՂn;>>d."7xcUUpDi2]VAIb/Vc-(;lFegXϨiݚ7ʤZc@`#aX}31ϫ1)4x`}DDO}/E-qǓO>y?6ƔE\qi(+*(4M[`ҥuSgpbtSVˠi HGA.[d 'JNZh_w}:,/| gy:^zg1,<7x㭷޺N; o`_rO~|8p]vş9䓒$5_]wCk_}ɸ+<ȝw٥*P5b!"Ɉ j8/^t 855t:k3tP%{t5 <go^oРZ cLe~Ewno 7|_}Cg$|{̛1zKP я~Ї EuY\ 'w]wPyo>G}|XVw]u%|{;NJB^t{o=_\O>qOvSA3" Te?M+&;_WyΒbu]yO]jO׿&"_N<IJ,~>K/ַ~m`"M0|6U03ndvY*:-XKSO g ~8` 4X aEH" !"#u 3f 7|w]WşWmQ^G!Z?'sN=%pG^p"2t˯a>~#;Eo{{N=,,KmHbw;N<\~=ܱii+!QB0 XD5u&CU|I{l2޷ʲ,>lDd@"x/+B_qw2W "XTMfF49t|Ѽ5?XW?ݼ{qpեhuqQ4h`m03@4< ,V;Swۢ[^}~>/,.,<=Aw`D(B Dvo_%o^#<{Ϊd&Gι]w㨣:csmG,Ik$g%vZӓK&s-Z? k_I # _yk4SD^Wg?Ck2ʜssFV<UtA!4v%YkРA,˲,B!D2XbN_FAޛwߟ۷?o?fS:nGn~M@"2:idA] }wk _G!M^QE(;^s| O-^z7 ڸH6uVM7_yWdw &g!qpkϱ+u q V kA$2°ڽnРA @h2Nog.~WxhZeUcᄁ(nZd?&ͫjrjG"%qjձ;|{޳V[=*OB,5?&C9s=餓n, I&''EdjjJG>11Ty9眳}(nAhۂ'Y^{$(nWn-U`H["8Ә A`z4hРA?:x@FcʇpEgD_N7?sO<oIU;yŖ[uxq}16# M§>o1u7מt;n~q` !{{6ldM7w/ү}k{~QMǜ~ƙ;M7u{~W|!͌u^{sg2с׾uQ`l}}Guϯ;]_4ADriu"XNURQ 4hq_+{10:G}?̜YYl~CZ851{$Ir{9眪vumvjj*M~[ov=:7(s… ,xk_[s}{~gbo~vqV~8p 뭿?|ᇝyHeFm6 1 fnϼ3ZaѠA ^{ CDRdw 4hР Y> YdzkZV:=")/̨4hРA8L%ay^eUOHPAD+֠A WE_ K?|H_w{S,Tu)$EDBBc^7hРAsM9P1&MS->|^SZB(˒,[ӃmРA H{f|Wf$Z^X/̲U seز,[VYGԭ(4% 4hsK*15֌ ##Ncy/yϹ|K>Pv{wUUx V7hРA/-=ǰ"Ur{ rF52:h3F!~ Do妷h =Ke@l,a.#i]-s-#fMaU4hР\R#2z8l5! go:ÎX*f@J;+Qǀ`fh 4hǁ9'!zVC5z qXng}4ͽIN9(C,X{{@Ĥ ƺ\ ?@hD7e({ҠAs sΤx>F@iI`rrrbb@`@?o4b`DDIJq5#A \cU\-F%窔3LRdIZx}r)HüN;e4U -,!@^vYp+6 fmGcI4hР\9]cXM OqF➲0ȄE~V@!!FB%o,%]>< D^VV3fƞX=h.4h`Ma>fp g$, X2 €4V pF/-{A7){RCF8 F},d]ݳ1/XޠA ֚*xf (1kH교iY[9j]w~ `UUIe=%(T#sJ"˄[? 4{^0 ˄)h#DA4$," 8'&Ph<|\8:E,g:Ƅ)b؈l&6Ãr;U@V" -033rv&`AD$4iD$"k1cDZʠב;,KD4 g 0KYʉѢz^2m?3ιl54h``R-[D(",H` XF`FwAYs"8z!0J(I.{~{Ԙ B P xMdX=CSG}F4DDdM,KbADKV^I$2Dvy^eι~jB:,K^|y^@1<ޯk-3NLL0LX׵dYFDEQi*"{-3xgkEYk˲̲,cYum%"ԄZu=}hРA9O1,㲬 ʹ -,KQbL"jk`d`fC`A>IQk* 0dp/n8COEBeQG- /oetv! ľuXJfj3j"_xqvuSwEvWρ.{ުLّz nH'`k y#_DqWBN4*ii}"TE1+(v'z8u~OΎ#̮D@x!4oQbIb!uED@+_%cdf"ҥy!cLUU:DTC Ȥ(V"tg][k1UQ*_DȘ ! ^9{VԛfSSSV5[/#ƨ@DDdx)4hb1) ҈K1]IFa$"ˆ8- 2424h`&uԉeTCζ( %XסܯB`0@13DНq(G~ X$<BU,t+˭1*,0 DQARA }%F  a!0F@QfYVpezC(v DzDk~o}[NgM6;y1qJՌu2X9MӲ,~Lɘ:v}(==Z5|Sl6`Xᴚ@Dc 7ozڕ+]D_8 4hb'Fr=߿nnfR8O=xl5pCcktZ3hyjtَ_|_ C>XV-2ƴ/{,|.^K.u].im'\$X:+J.H/o*jK|}ut eY2+YHd򩥓/7hРkz 3 z EDb-4FSV"HZ#u#`S!!(=4YCDC-3vt0"D(c`Wo 8q۝ IDATbuw6X0c^9I@(Ibwu?{[ZYzi1٩{οcN9]iVU%" s}euP8K/y*,umuMƀKQc9Hdj[y^V5Ƈ`d<'I2>]23 8EMJ̼⋐wcKåCUpad&QdP5Mb41Fx՘{3Mb&11Q8( # U߽ y~z:Uz+;]W\`QFq\U(v#iNK):N# Z}R9-Z,r*YnJ(qi$i V.H_8џ>i;`al&QIQ=k:qY7?h5v7"u $ē [`@fcv};XGA 12038”vqH޵y0@]O.A0㻛7oyw^{ TfAIQ'Z{tg}SܼcJ bO/~ꝷڲy5f}d<״1?ƍ{w8iͳ}v벥KwT*.Z/|-MD,%{}=Ⱥu:QGٰa}"-|7>eϽm;~zݳWo} l;^y>tPc"3O-fɓB˞}sq ׎ֶ;zh_b4h@k?`A$J@J?=[l>bQn߀3g⚫7m[negR~Æ? 2ζٳoO>v&iBлg-nXdaC~|Q(o;;[lb7,|@]P3#VhreYPmzX |QG{&|1Lw=!]Ȋy&\"`{., !N`OX޸ۮ;(zr9uVڢyG޲eۖ-[1팕RMŢPRT:ۿx٥ }~県?_\T4Rg) ---N;7ظq'DRU;~klڸcOb%/~V\?ug]ۖҫY2zf_76oڀW┓N|wㆍ7bŊJb%@A -JE,R(LZӟ[^}wD{EO?$2ݴ`cO.ӟ^L+ZR'PH)Q`~9gm޴5/}{?_>~ۭ^$JG9jMӏI%bG;n}ۖw;5/};+;Z2g;WcȲT*_8hv;LfB0@` d: PA2%W !skh}$%v}ʻW{i{UG],UKx{BV(FdyXc O=bx饗.Wk1{?w\"ZbDq5-ԧN<ϿZB]{!ᄏT*U*}?z1VK$.=Јaßz_Xѻif.[ŵy&)TVJJ=_bPsW\qu~4e@Z;cK&k=k%IRU֕JP.Icǎ%O/~衇:ꨓO>yB xb Bʯ|}#yy/{ٷz;). 6!bgI0MM4c8pNy衇ָ;ֺo~|feyٳTk[ps˖]s5i~[o>t1瞳:߻z˨~ClG 4n,@BƮ,5;xFDeHU`QW| ``IAJ8Bx !h`S0`1 {n>Zjvv(-lXḄ?z7ziR`{{;0-H%ee$.~jу} X"@'b%HFJ)`N+Ub}}y͚;W7ogw^ة'8{aM4鰏|DG>v{Ξ=T,yđ@(w68y.}rѵ?AyG[QJ!=tvvJ)4E$ vOQ4qR;n͚5ӦM9s]__ $,%K=8 <5y98. Ιw6% 4@B .  8g8b#f}C52T3B}g"@L!={ԥ<=.f]v )Q0 | Kfٳ*O>&N X;DNjS*Fk}_ET7 0AI7ƀRACX,G~_w}o 7yUW^zٲewyw\tҕVy~VPz mjj C֖REy @F g%eN.{.cM&/ i-Ydɒ%˖-3{ͿIJy9\~F$Í,ѣǎR*Zp~*sc "K !0o޼ &1bZ:F!}7nޒZڽO_޸dSBEϿrҔC>3n+WXq!ɭa]-K$62ǽD޽{ns.L) }f$I*X,zbR}(9b].zr3# ׽F'N\pah4ݱc1&Mv}Bѝ$P Ӳ瞟qkW#noBiji =i=1ވbG^}[~;J5O.~PYnlN @dd$PY*%#&XKc-"@yF 3E#@콳Rh`atifc Dk:E i.=! P7#`Ilt7>aW,UfTDiG SI]"LBE1o۬h)\>͛BM8aԩM|quQׯ]j ғ]ԷւsFXb;@*uο+WOO=}d}FrI8S}gŊǝp"!kՔQD:61Aܔ < p9s6m_/0JJMK`DrkinU|=s?` #GoNsGlذuŋ/N6Grʴi_Ν;7t2,Y2|pt .hG.}G5f3p |~ٳwe>x]wݵhѢrSK4on}^qŗ93XܣGO=3}K_:n!s:lļyBkJ5T5hg.1@AFwYĒP3u?!03 4j[G DD uB*$#OR&,uEv-#)1G`(;^z֬Y{q뜋:`}_bSRwlRN${mV.6k9XJ455*ZD2kN7ZF*Y-WJ8Gl|GGGu{Flmki<"10q u \'F9}1C#bXr&GRP@ D){"k3}+9爼RQcn-o4 WLWd.:뵊&!19%B]X,y)+X.(E 4/yy@D]KJx2qf`NE1;8k=D<# B`RDZzgq$ɉ@J8f&T,ZQJd-1RyŮߨU 2{&:VjǸdq =tĉʥRVu>ܙ$AkR9][}rּČQ;ҵZ\.9EQp ,$Q qF<^)%+R RZ(1\.gyh(K7.X0z$I;`b(ZjFr!LR =uVJJVŕJ%"g-0J!'އLOm b 8V'ܰ~}% Z4CGQPH$skm`[l f 4cN,C%'@̾y*J5QseY=0k%@ zc[ g,Uf,KkΚ8 љ̙erj={bZ&L<2HXI.ݏz-39BA8:^:N8Yg*$h-7ykJ8R"1Y8gQDIouP, Œ#_Mk:Ԫ)BĝՊq/~5+m6OəCƏ6Oi1JX";9bV"%VaoPXu{%ZpUP(mqRHk],꫓$j5fS?lm_\iڼVcYck{@,l:ӧ`(.Tӊ([o嵃&Nw!DJ|m޼qknX&8I٧Wq[+_Jxc(JrcYFJdzfz}+kV-Rg ^MɰC Vl!=s n&yHA! fBkT3Wk՞B[Fi)%# `My+YMi]JS)%%0^Bp;k8Jk8AKQt*"%\K٣9֢(zn3BIM𞥔׬ P 2)@ r> ( jD"ɞs&RgJijDm=[z @):zbcILvlm$"-E%D R ϭJ ('IYSSp$Ra ǑLE։ q!ӬQ.`Vcu ^ L:%I IDATP Я\ X1&00(Aw@ 4R@h"mjfDDR(fD3#Bf $ PJ-MFkY,NsShw7mٴaÆ*FxE_?3{ԨQ¶-ol\-F5z8+B%T,\yqG,۱n՛|g''QYh ə8.;sOT:z-pn޲/sn̙Æ {g'( fݻ75[?{~wu7 <Ǯ\%DZ_{G9k]|cN:\.K)ZU+!oԢlxs (~v#g /ZjժiӦxdO/YB\qcqX{g !vlrkO8ľ}{晧8J-Zȣwщ6˔Bd/?9묳{s'̞ݫw֝> Yػ uuֿ=@R9s{ HJH?[l{Rw~zݏ?P| DJ IAlo`*| dB0<23iPfA2l@`f\]Ɋ5\\fDIuGPɰ#9Iw v5kٳ&{(iG>f{E/i6qcGڳWv;o$Jnz{ۯmkc;k_n1dV ~w^[Wk;ͻ}>X N5ăxx|g{{et㍅(M -ղeK.JoO۾ݴڹfx[gVBg"0z/ؓ5-X _-읻7}soO?nٲe_WyǑBeɒ%^Zϝ}Vm/^z/}-7qV)qE r=3۶l~ч/K믾r'yk|[7oZ/_qٯ괏:`_(BrOVxYg|;ν!V||}')%" !~[׭[7d^/s׿>}V=GP (|4@B !. 4gfD䜗J oR3FŲc!μOz-q)V&MB8,SjR)B LX47ؼ~=tG}u/qiC \{}׾+/i [뫖N<̝#{ea#w v˯{Q7lZzEgqUW]k~0yRO %g\tE=zxSN>5}|ܹi-wyV/C^T?BL%KlݲOY<9K<=頃׿5C'NdRTTK_Ν;_|#FTT*wB~.ϧMk^^z_Fgsb:mzg}vPPZskPV'No}w߱GQ}[yN:ZfyEf̏>X{Es瑒 T*c.G~hE 7N t}[7~GZk:S6Ks?ક+;v߿aDZVK Co9_{Sn4z8nii3f\q[l||s8Q9rڵ@yR,DdɒS2{fo:+V̘vŤPjmm;Z0 7ܰx" H )R <%K#DTjq\r#i$EEƘ<-yR֖eyʕG~8XZΟ B\H< >}T+iR= J#FxǏ?;nk|aBky睋|"˲Rʕ8  '6! s4@ B `" ] ={#/tuq=!<NJQD,§ 咩[nՅRy 57:QHG^ () 5?9TBcVr8U1c"X4MMMMwnݖo鸰א5ˆd0xz-S3>kItv@)zttV(*J dZ_y5tFtK?fuwFfa}ooGS'L( ${R)<'<-UW]կ~UK/Bksq$"2uxť&"J^}dP+v68PEtlY1y5DZXK,zPoCT;RGZ2JMaF;z}ugSlU( JJ",@8. 8xRl[zww'޻[w2;%S#Φq=ӎIFzB2ZYk_[z >3ݫ?Q;p˯{N%x_jiJ4*oԘ斞{ovǢ$ջٳݺT*}ʯ6hA!6x  rcƌ 7]ˌs0z̘0}V*GqeT9M4IJgΜ\r.<8Nr MBg$I8rcWe,:eRE4p~w1c+~ʔ)9)h5a„Ϟ={۶mO9S?&PHĈYJٷ^}  n'_a7gokV*7_~ӔZz9锶6[߻:)0`4yW9s1s4~?B#fm{A'HjHCK>كP$[ZU,q柰$be^oOytlj;oRu."Ymoooݸ;(أ@Ԝ<ݱe˦ͺo mֱsk 枺ϐP@޳mᄏI!C"۷|dYK{TkYcGVPr.5b@i = dM^6iMSI0Q*֑g `(`QЇI9B£m֬Y>Q*Biȑ^yT*utt9H 0#P,Y%Z Mwwhj(8ɽ:QR̲YXUqx:.Ho_!k"{/Q "{JbA 9u<"Wkr2~^uV@jxr,zKq!S"6 BR ȃX(gyMBA -hN2F6WnW@+5$I};R+C؊piJ"EI8hH0032{`4HDy$+ ܧ:*EIQ4ln.;hQZP%# @7d$8FHQ,qOcl?v"/ !'$ J bt!b{/ QV*bHDy!D5MYk]3)eqYDTQ\UbVkii馛>huZ8AkDܱcGHP0 y: [k|&YJMdq3$1T*y.Qq)hy‚Ҷ\.#"9y#Ƴd9f_DkfBBYB`p* JUJY*i u$U:8. ;w, RZK%!0)Ɲ Ef(Z/D Z{ 1 znjiqIRKf0΅xBѰhuK>A"{f8kSQ\ J]3x_bcHJ2 LDZ){OBcb[99cm1sP x0, "bS$JgDPeI b VV +XN+P$zŖ< A}0[B,ZkVS6bC&@8J $(J)uP({EQg}2UB@{{{KKKjg(C0B TH)  fR@ QY?un!#GI5 &b][%YMX R*tC#hDqGGGnj[g@kG9{L$VTYE`^,{i\v6k lY*eR߹^\<Yh_)FRZGU!%q?|(sb=`=ֲs,HM T(T@83 )&g֓,B20BJiɍg)@8oQJʇq[ $ Iܕn%KRUDaF݄qc݃lFZ1#!1ұ1y "!$q&MS\8~2X,"w.m3Aĺ:(Y HkODY'qIRA>H4{"ʲ48bQQduΣ@c,GF:#xI+hUJ+XZ!*Us ,K/?>MDYRÆRk,Zi`IE`Qf(&NgD'? `CkwVkSt`kiwDi7Q]iغas(NҔ{A""Hw';tAf$µ*!˲u`c>C<宣B 빶kviP CXGZS[,/ Upu6x zFCp6X}%ٳ6 L10 d|}b\aβGhӱ*|Bs.YpJRmb8ItfY"i !Ek+Zk)Fn4CQ6u]4[QELBDH{_E}D[s.Zuxk4+F_v!UW\~V c`j;+&O<̳1&s"'MZ1J,Y|'ՖSv_swoqS6dŝʲD|]3~[o`i馛>~˛CvqWUuo~믌7nF7o^nH4q)֭\rC>SoG8wWy~n~vӷ~{OO8qX"DŋoV9stsΝ3 }}y6MMA3|]iEvCM @ϲ lp"7v;'nVkI'dMѥKΝ[Uv=v1^zwҥ践~lפRl9 qhbmmV X5bA 2k.HL% ȂFj$*8NB<W!ڋX!` d `Az.`j` yh#GD@!8f/I,5)qbwsCD H%,YxIWx{-Y|3\5'?~q+~+Ӳ$=x'X>ˍ?~߹qwO o;wsf3w_ȒFabw'|k<?s᷷^?qp/*8t#"`R_l޽(rE`0J[V/?zO=oSuя.~\zх?bNYi6zV'-}xqL$iBZ0Q4{ QvL,^bҤTUj3"x&+cHki,ZY?W돟E-]rie+_J$ݺ[wwY " @2)94e/%Ztְa,,IG5 !!,Xi`XJ93&-`@lD@ 0 d ("E!#  !DjTwA%ƠrsM>f ٕOkӍlj(lQq삈ꪸo}=k, A9Q+k|W[~}'&*Mʛnk0a3_~wG!κK6?npU^zuogy駙;(;y3i,nsu;~P~尞\V%,3g#;C{RFBn4/W?90Դ=v_E?ʲ|[L{!_:uje"m:TTZ V}=}͞R/3  v:v;P5!6+VwqOM{׿{yĥ]ة .(v]U.]|YB ,5:=bĘ ;oe&AA)EEc\( V;e3w@#GSBp  (*#]mJϪjgAR2\W%[ӎFQ]{2Xyt-ue٢7iO̫Xdɡ3g"I8knP|b` xO~ 1L)ZwK]^BpÇAo*g?7?b'u,-Nz뭟x≏x>r Cf VeUvZeO=ox_z Dv噽 Qx`8`~_|EQHd jZQZ#Mл:MSg$۞vZm6+<_ߎk_ڐAooo$7kcvg~ƌDFM+W[Y[8'oV[OH$tҥ Anlզ͕F%W$t@գ~!} ;3Sy(v!ul@xh]@X$@J0@!LBkׇz#V/C+ \g7{䠁ૣ?rآ3̼jhhEHCZ5 r}lQ|*1PU.[gTzdYgA҉6lk@|Ǔ,*HZlF Xk}-^wuv}뮻vĈZ$I@tF@QF͞=g}Gz7QBF188,gvi+b7eY$ 3)(Ml)5}'5 8$"2nL'?9xA i``a~;&Rǟr_RE,<Ͳ,$I2iҤ_>;co7skk]tz gHZk x˜ C C0Ա` (AD#cQ'7fh[A4vz~ͭAHx QsV5&ifi}u±GkdMg 3fVJ6@(&K+[zEuͶge鿹$k^yG9vakԳc=ǖvM̻tfR !JsU90&rիBPŽVh((,d)bBɉw-55[@x)}p|$d`OՕ"HsuFG`p+pAg$XϘO}}x#)}̞={I̝{Eh4uU}ӟ>묳~di7x9k䭳0NsQGwy3O>gΜ(^b N IX@ .7nܓO>V[;Mv!mfO=YсrG$}z3= ?Qk2z/1y䑥ubvk_Ϟ=K# @aÆ FY׃&ֺ8" " CCq㡀;=H@`,q}^D Ff]_FP Po(8gADZ VKhޓڜ}I;5 y@lV I&o=ym3֬M6l1c7;#z@t衇≵u&Xɖ;n抷 Gs̱w6?n V+x9jM6;Fo/r;aGo}f<`1cnjMs}Mz棎9o^t@glO /tq͘1s Ə8qQG͜yUF/4{gŗ^n6[ڻ3dMOxܸ3gjU_sŗk}n4fÍ 2z„#G&{zS8CjJ骪k!Drve¦ul2f]>$) ;N$t0a܄Jʪ6IbvygS}=M'lwհ#p;FK.]>`Kw h%>I5b$r@`\*(=4ym9ۼYw:{bسl@ :,uN{! 0pp,`$C:XHJ)A*K "MdJiVB$ PuٶbjoBFotEPUN%Jbdf`@'˒RHN։g?D_H)CUeQ*GA=j/: Th(k+cRIF\uXr_!яTk5ֺ҆ēs;c:4 ^F_$D{+C˒v61YQW6Bh*m(Tb ސBy/ *F(,M*Dtծte+|90V whp)ۿ7ZJρ QBB9PQpzsE՗V{jVi8{=~MZb`f(JJ8pVmFkPJ)  t"#C I׫R" b@BbLkgM zBŀ P;oB@ဤ<{`NFS)ʺ̪J(͂¢(I VY^ )ѧ K4 )&Ѩ|pIQ!0R{QN(0a„f/$l]@T4\p=i^233 !ԉΧIr]XQVXk$4 f{X,TE.]>wY q02L{ŗzW$ \U xIQ@xOa͗" lȐEl@ 5qY 8q0ڗ ˎ2eWiB%2[iw"\H,5 s11anZ}}}YL6 FPJya֦F8/5)cL`f !t:M>Peޯ$O,FDl͒Z6G+,SmֶS武`Y4Ͳ,{zz73+@")ef7f쫯ω(ORkm2J2k4eYV"]HQUUv;x4MGFEIZVYQK.]>!Đem|hRQi" A*bot@)7 H:;26@R?YP%\$Ǖr 9~ȉc`gQYĐQ!ˌ18$R-MIJkRHeY kZC3iOAql4;s^5D{`b$>Sq2xEa.YEb J)M  @>HYheaRn˂b<6(Zu:")VUS;9yEz9fmTEjYY%ZD@)eHqFQx,c.IA/X.]c)b~$^_mȡAb'/7TȂVG9=pb!8@1sh Fj5IQX,*`@HPX6*:QEU@D! .I=000|X;IPN,1 {O12HqC1bժU1F1f)e;:ke#xoHEc""*"MS*JLZ%4̄5!07\{%^40H$F@@/\u<ϝsy1H U leQi лj6Zo\&`ΛU}C=* "&I$s=w뭷6:kHVUu&IeVYPեK.@ֻ`|ZS20܍MZk2 aHR!"Fu^S5Xuw-Ƭshue@Js AkĨ[ WDSjmbͻ$.pZw]swywLFڻ4E6ƸLA EQńԘWϞ=;NTΟ?w]kh5w8qϨDeM"$Hp̠ ED'eQJŪGwxB9ldUkٳYE3˝sbZmwnǹ?5rA(˒ ,fp>KeȢcn,MsDi.*ϛD?)Z k +ZBH9Zhjf$@ѓj68iϮ*꼙iȎxdDΆfTPUAluBDHkI.j/$2ն%~ז3avvۺ`[$UQFoCݪ]tRB@@@H5)@B{VxD(g5O  !g!P," #`޻jo^s,^D&[ˌNI1Kض 1H@`f#X'yo~wח=rQ`IP)Јo|^x导ʆmwov~E>fk (f^}ͷ{.nƭ[o}x);`^}_ zp`ժח曱WZa$߸{9lӉ~xі[nI7p*<ۮҤ^{?رcy5iE\R|kN'g?[h7!G,}6`c̢+N܄;n7^_>bQw\r.|N?i͆bW¸qR矻[\0_Ǐ/K<\UmQef/Z/?o•4 IDATӋ/&lM_ ?0iVIwwU&LyX.]>W55I">x:;40#0HWC }EえDX* 5iH@D!aF !(XsF10k,ѩ1qv 3$IAb{%6p xN9{ヒ=pmVu,OlU3?GuT__߳ .8#H ?Wk(W~INg( DP5*pu]J8gO<?Hq4IIyχ~^HD:}L/yjN?>٧ya\t5*;sבG~ "gHeYNcs3)t^{UW]:ʫ6u:|tz ?'zՅ_Ț]g&lGoۿYzkmeET( Z!0|ѳf͚u[O#jm[6UU=t{Zf/rː9(&af$"R&.87rȲ,G UUU'>}z/LD Bp)0O?5eӞ:us@Hl``63;|͗.]ieeY6,&?f̘;r!b 2(Ρ=:Xw/~9V#?NxWLN<ǎ_l;h>O?dMqh__)yZ[QsڤGҵJ'^ț "ҥnH?hi1$Ǟ4i򩧞ŋSx<q6.{ޕ+5BН2eݟe =+FE\ٴֶZ-D A$/Ic B$zQ)ChTꊙ 5vteCn`kQlM4MSI2888o޼ۮjzV_~yҤI'tG}QffGeߟҥKN,hvU1DNkb!`:aEDgv9s>~qmO|bi{m[<Ѓ9xw|'IM◾c{W:(Ӊh̘qرc7p$Ip[mՇgFc^~ 76|i&no9~g9kM=mF;Ç>7nx$ sn65j3oVx૯:bĈ;ov[]v{YҥK"8J˟%֪=DEHQȲlU 㜃1?Hm2aUVs8ifۍJ!թ`]jRRIMUUq)"RCYj-Z&mѷS?q|mJ<8cLj|dB}&u]@+oxb`nP+ ;6r_,˪"7iD!ꪨ̠ VvFF͢(01뼑UE-Nyߟ5s,UVFeYVdֻh\btCQQjч,z b|Aҥ߯!D,DH$vhF}n|"8E=s/.d-(22}ϰa(cnuh-2TҊ0 P+dꙧ{Ə?ܳ7b /eYU$aDD@RZk%%iZVU՝bfOOQ@ӴQejҢwJN]Q4I+kS0BTF+`DX-VB+j۝NgHUkH*:.,-KeHKLBh6u]+Ezi^t޿썿k_nYD=Sw< VAmf&k p )!DS tl$M^f9gf"Uf0@֠}@/Ð)n4Z13dJ[&*D%6XʱCF2t4dqANtZʨ!N4Ǩ (:1}RJE튲,zd}A[b``1]Sm׬K.{?ҡ7Q(qYf1FR$IڻNl( ,KB힞~ $B.DLQt,ӊ I@_@# c<ň #$I5j0 NJF }Y ]HfekTF{Ϩ(*T='Y @C$B轍ιym`M IB }`\Hu@>4IdL$.]ߥR|8EE9A ׼du& @ [Ѓ։Vy`P}Z$Zzˢn,VY1'ZEBYU!"1bʷB |ݻrŰ6MMCfqRm%Of󮪑7Yn*XH8ID+VȲle<'"f[I7Z!o2l)$Mi52!yeQDiu|R(jK&g@vҥ묶ֽ͞ySϝs??~r󦍔s['9UU!Dq9,MQ$8QNHIN̤i)⽒B'F' %KI k5Y0%IS)ӺHHouTc%MJv+xgMHseWιHJňrFuxcNkNk>Uid4XSx@;\KU qO<'MOY]٦O9U+VUO ,kQ1.D)u=gÆ P `mnokH!{fIO?TRi]2訴Vݵ{GW]yΟ?CWʴi94 ()I#V,_z޾{l֔`+/}`}m$8-Yx!GZ7׮>bׯ[/uq5Ӱcǎ_.c˖-K,I֮}֭'yoY1nͺ1c,[]otsݽ |\) ۷mk/H5598gYbz&B;1ɘb['4WhDpyyv'ulٴ_{KgL))6޹X+$3vug~Vke8{gJ9ܚ5 !")kjHaҴY^[3 _r+^qO~ ݱ/)۾/lkݳrŲϻ޶n/]u n'?7m&{~x,̟w8{ʇ+k}od7R<%7nXXmmϾrޙ<|5~z?0B$dΗ{g>0J6xkS|ʇ*>=c:`񙋋f„3/\dIKKBk|Xp ϧ{{u/0c { [8ֻ72.j88j̩F0~)6?Ԙ-`]T,@JuZq) 6eW^Ƙ~󦍟dΙ1s޽cƌrYav@P|Q,ܦMcǎ͌QQ  5zt!CYڧo_:pQMM=|ʔ'?iЯ{ϝ{XX 8I[C ?2ΙRu4{<Ȕwe'>iOR%8qE\BYN5*qޟ2z4!uZwd~ h{G~/{(}@Y>mq6aR۶9#N777]Otֶ;JuW_}AeJŇӿ <~G/ BLd3/}@o#j=c1ZGł76]͔:mj߾}[[[ UV\P!/i]d5ϳRJ}}}^;!B"3"̋vx]a {+v]C^r0\!c e$R)RǪR!1L2RK4F9eY %YJZk)s9jd+{U!xLFRN(#B,˴*ĄjR@1oRVWWeY\(-j8C^t˘B@۪XDT2ꉧlpG\'mղxG)JIdYV,+JCCC۫BiIDATBȃJz9pjH~emX*HeQЋŢBIpq@:qַK)e1)4)i.Rr[TJTsZ*R /+*sƘ~BRf!UhgJL 4KRr[b&0* ($ۻwPQpR@(g!Tpk-Bk  `4B-KKBeYF)Bx;Đ*zB8J:/^HXĩէCںy-q)C;CK)pvT C$ޞBoYu߅{Bt΃#mb!o_k~uS:j۵K[o ,C"o3Q;B:X!PHQ'$t(_Jg O<9Ƭ!r@;;XK-7:<7k#~B=~: dߔ@ 77NOverview