pax_global_header00006660000000000000000000000064126235644310014520gustar00rootroot0000000000000052 comment=ac0b4f17fe659e106254d2b4dd4d9b662fcf1cfc openggsn-0.92/000077500000000000000000000000001262356443100132725ustar00rootroot00000000000000openggsn-0.92/.gitignore000066400000000000000000000010241262356443100152570ustar00rootroot00000000000000Makefile Makefile.in aclocal.m4 autom4te.cache compile config.guess config.h.in* config.h config.log config.status config.sub configure depcomp install-sh libtool ltmain.sh missing openggsn.spec stamp-h1 doc/Makefile.in ggsn/Makefile.in gtp/Makefile.in sgsnemu/Makefile.in debian/openggsn/ debian/*.debhelper debian/libgtp/ debian/*.log INSTALL debian/autoreconf.* debian/*.substvars debian/tmp/ sgsnemu/sgsnemu debian/files debian/libgtp-dev/ libgtp.pc ggsn/ggsn m4/ *.swp *.o *.a *.la *.lo *.orig *.rej */.deps */.libs */Makefile openggsn-0.92/AUTHORS000066400000000000000000000002451262356443100143430ustar00rootroot00000000000000OpenGGSN - Gateway GPRS Support Node Copyright (C) 2002 Mondru AB. The initial developer of the original code is Jens Jakobsen Contributor(s): openggsn-0.92/COPYING000066400000000000000000000430761262356443100143370ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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 Appendix: 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) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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. openggsn-0.92/ChangeLog000066400000000000000000000112171262356443100150460ustar00rootroot000000000000002004-12-30: Jens Jakobsen Initial MAC OS X support Quality assurance and improved error logging QoS length bug fix. 2004-09-11: Jens Jakobsen Added selection mode option to sgsnemu. Added charging characteristics option to sgsnemu. Only include charging characteristics in create PDP context request is if flags are set. (Thanks to Loic Bernable ). PPP PCO length bug fix. (Thanks to Loic Bernable ). IP pool hash table bugfix and improved logging. Improved configure.in and Makefile.am for compilation under Solaris. New config.sub and config.guess. 2004-04-28: Jens Jakobsen Improved Solaris support. OpenGGSN now correctly initializes tun/tap driver under Solaris. As a consequence the ggsn network interface IP address is set to the network address plus one. Added routing manipulation and IP address alias capability for FreeBSD. 2004-01-19: Jens Jakobsen Initial FreeBSD port (Thanks to Pavel Andreev ). IMSI bugfix. The IMSI encoding used by create PDP context was missing the leading '1111' to indicate that the 16'nd digit was unused. (Thanks to Pavel Andreev ). 2004-01-15: Jens Jakobsen Added iptables firewall script. 2004-01-14: Jens Jakobsen Changes to allow compilation under Solaris: u_int8_t changed to uint8_t and tun api changed for sun platform (#ifdef). 2004-01-09: Jens Jakobsen Fixed bug which included NSAPI in GTPv0 create PDP context messages. 2003-11-10: Jens Jakobsen Added --net option for sgsnemu. Allow user to specify the network address and mask of the local interface. Added --gtpversion option for sgsnemu. Allow user to specify which GTP version to use. Added --nsapi option for sgsnemu. Allow user to specify which NSAPI to use. Changed the functionality for multiple contexts. Previously contexts were differentiated by nsapi. This limited the number of contexts to 16. Now each context is established with a new imsi and msisdn. 2003-10-22: Jens Jakobsen Support for GTP1. Currently without support for the secondary pdp context activation procedure. sgsnemu will first attempt to use GTP1. If that fails it will proceed with GTP0. Various gtplib API changes to allow support for GTP1. gtplib now listens to 3 separate UDP ports: GTP0, GTP1 control plane and GTP1 user plane. A socket for each port has to be included in the application select loop. gtplib now verifies that messages are valid for the particular type of support node. As an example a received Create PDP Context Request message is not allowed for an SGSN. Standards compliance document. 2003-07-07: Jens Jakobsen Added spec.in file for building binary RPM packages. Now openggsn will install binaries, man pages as well as scripts. Added ggsn and sgsnemu man pages Added ggsn Sys V init script Added bootstrap script for autotools automation 2003-04-11: Jens Jakobsen Added -ggdb to gtp, sgsnemu and ggsn makefiles in order to include debugging information. Added ippool.c and ippool.h to ggsn. This allows for generic allocation of dynamic ip addresses based on a / description of ip address space. The same files are also used in sgsnemu, but only for hashing IP addresses. At the same time the corresponding functionality is removed from pdp.c. Added syserr.h and syserr.c to ggsn and sgsnemu. These files allow writing to syslog with file name and line number. Later this should also be introduced in gtp. Added support for DNS protocol configuration options in ggsn for create context response. This allow the MS to setup DNS configuration correctly. tun.c and tun.h have been updated to allow setting interface IP addresses and routes by means of ioctl and netlink. This allow sgsnemu to allocate an interface IP address for each context established. 2003-01-29: Jens Jakobsen Added -L../gtp to sgsnemu and ggsn makefiles so that make will work without an installed libgtp. Added sgsnemu check to check for valid pointer when deleting tun. Removed enabling of ip_forward = 1 from ggsn.c and sgsnemu. From a security point of view it was not very good that openggsn automatically enabled routing. Added ipup, ipdown and createif to sgsnemu/cmdline.ggo. Now sgsnemu will set up default route and then execute ipup script after tun device has been set up. After tun has been deleted the ipdown script is executed. Added support for ping to sgsnemu. Added ipup and ipdown to ggsn/cmdline.ggo. openggsn-0.92/Makefile.am000066400000000000000000000002341262356443100153250ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in SUBDIRS = lib gtp ggsn sgsnemu doc pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libgtp.pc openggsn-0.92/NEWS000066400000000000000000000043231262356443100137730ustar00rootroot00000000000000OPENGGSN NEWS ============= OpenGGSN - Gateway GPRS Support Node Copyright (C) 2002, 2003, 2004 Mondru AB. Version 0.84 ============ * Initial MAC OSX support (Thanks to Pekka Nikander) * Quality assurance and improved error logging (Thanks to Pekka Nikander and Jonny Winberg) Version 0.83 ============ * Added selection mode and charging characteristics option to sgsnemu. * Bug fixes on charging characteristics and PPP PCO length.(Thanks to Loic Bernable ). * Improved Solaris support, hash table bugfix and improved logging. Version 0.82 ============ * Improved Solaris support. * Routing manipulation and IP address alias capability for FreeBSD. * Initial Debian port (Thanks to ARAKI Yasuhiro ). Version 0.81 ============ * Initial FreeBSD port (Thanks to Pavel Andreev ). * IMSI '1111' bugfix (Thanks to Pavel Andreev ). Version 0.8 =========== * Support for compilation under Solaris. * Iptables firewall script. * New options for sgsnemu Version 0.7 =========== * Support for GTP1. Currently without support for the secondary pdp context activation procedure. * sgsnemu will first attempt to use GTP1. If that fails it will fallback to using GTP0. * Standards compliance document. Version 0.6 =========== * Improved README file. * Now uses ioctl instead of ifconfig and route in ggsn and sgsnemu. * Absolute path to gtp library in ggsn/Makefile.am and ggsn/Makefile.am * Compiles with gengetopt 2.8 (Thanks to Lorenzo Bettini ) * sgsnemu is now able to handle several contexts and allocate interface IP addresses for each context. * ggsn now supports protocol configuration option DNS addresses. This allow mobile stations to set up DNS based on information configured in the ggsn. * Ping facility in sgsnemu allow testing without the need to route packets through the tun interface. * Man pages for ggsn and sgsnemu. * Sys 5 init script. * Spec file for building binary RPM packages. * If not --createif exit after "ping" or "echo" finishes * If sgsnemu echo failure, exit with code != 0 Version 0.5 =========== * Initial release. See README file for installation and usage instructions. openggsn-0.92/README000066400000000000000000000327061262356443100141620ustar00rootroot00000000000000OPENGGSN README =============== QuickStart ========== Requirements ------------ *Linux* OpenGGSN was developed and tested using Redhat 8.0 and 9.0. It should run also on other Linux distributions as well as FreeBSD, but this is untested. Compilation on Solaris 2.8 has also been verified. *Tun* The tun driver is required for proper operation of openggsn. For linux kernels later than 2.4.7 the driver is typically included, but need to be configured for automatic loading: 1. Add the following line to /etc/modules.conf: alias char-major-10-200 tun 2. depmod -a Installation from binary ------------------------ rpm -i openggsn-.rpm This will install binaries, man pages, configuration files as well as a Sys V init script for the ggsn. Installation from source ------------------------ 1. ./configure 2. make 3. make install You need to be root in order to install the package, but not in order to compile. Running ------- *sgsnemu* Start the emulator as root using the command: sgsnemu -l 10.0.0.50 -r 10.0.0.40 --createif --defaultroute This will cause the sgsn emulator to bind to local address 10.0.0.50 and connect to the ggsn found at 10.0.0.40. It will first send off an ECHO_REQUEST message. After this it will attempt to establish a pdp context. If successful it will create a local interface and set up routing. Now you should be able to ping through the connection. Use a network analysator such as ethereal to monitor the traffic. sgsnemu -h will show a list of available options. sgsnemu -c sgsnemu.conf will use sgsnemu.conf as a configuration file. A sample file is provided in examples/sgsnemu.conf. *ggsn* Edit the configuration file ggsn.conf found under openggsn/examples. Start the ggsn as root using the command: ggsn --fg -c examples/ggsn.conf -l 10.0.0.40 --statedir ./ This will run the ggsn in foreground using the local interface 10.0.0.40. If you don't have a GSM network available for testing you can use sgsnemu to test the GGSN. Support ------- If you have any questions drop me a line at jj@openggsn.org. Features ======== OpenGGSN is an open source implementation of GPRS Support Nodes (GSNs). It implements the GPRS tunneling protocol (GTP) version 0 and version 1. OpenGGSN provides 3 components: * gtplib * ggsn * sgsnemu *gtplib* This library contains all functionality relating to the GTP protocol. Use this library if you want to implement your own GSN. gtplib supports both GTPv0 (GSM 09.60) and GTPv1 (3GPP 29.060). At the moment no interface documentation is available for download. *ggsn* The ggsn implements a Gateway GPRS Support Node. The GGSN is a small application which is provided in order to test and demonstrate the use of gtplib. It is fully compliant to the 3GPP standards, but lacks important functionality such as charging and management. Use this application as a starting point if you want to build your own GGSN with your own fancy VPN, management and charging functionality. *sgsnemu* This application emulates a Serving GPRS Support Node (SGSN). sgsnemu enables you to test your 3GPP core network without the need to invest in a 3G radio access network. An important application of sgsnemu is the testing of roaming connectivity through a GPRS roaming exchange. sgsnemu will first attempt to use GTPv1. If unsuccessful it will fallback to GTPv0. Performance =========== Two experiments were performed in order to test the performance of sgsnemu and ggsn. The ggsn used a 550 MHz Athlon with 384 MB of RAM. sgsnemu used a 1 GHz Athlon with 256 MB of RAM. Both machines had 100 Mb/s NICs (RTL-8139) and were connected through a crossed patch cable. Both tests were performed by sending ICMP echo packets from sgsnemu to the ggsn. 89.5 Mb/s IP throughput when sending 10000 ICMP ping packets with a payload of 1400 bytes. Transfer time 1.27 sec, no packets lost. 71.4 Mb/s IP throughput when sending 10000 ICMP ping packets with a payload of 1000 bytes. Transfer time 1.15 sec, no packets lost. 12,1 Mb/s IP throughput when sending 10000 ICMP ping packets with a payload of 100 bytes. Transfer time 0.84 sec, no packets lost. Required software ================= Tun --- Both ggsn and sgsnemu uses the tun package. You need at least tun version 1.1. With Linux tun is normally included from kernel version 2.4.7. To configure automatic loading: 1. Add the following line to /etc/modules.conf: alias char-major-10-200 tun 2. depmod -a Alternatively you can execute "modprobe tun" on the commandline. For Solaris the tun driver needs to be installed manually. For general information about tun see http://vtun.sourceforge.net/tun/ Gengetopt --------- Gengetopt is required if you want to change the options defined in the cmdline.ggo source file. You need at least gengetopt version 2.8. If you are just going to compile the programs you don't need gengetopt. To use gengetopt for the ggsn do the following: cd ggsn gengetopt < cmdline.ggo --conf-parser To use gengetopt for the sgsnemu do the following: cd sgsnemu gengetopt < cmdline.ggo --conf-parser For more information about gengetopt see http://www.gnu.org/software/gengetopt/gengetopt.html Compilation and Installation ============================ Setting up autotools -------------------- You do not need to perform this step if you are only going to compile the package: 1. Get version from somewhere: Script to extract version from configure.in 2. Copy the latest config.guess and config.sub from ftp://ftp.gnu.org/gnu/config 3. Run autoscan and copy configure.scan to configure.in 4. Add/edit the following lines in configure.in: - AC_INIT(openggsn, 0.70, jj@openggsn.org) - AC_CONFIG_SRCDIR([gtp/gtp.c]) - AM_CONFIG_HEADER([config.h]) - AC_PROG_LIBTOOL - AM_PROG_LIBTOOL - AM_INIT_AUTOMAKE() 5. libtoolize --automake --copy (ads copy of ltmain.sh) 6. aclocal 7. autoheader 8. automake --add-missing --copy (Ads copy of mkinstalldirs missing, install-sh, depcomp) 9. automake 10. autoconf The above will initialise the project to the current version of autotools (As installed in RedHat 8.0). See http://sources.redhat.com/autobook/autobook/autobook_25.html#SEC25 for details on autotools. Checking out from CVS --------------------- To download the latest source code from anonymous CVS: cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ggsn login cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ggsn co openggsn Or to download from developer CVS: export CVS_RSH=ssh cvs -z3 -d:ext:developername@cvs.sourceforge.net:/cvsroot/ggsn co openggsn Both the above sets of commands creates a new directory called openggsn. Compilation and installation ---------------------------- If compiling under Solaris you need to edit the following line in ggsn/Makefile.in and sgsnemu/Makefile.in: LDFLAGS = -Wl,--rpath -Wl,/usr/local/lib @EXEC_LDFLAGS@ should be changed to: LDFLAGS = -lresolv -lsocket -lnsl @EXEC_LDFLAGS@ Note that the above is not necessary on other platforms. Compilation and installation is performed by the following steps: 1. ./configure 2. make clean 3. cd gtp 4. make 5. make install (as root) 6. cd .. (Step 3 to 6 you only need to run the first time to install libgtp) 7. make 8. make install (as root) 9. Add /usr/local/lib to /etc/ld.so.conf 10. run ldconfig (Steps 9 and 10 are not required as path to libgtp is included in Makefile) Documentation can be converted to html by issuing: 1. txt2html -pm -tf README > README.html 2. txt2html -pm -tf NEWS > NEWS.html 3. txt2html -pm -tf ChangeLog > ChangeLog.html 4. man2htm ggsn.8 > ggsn.html 5. man2htm sgsnemu.8 > sgsnemu.html Installation from binary ------------------------ 1. rpm -i openggsn-.rpm This will install binaries, man pages, configuration files as well as a Sys V init script for the ggsn. Running ggsn ============ Use ggsn -h for a list of available options. All options available on the command line can also be given in a configuration file. See examples/ggsn.conf for the format of this file. Start the ggsn as root using the command: ggsn -c examples/ggsn.conf --fg -l 10.0.0.40 --net 192.168.0.0/24 --dynip 192.168.0.0/24 First a tun network interface will be created. In the above example the network interface address is 192.168.0.0 and the mask is 255.255.255.0. You can check that this interface is up by using ifconfig. After tun has been successfully established the ggsn will wait for GTP create PDP context requests on the local interface 10.0.0.40. Currently all requests are accepted, and no password, username or APN validation is performed. When receiving a create PDP context request a dynamic IP address will be allocated from the address pool determined by --dynip. In the above example the first allocated address will be 192.168.0.1, followed by 192.168.0.2 and so on. The request is confirmed by sending a create PDP context response message to the peer (SGSN). Now IP packets will be forwarded between the tun network interface and the established GTP tunnel. In order to allow users to access the external network routing needs to be set up. If private addresses are used you need to configure network address translation. See the Linux Networking HOWTO for details. Remember to enable routing: echo 1 > /proc/sys/net/ipv4/ip_forward If you installed using a binary RPM package it is possible to start ggsn by using the Sys 5 script: /etc/init.d/ggsn start Running sgsnemu =============== Use sgsnemu -h for a list of available options. All options available on the command line can also be given in a configuration file. See examples/sgsnemu.conf for the format of this file. If you want to test a GRX roaming connection you will need to do the following: 1. Install sgsnemu on a Linux Box. See under installation above. 2. Connect your Linux box with sgsnemu installed to the GPRS core network. Use the same LAN switch as the one your SGSN is connected to. You also need a free IP address that can be used by sgsnemu. 3. You need to configure networking in terms of interface address, subnet mask and default route. See the Linux Networking HOWTO for details. 4. Launch sgsnemu with something like: sgsnemu --listen 10.0.0.50 --remote 10.0.0.40 --dns 10.20.38.51 --timelimit 10 --contexts 0 sgsnemu will print something like the following on the screen:

  Using DNS server:      10.20.38.51 (10.20.38.51)
  Local IP address is:   10.0.0.50 (10.0.0.50)
  Remote IP address is:  10.0.0.40 (10.0.0.40)
  IMSI is:               240011234567890 (0x98765432110042)
  Using APN:             internet
  Using MSISDN:          46702123456

  Initialising GTP library
  OpenGGSN[1823]: GTP: gtp_newgsn() started
  Done initialising GTP library

  Sending off echo request
  Waiting for response from ggsn........

  Received echo response. Cause value: 0

This is quite good. It means that you managed to send off an echo request to a remote GGSN, and it was friendly enough to answer you. If you did not get an echo response it means that something is wrong either with your setup OR with the GRX connection OR with your roaming partners connection. If the above went well you might want to try to establish a PDP context to the remote GGSN. Note that you should be careful when establishing PDP contexts using sgsnemu as each established PDP context will result in a Charge Detail Record (CDR) being generated by the GGSN. You should use real IMSI and MSISDN from a valid test SIM card. Otherwise some poor customer might get charged for your testing. Also note that you are establishing a connection to the Gi network, so please be carefull not to route internet traffic onto the GPRS core network! Assuming you know what you are doing: sgsnemu --listen 10.0.0.50 --remote 10.0.0.40 --dns 10.20.38.51 --timelimit 10 --contexts 1 --apn internet --imsi 240011234567890 --msisdn 46702123456 --createif --defaultroute sgsnemu will print something like the following on the screen:

  Using DNS server:      10.20.38.51 (10.20.38.51)
  Local IP address is:   10.0.0.50 (10.0.0.50)
  Remote IP address is:  10.0.0.40 (10.0.0.40)
  IMSI is:               240011234567890 (0x98765432110042)
  Using APN:             internet
  Using MSISDN:          46702123456

  Initialising GTP library
  OpenGGSN[1838]: GTP: gtp_newgsn() started
  Done initialising GTP library

  Sending off echo request
  Setting up PDP context #0
  Waiting for response from ggsn........

  Received echo response. Cause value: 0
  Received create PDP context response. Cause value: 128
  Setting up interface and routing
  /sbin/ifconfig tun0 192.168.0.1
  /sbin/route add -net 192.168.0.0 netmask 255.255.255.0 gw 192.168.0.1

Now a context is established to the remote GGSN. The IP address of the context is 192.168.0.1. You should be able to ping a known address on the Gi network of the roaming partner. You should even be able to do web browsing through the PDP context. Note however that you probably need to adjust your routing tables, so that you make sure that all GRX traffic is routed to the GPRS core network and everything else through the PDP context. The proper way to do this is to use policy routing. Also note that you are effectively connecting the same computer to both the Gn and Gi network, so please be carefull not to route internet traffic onto the GPRS core network and please protect yourself against hackers! For this reason it is advised to always use --contexts 0 when testing a live network. After --timelimit seconds the PDP context is disconnected with the following messages from sgsnemu:

  Disconnecting PDP context #0
  Received delete PDP context response. Cause value: 128
  Deleting tun interface

openggsn-0.92/README.FreeBSD000066400000000000000000000017341262356443100153700ustar00rootroot00000000000000OpenGGSN/FreeBSD notes FreeBSD support is experimental, please test and report bugs. The FreeBSD port is tested on FreeBSD 4.x, but may also work on 5.x series. 1. Compiling You will need GNU make installed, standard BSD make will not work. Everything should be straight-forward, run ./configure then gmake. 2. Kernel tuning Your kernel needs to include tun driver (GENERIC kernel does), make sure your kernel config file contains the line pseudo-device tun or load the tun kernel module manually by issuing "kldload if_tun" as root. OpenGGSN doesn't try to load the module itself right now. Also make sure your kernel has IP Forwarding enabled (it is not by default). Add the line gateway_enable=yes to your /etc/rc.conf or manually issue "sysctl net.inet.ip.forwarding=1" 3. Known problems After ggsn is started, you have to manually add the route for your address pool: route add 192.168.0.0 -netmask 255.255.255.0 -iface tun0 -- Pavel Andreev openggsn-0.92/README.MacOSX000066400000000000000000000015141262356443100152440ustar00rootroot00000000000000OpenGGSN/Mac OS X notes Mac OS X support is experimental, please test and report bugs. The Mac OS X port is tested on Mac OS X 10.3.5, but may also work on other versions. 1. Compiling Everything should be straight-forward. Create a separate build directory, run ../openggsn/configure and then make. If you have problems with configure, execute $ aclocal && automake && autoheader && autoconf and then again configure and make. 2. Kernel extensions You need to add a tunnel kernel extension to your kernel. The one I've used is available at http://www-user.rhrk.uni-kl.de/~nissler/tuntap/ but there are others. If you want to compile that from sources (instead of using the binary installer), you will also need kernel sources from opensource.apple.com. 3. Known problems None. -- Pekka Nikander openggsn-0.92/README.Solaris000066400000000000000000000015621262356443100155710ustar00rootroot00000000000000OpenGGSN/Solaris notes ====================== Solaris support is experimental, please test and report bugs. The Solaris port is tested under Solaris 8. Compiling --------- You need to edit the following line in ggsn/Makefile.in and sgsnemu/Makefile.in: LDFLAGS = -Wl,--rpath -Wl,/usr/local/lib @EXEC_LDFLAGS@ should be changed to: LDFLAGS = -lresolv -lsocket -lnsl @EXEC_LDFLAGS@ After this you install by the following commands: ./configure make make install TUN --- You might or might not need to install the tun driver manually. For general information about tun see http://vtun.sourceforge.net/tun/ Known problems -------------- Currently multiple IP addresses on the same network interface is not implemented for Solaris. Currently routing table manipulation is not implemented for Solaris. You have to set the routes manually after you start ggsn or sgsnemu. openggsn-0.92/acinclude.m4000066400000000000000000000012011262356443100154550ustar00rootroot00000000000000dnl Available from the GNU Autoconf Macro Archive at: dnl http://www.gnu.org/software/ac-archive/htmldoc/adl_func_getopt_long.html dnl AC_DEFUN([adl_FUNC_GETOPT_LONG], [AC_PREREQ(2.49)dnl # clean out junk possibly left behind by a previous configuration rm -f lib/getopt.h # Check for getopt_long support AC_CHECK_HEADERS([getopt.h]) AC_CHECK_FUNCS([getopt_long],, [# FreeBSD has a gnugetopt library for this AC_CHECK_LIB([gnugetopt],[getopt_long],[AC_DEFINE([HAVE_GETOPT_LONG])], [# use the GNU replacement AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1) AC_CONFIG_LINKS([lib/getopt.h:lib/gnugetopt.h])])])]) openggsn-0.92/configure.ac000066400000000000000000000056651262356443100155740ustar00rootroot00000000000000# Process this file with autoconf to produce a configure script. AC_INIT(openggsn, 0.92, laforge@gnumonks.org) AC_CONFIG_SRCDIR([gtp/gtp.c]) AM_CONFIG_HEADER([config.h]) #AC_CONFIG_HEADER([config.h]) AC_CANONICAL_SYSTEM dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) # Checks for programs. AC_PROG_CC AC_PROG_INSTALL AC_PROG_AWK AC_PROG_CPP AC_PROG_CXX LT_INIT AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_LIBOBJ_DIR([lib]) AC_ARG_ENABLE(static-exec, [ --enable-static-exec Enable static linking of executables], [ EXEC_LDFLAGS="-all-static"]) AC_SUBST(EXEC_LDFLAGS) case "${host}" in i*86-*-linux-gnu*) EXEC_LDADD="" ;; *solaris*) EXEC_LDADD="-lresolv -lsocket -lnsl" ;; esac AC_SUBST(EXEC_LDADD) # Checks for libraries. # FIXME: Replace `main' with a function in `-le': #AC_CHECK_LIB([e], [main]) # FIXME: Replace `main' with a function in `-lgtp': #AC_CHECK_LIB([gtp], [main]) # FIXME: Replace `main' with a function in `-links': #AC_CHECK_LIB([inks], [main]) # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h]) # Check for if header AC_CHECK_HEADERS([linux/if.h net/if.h]) # Check for tun header AC_CHECK_HEADERS([linux/if_tun.h net/if_tun.h]) # Check for netlink and rtnetlink headers AC_CHECK_HEADERS([linux/netlink.h linux/rtnetlink.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_HEADER_TIME # check for ifaliasreq AC_MSG_CHECKING(whether struct ifaliasreq exist) AH_TEMPLATE(HAVE_IFALIASREQ) AC_EGREP_HEADER(ifaliasreq, net/if.h, [AC_MSG_RESULT(yes) AC_DEFINE([HAVE_IFALIASREQ])], AC_MSG_RESULT(no)) # check for ifreq.ifru_netmask AC_MSG_CHECKING(whether struct ifreq.ifru_netmask exist) AH_TEMPLATE(HAVE_IFREQ_IFRU_NETMASK) AC_EGREP_HEADER(ifru_netmask, linux/if.h, [AC_MSG_RESULT(yes) AC_DEFINE([HAVE_IFREQ_IFRU_NETMASK])], AC_MSG_RESULT(no)) # check for rt_msghdr AC_MSG_CHECKING(whether struct rt_msghdr exist) AH_TEMPLATE(HAVE_RT_MSGHDR) AC_EGREP_HEADER(rt_msghdr, net/route.h, [AC_MSG_RESULT(yes) AC_DEFINE([HAVE_RT_MSGHDR])], AC_MSG_RESULT(no)) # Checks for library functions. AC_PROG_GCC_TRADITIONAL # AC_FUNC_MALLOC # AC_FUNC_MEMCMP AC_CHECK_FUNCS([gethostbyname inet_ntoa memset select socket strdup strerror strtol]) AC_CHECK_FUNCS(inet_aton inet_addr, break) # check for getopt in standard library adl_FUNC_GETOPT_LONG AM_INIT_AUTOMAKE() PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) AC_CONFIG_FILES([Makefile doc/Makefile ggsn/Makefile gtp/Makefile lib/Makefile intl/Makefile po/Makefile sgsnemu/Makefile tests/Makefile libgtp.pc openggsn.spec]) AC_OUTPUT openggsn-0.92/contrib/000077500000000000000000000000001262356443100147325ustar00rootroot00000000000000openggsn-0.92/contrib/openggsn.service000066400000000000000000000002751262356443100201400ustar00rootroot00000000000000[Unit] Description=OpenGGSN [Service] Type=simple Restart=always ExecStart=/usr/bin/ggsn -c /etc/ggsn.conf -f RestartSec=2 RestartPreventExitStatus=1 [Install] WantedBy=multi-user.target openggsn-0.92/debian/000077500000000000000000000000001262356443100145145ustar00rootroot00000000000000openggsn-0.92/debian/changelog000066400000000000000000000014351262356443100163710ustar00rootroot00000000000000openggsn (0.91+git34) UNRELEASED; urgency=medium * Non-maintainer upload. -- Holger Hans Peter Freyther Tue, 23 Sep 2014 16:38:32 +0200 openggsn (0.91+git33) precise; urgency=low * Fix init script. -- Eric Butler Fri, 24 Aug 2012 21:15:32 -0700 openggsn (0.91+git33) precise; urgency=low * Update package for Ubuntu Precise. -- Eric Butler Tue, 14 Aug 2012 16:48:59 -0700 openggsn (0.91-2) unstable; urgency=low * Switch to source/format (git) -- Harald Welte Tue, 10 May 2011 17:31:37 +0200 openggsn (0.91-1) unstable; urgency=low * Initial release (Closes: #nnnn) -- Harald Welte Tue, 24 Aug 2010 11:23:40 +0200 openggsn-0.92/debian/compat000066400000000000000000000000021262356443100157120ustar00rootroot000000000000009 openggsn-0.92/debian/control000066400000000000000000000021701262356443100161170ustar00rootroot00000000000000Source: openggsn Section: net Priority: optional Maintainer: Harald Welte Build-Depends: debhelper (>= 9), autotools-dev, pkg-config, libdpkg-perl, git, dh-autoreconf, libosmocore-dev (>= 0.8.0) Standards-Version: 3.9.6 Homepage: http://sourceforge.net/projects/ggsn/ Vcs-Git: git://ggsn.git.sourceforge.net/gitroot/ggsn/ggsn Vcs-Browser: http://ggsn.git.sourceforge.net/git/gitweb.cgi?p=ggsn/ggsn;a=summary Package: openggsn Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Gateway GPRS Support Node Package: libgtp0 Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: library implementing the GTP protocol between SGSN and GGSN Package: libgtp0-dev Depends: ${misc:Depends}, libgtp0 (= ${binary:Version}) Multi-Arch: same Architecture: any Section: libdevel Description: Development files for libgtp Package: openggsn-dbg Section: debug Architecture: any Priority: extra Depends: ${shlibs:Depends}, ${misc:Depends}, libgtp0 (= ${binary:Version}), openggsn (= ${binary:Version}) Multi-Arch: same Description: Debug symbols for OpenGGSN openggsn-0.92/debian/copyright000066400000000000000000000027741262356443100164610ustar00rootroot00000000000000This work was packaged for Debian by: Harald Welte on Tue, 24 Aug 2010 11:23:40 +0200 It was downloaded from: http://sourceforge.net/projects/ggsn Upstream Author(s): Jens Jakobsen Harald Welte Copyright: Copyright (C) 2002 Mondru AB Copyright (C) 2010 Harald Welte License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This package 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, see On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". The Debian packaging is: Copyright (C) 2010 Harald Welte 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. # Please also look if there are files or directories which have a # different copyright/license attached and list them here. openggsn-0.92/debian/docs000066400000000000000000000000141262356443100153620ustar00rootroot00000000000000NEWS README openggsn-0.92/debian/libgtp0-dev.install000066400000000000000000000001311262356443100202140ustar00rootroot00000000000000usr/include/* usr/lib/*/lib*.a usr/lib/*/lib*.so usr/lib/*/lib*.la usr/lib/*/pkgconfig/* openggsn-0.92/debian/libgtp0.install000066400000000000000000000000241262356443100174410ustar00rootroot00000000000000usr/lib/*/lib*.so.* openggsn-0.92/debian/openggsn.examples000066400000000000000000000000511262356443100200700ustar00rootroot00000000000000examples/ggsn.conf examples/sgsnemu.conf openggsn-0.92/debian/openggsn.init000077500000000000000000000104141262356443100172240ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: openggsn # Required-Start: $network $local_fs $remote_fs # Required-Stop: $network $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Gateway GPRS Support Node # Description: Gateway GPRS Support Node ### END INIT INFO # Author: Harald Welte # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="OpenGGSN Gateway GPRS Support Node" NAME=ggsn DAEMON=/usr/bin/ggsn DAEMON_ARGS="" # Arguments to run the daemon with PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/openggsn # Exit if the package is not installed [ -x $DAEMON ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/openggsn ] && . /etc/default/openggsn # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions DAEMON_ARGS="$DAEMON_ARGS" # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 # Check for runtime directory of nonvolatile data if [ ! -d /var/lib/ggsn ]; then mkdir /var/lib/ggsn fi # Check for GTP restart counter if [ ! -f /var/lib/ggsn/gsn_restart ]; then echo 0 > /var/lib/ggsn/gsn_restart fi start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac : openggsn-0.92/debian/openggsn.install000066400000000000000000000000651262356443100177250ustar00rootroot00000000000000/usr/bin/ggsn /usr/bin/sgsnemu /usr/share/man/man8/* openggsn-0.92/debian/rules000077500000000000000000000013741262356443100156010ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # # Modified to make a template file for a multi-binary package with separated # build-arch and build-indep targets by Bill Allombert 2001 # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This has to be exported to make some magic below work. #export DH_OPTIONS export DEB_BUILD_HARDENING=1 %: dh $@ --with autoreconf override_dh_strip: dh_strip --dbg-package=openggsn-dbg openggsn-0.92/debian/source/000077500000000000000000000000001262356443100160145ustar00rootroot00000000000000openggsn-0.92/debian/source/format000066400000000000000000000000151262356443100172230ustar00rootroot000000000000003.0 (native) openggsn-0.92/doc/000077500000000000000000000000001262356443100140375ustar00rootroot00000000000000openggsn-0.92/doc/Compliance.html000066400000000000000000001065741262356443100170140ustar00rootroot00000000000000 Compliance

Protocol Compliance List

OpenGGSN supports both GTP0 (GSM 09.60) and GTP1 (3GPP 29.060). In the following tables the support of each individual message type is detailed. The numbers before each feature indicates the relevant section in the standard.

GSM 09.60 (GTPv0)

Feature
gtplib
ggsn
sgsnemu
notes
7.4 Path Management Messages




7.4.1 Echo Request Supported
Supported Supported
7.4.2 Echo Response
Supported Supported Supported
7.4.3 Version Not Supported
Supported Supported Supported
7.5 Tunnel Management Messages




7.5.1 Create PDP Context Request
Supported Supported Supported
7.5.2 Create PDP Context Response Supported Supported Supported
7.5.3 Update PDP Context Request Supported Supported Not applicable
7.5.4 Update PDP Context Response Supported Supported Not applicable
7.5.5 Delete PDP Context Request Supported Supported Supported
7.5.6 Delete PDP Context Response Supported Supported Supported
7.5.7 Create AA PDP Context Request Unsupported Unsupported Unsupported
7.5.8 Create AA PDP Context Response
Unsupported Unsupported Unsupported
7.5.9 Delete AA PDP Context Request Unsupported Unsupported Unsupported
7.5.10 Delete AA PDP Context Response Unsupported Unsupported Unsupported
7.5.11 Error Indication
Supported Supported Supported
7.5.12 PDU Notification Request
Unsupported Unsupported Unsupported
7.5.13 PDU Notification Response Unsupported Unsupported Unsupported
7.5.14 PDU Notification Reject Request Unsupported Unsupported Unsupported
7.5.15 PDU Notification Reject Response Unsupported Unsupported Unsupported
7.6 Location Management Messages




7.6.1 Send Routeing Information for GPRS Request
Unsupported Unsupported Not applicable
7.6.2 Send Routeing Information for GPRS Response Unsupported Unsupported Not applicable
7.6.3 Failure Report Request
Unsupported Unsupported Not applicable
7.6.3 Failure Report Response Unsupported Unsupported Not applicable
7.6.5 Note MS GPRS Present Request
Unsupported Unsupported Not applicable
7.6.6 Note MS GPRS Present Response Unsupported Unsupported Not applicable
7.5 Mobility Management Messages




7.5.1 Identification Request
Unsupported Not applicable
Not applicable
7.5.2 Identification Response Unsupported Not applicable Not applicable
7.5.3 SGSN Context Request
Unsupported Not applicable Not applicable
7.5.4 SGSN Context Response Unsupported Not applicable Not applicable
7.5.5 SGSN Context Acknowledge Unsupported Not applicable Not applicable

3GPP 29.060 (GTPv1)

Feature
gtplib
ggsn
sgsnemu
notes
7.2 Path Management Messages




7.2.1 Echo Request Supported
Supported Supported
7.2.2 Echo Response
Supported Supported Supported
7.2.3 Version Not Supported
Supported Supported Supported
7.2.4 Supported Extension Headers Notification
Supported Supported Supported
7.3 Tunnel Management Messages




7.3.1 Create PDP Context Request
Supported Supported Supported 1
7.3.2 Create PDP Context Response Supported Supported Supported
7.3.3 Update PDP Context Request Supported Supported Not applicable 1
7.3.4 Update PDP Context Response Supported Supported Not applicable
7.3.5 Delete PDP Context Request Supported Supported Supported
7.3.6 Delete PDP Context Response Supported Supported Supported
7.3.7 Error Indication
Supported Supported Supported
7.3.8 PDU Notification Request
Unsupported Unsupported Unsupported
7.3.9 PDU Notification Response Unsupported Unsupported Unsupported
7.3.10 PDU Notification Reject Request Unsupported Unsupported Unsupported
7.3.10 PDU Notification Reject Response Unsupported Unsupported Unsupported
7.4 Location Management Messages




7.4.1 Send Routeing Information for GPRS Request
Unsupported Unsupported Not applicable
7.4.2 Send Routeing Information for GPRS Response Unsupported Unsupported Not applicable
7.4.3 Failure Report Request
Unsupported Unsupported Not applicable
7.4.3 Failure Report Response Unsupported Unsupported Not applicable
7.4.5 Note MS GPRS Present Request
Unsupported Unsupported Not applicable
7.4.6 Note MS GPRS Present Response Unsupported Unsupported Not applicable
7.5 Mobility Management Messages




7.5.1 Identification Request
Unsupported Not applicable
Not applicable
7.5.2 Identification Response Unsupported Not applicable Not applicable
7.5.3 SGSN Context Request
Unsupported Not applicable Not applicable
7.5.4 SGSN Context Response Unsupported Not applicable Not applicable
7.5.5 SGSN Context Acknowledge Unsupported Not applicable Not applicable
7.5.6 Forward Relocation Request
Unsupported Not applicable Not applicable
7.5.7 Forward Relocation Response Unsupported Not applicable Not applicable
7.5.8 Forward Relocation Complete Unsupported Not applicable Not applicable
7.5.9 Relocation Cancel Request
Unsupported Not applicable Not applicable
7.5.10 Relocation Cancel Response Unsupported Not applicable Not applicable
7.5.11 Forward Relocation Complete Acknowledge
Unsupported Not applicable Not applicable
7.5.12 Forward SRNS Context Acknowledge Unsupported Not applicable Not applicable
7.5.13 Forward SRNS Context
Unsupported Not applicable Not applicable

Notes

1) The "Secondary PDP Context Activation Procedure" is not supported.




openggsn-0.92/doc/Makefile.am000066400000000000000000000001241262356443100160700ustar00rootroot00000000000000man_MANS = ggsn.8 sgsnemu.8 man_aux = $(man_MANS:.1=.x) EXTRA_DIST = $(man_MANS) openggsn-0.92/doc/ggsn.8000066400000000000000000000146121262356443100150720ustar00rootroot00000000000000 .\" * OpenGGSN - Gateway GPRS Support Node .\" * Copyright (C) 2002, 2003 Mondru AB. .\" * .\" * The contents of this file may be used under the terms of the GNU .\" * General Public License Version 2, provided that the above copyright .\" * notice and this permission notice is included in all copies or .\" * substantial portions of the software. .\" * .\" * The initial developer of the original code is .\" * Jens Jakobsen .\" * .\" * Contributor(s): .\" * .\" Manual page for ggsn .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH ggsn 8 "July 2003" .SH NAME ggsn \- Gateway GPRS Support Node. .SH SYNOPSIS .B ggsn \-\-help .B ggsn \-\-version .B ggsn [ .BI \-\-fg ] [ .BI \-\-debug ] [ .BI \-\-conf " file" ] [ .BI \-\-pidfile " file" ] [ .BI \-\-statedir " file" ] [ .BI \-\-listen " host" ] [ .BI \-\-net " net" ] [ .BI \-\-ipup " script" ] [ .BI \-\-ipdown " script" ] [ .BI \-\-dynip " net" ] [ .BI \-\-statip " net" ] [ .BI \-\-pcodns1 " host" ] [ .BI \-\-pcodns2 " host" ] [ .BI \-\-timelimit " seconds" ] .SH DESCRIPTION .B ggsn is part of the .B OpenGGSN project, and implements a Gateway GPRS Support Node. It is used by mobile operators as the interface between the Internet and the rest of the mobile network infrastructure. The GPRS functionality and protocols has been standardised by the Third Generation Partnership Project (3GPP). According to the 3GPP specifications a GGSN has two interfaces: The Gn/Gp interface and the Gi interface. The Gn/Gp interface can be seen as the downlink interface of the GGSN. It is used for communicating with the Serving GPRS Support Node (SGSN) which again interfaces to the radio access network. The Gn/Gp interface uses the GPRS tunneling protocol (GTP). User data packets (typically IP packets) are tunneled over GTP, which again uses UDP over IP. The other interface can be thought of as the uplink interface, and interfaces the GGSN to an external data network. Gi is most often an interface to the Internet. .B ggsn uses the .B TUN/TAP driver for the Gi interface. A tun network interface is established when the .B ggsn is started. .B ggsn will accept incoming connections from mobile stations through the radio access network and the SGSN. When a connection request is received the ggsn will allocate a dynamic IP address for the mobile station, and allow the mobile station to access the Gi interface. Connections are terminated by either the mobile station or the SGSN. Runtime errors are reported using the Osmocom logging framework. Typically .B ggsn will be deployed with two Ethernet interfaces. One for the Gn/Gp interface, and one for the Gi interface. Policy routing and firewall rules should be used in order to separate Gi traffic from Gn/Gp traffic. .SH OPTIONS .TP .BI --help Print help and exit. .TP .BI --version Print version and exit. .TP .BI --fg Run in foreground (default = off) .TP .BI --debug Run in debug mode (default = off) .TP .BI --conf " file" Read configuration .I file (default = /etc/ggsn.conf) where each line corresponds to one command line option, but with the leading '--' removed. Command line options override the options given in the configuration file. .TP .BI --pidfile " file" Filename of process id .I file (default = /var/run/ggsn.pid) .TP .BI --statedir " path" .I path to directory of nonvolatile data (default = /var/lib/ggsn/) .TP .BI --listen " host" Local interface IP address to use for the Gn/Gp interface. This option must be specified. For security issues it is not possible to use INADDR_ANY. .TP .BI --net " net" Network address of the Gi interface (default = 192.168.0.0/24). The network address is set during initialisation when .B ggsn establishes a tun device for the Gi interface. .TP .BI --ipup " script" Script executed after the Gi tun network interface has been brought up. Executed with the following parameters: .TP .BI --ipdown " script" Script executed after the Gi tun network interface has been taken down. Executed with the following parameters: .TP .BI --dynip " net" Dynamic IP address pool. Specifies a pool of dynamic IP addresses. If this option is omitted the network address specified by the .BI --net option is used for dynamic IP address allocation. .TP .BI --pcodns1 " host" PCO DNS Server 1 (default = 0.0.0.0). PCO stands for Protocol Configuration options, and is part of the GPRS protocols. It is used to inform the mobile station about the DNS address to use for host name resolution. .TP .BI --pcodns2 " host" PCO DNS Server 2 (default = 0.0.0.0). PCO stands for Protocol Configuration options, and is part of the GPRS protocols. It is used to inform the mobile station about the DNS address to use for host name resolution. .TP .BI --timelimit " seconds" Exit .b ggsn after \fIseconds\fP. Used for debugging. .SH FILES .I /etc/ggsn.conf .RS The configuration file for .B ggsn. .RE .I /var/run/ggsn.pid .RS Process ID file. .RE .I /var/lib/ggsn .RS Directory holding nonvolatile data. .RE .SH BUGS Report all bugs to the OpenGGSN bug tracking list at .I http://sourceforge.net/projects/ggsn/ .B ggsn has very limited management support. Currently both SNMP as well as billing mechanisms are missing. .SH "SEE ALSO" .BR sgsnemu (8) .SH NOTES .LP Besides the long options documented in this man page .B ggsn also accepts a number of short options with the same functionality. Use .B ggsn --help for a full list of all the available options. The TUN/TAP driver is required for proper operation of .B ggsn. For linux kernels later than 2.4.7 the TUN/TAP driver is included in the kernel, but typically needs to be loaded manually with .B modprobe tun. For automatic loading the line .B alias char-major-10-200 tun can be added to .B /etc/modules.conf. For other platforms see .I http://vtun.sourceforge.net/tun/ for information on how to install and configure the tun driver. .B ggsn uses the GPRS Tunneling Protocol (GTP) as specified by the Third Generation Partnership Project (3GPP). 3GPP protocols specifications can be found at .I http://www.3gpp.org .SH COPYRIGHT Copyright (C) 2002, 2003 by Mondru AB. The contents of this file may be used under the terms of the GNU General Public License Version 2, provided that the above copyright notice and this permission notice is included in all copies or substantial portions of the software. .SH AUTHORS Jens Jakobsen openggsn-0.92/doc/sgsnemu.8000066400000000000000000000223111262356443100156100ustar00rootroot00000000000000 .\" * OpenGGSN - Gateway GPRS Support Node .\" * Copyright (C) 2002, 2003 Mondru AB. .\" * .\" * The contents of this file may be used under the terms of the GNU .\" * General Public License Version 2, provided that the above copyright .\" * notice and this permission notice is included in all copies or .\" * substantial portions of the software. .\" * .\" * The initial developer of the original code is .\" * Jens Jakobsen .\" * .\" * Contributor(s): .\" * .\" Manual page for ggsn .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH sgsnemu 8 "May 2004" .SH NAME sgsnemu \- Serving GPRS Support Node Emulator .SH SYNOPSIS .B sgsnemu \-\-help .B sgsnemu \-\-version .B sgsnemu [ .BI \-\-debug ] [ .BI \-\-conf " file" ] [ .BI \-\-pidfile " file" ] [ .BI \-\-statedir " file" ] [ .BI \-\-dns " host" ] [ .BI \-\-listen " host" ] [ .BI \-\-remote " host" ] [ .BI \-\-contexts " num" ] [ .BI \-\-timelimit " seconds" ] [ .BI \-\-gtpversion " version" ] [ .BI \-\-apn " apn" ] [ .BI \-\-selmode " selmode" ] [ .BI \-\-imsi " imsi" ] [ .BI \-\-nsapi " nsapi" ] [ .BI \-\-msisdn " msisdn" ] [ .BI \-\-qos " qos" ] [ .BI \-\-charging " charging" ] [ .BI \-\-uid " uid" ] [ .BI \-\-pwd " pwd" ] [ .BI \-\-createif ] [ .BI \-\-net " net" ] [ .BI \-\-defaultroute ] [ .BI \-\-ipup " script" ] [ .BI \-\-ipdown " script" ] [ .BI \-\-pinghost " host" ] [ .BI \-\-pingrate " num" ] [ .BI \-\-pingsize " num" ] [ .BI \-\-pingcount " num" ] .SH DESCRIPTION .B sgsnemu is part of the .B OpenGGSN project, and implements a Serving GPRS Support Node (SGSN) emulator. It can be used for testing Gateway GPRS Support Nodes (GGSNs), GPRS core networks as well as GPRS roaming connections. The GPRS functionality and protocols has been standardised by the Third Generation Partnership Project (3GPP). According to the 3GPP specifications an SGSN has several interfaces. .B sgsnemu implements the Gn/Gp interface which is used towards GGSNs. The Gn/Gp interface can be seen as the uplink interface of the SGSN. It is used for communicating with a GGSN which is typically connected to the Internet. The Gn/Gp interface uses the GPRS tunneling protocol (GTP). User data packets (typically IP packets) are tunneled over GTP, which again uses UDP over IP. .B sgsnemu will establish a number of connections to the GGSN. An internal ping facility will transmit ping requests through the established connections. Alternatively a local network interface can be created. In this case .B sgsnemu will forward packets between the local network interface and the established connections on the Gn/Gp interface. .B sgsnemu uses the .B TUN/TAP driver for the local interface. A tun network interface is established for each connection established to the GGSN. Runtime errors are reported using the Osmocom logging framework. .SH OPTIONS .TP .BI --help Print help and exit. .TP .BI --version Print version and exit. .TP .BI --debug Run in debug mode (default = off) .TP .BI --conf " file" Read configuration .I file where each line corresponds to one command line option, but with the leading '--' removed. Command line options override the options given in the configuration file. .TP .BI --pidfile " file" Filename of process id .I file (default = ./sgsnemu.pid) .TP .BI --statedir " path" .I path to directory of nonvolatile data (default = ./) .TP .BI --dns " host" DNS server to use for APN lookups. If omitted the default system DNS configuration will be used. .TP .BI --listen " host" Local interface IP address to use for the Gn/Gp interface. If omitted .B sgsnemu will listen to any IP address. .TP .BI --remote " host" GGSN .I host to connect to. If DNS is setup correctly it should be possible to specify the access point name (APN) as the remote host. .TP .BI --contexts " num" Number of contexts to establish. (default = 1). For multiple contexts the the first context is established using imsi + 0 and msisdn + 0. The second context is established using imsi + 1 and msisdn + 1. The third.... .TP .BI --timelimit " seconds" Exit .B sgsnemu after .I seconds. When using the ping facility .B sgsnemu will also exit after .B --pingcount packets has been transmitted. .TP .BI --gtpversion " version" .I version of GTP to use when establishing a context (default = 1). If not able to establish a GTPv1 context sgsnemu will fall back to using GTPv0. .TP .BI --apn " apn" .I apn to use when connecting to the GGSN (default = internet). APN is an abbreviation of Access Point Name. .TP .BI --selmode " selmode" Selection mode to use when connecting to the GGSN (default = 0x01). The encoding of the selection mode is as follows: 0: MS or network provided APN, subscribed verified, 1: MS provided APN, subscription not verified, 2: Network provided APN, subscription not verified. .TP .BI --imsi " imsi" .I imsi to use when connecting to the GGSN (default = 240010123456789). IMSI is an abbreviation of International Mobile Subscriber Identity. Must be exactly 15 digits. See the .I contexts option for the the use of the .I imsi option with multiple contexts. .TP .BI --nsapi " nsapi" .I nsapi to use when connecting to the GGSN (default = 0). Must be between 0 and 15. .TP .BI --msisdn " msisdn" .I msisdn to use when connecting to the GGSN (default = 46702123456). MSISDN is an abbreviation of International Mobile Integrated Services Digital Network. Effectly a phone number in international format without the leading 00 or 011. See the .I contexts option for the the use of the .I msisdn option with multiple contexts. .TP .BI --qos " qos" .I qos to use when connecting to the GGSN (default = 0x0b921f). QoS is an abbreviation of Quality of Service. See 3GPP specification 09.60 for the format of this string. .TP .BI --charging " charging" Charging characteristics to use when connecting to the GGSN (default = 0x0800). 0x0800 = Normal, 0x0400 = Prepaid, 0x0200 = Flat rate, 0x0100 = Hot billing. See 3GPP specification 32.015 for the format of this field. .TP .BI --uid " uid" User ID to send to GGSN as protocol configuration option. .TP .BI --pwd " pws" Password to send to GGSN as protocol configuration option. .TP .BI --createif Create a local tun interface. This interface will be used for forwarding packets to and from the Gn/Gp interface. Use with caution as the Gn/Gp interface is normally be routed to the Internet by the GGSN. Only one interface will be created, even if several contexts are established. The interface will be given an IP address for each established context, or the address can be specified with the .I net option. .TP .BI --net " net" Network address of the local interface. The .I net option is only valid when the .I createif option is used. The .I net option is given in cidr format (net/mask bits). If the .I net option omitted an IP address is allocated for each context established. .TP .BI --defaultroute Create a defaultroute to the local tun interface. .TP .BI --ipup " script" Script executed after the tun network interface has been brought up. Executed with the following parameters: .TP .BI --ipdown " script" Script executed after the tun network interface has been taken down. Executed with the following parameters: .TP .BI --pinghost " host" Ping .I host through the Gn/GP interface. Ping statistics are reported much like done by the original ping program. Use this facility for performance test of GGSNs. .TP .BI --pingrate " num" Number of ping requests per second (default = 1) .TP .BI --pingsize " num" The size of each ping requests in octets (default = 56) .TP .BI --pingcount " num" Number of ping requests to send (default = 0). A value of 0 (zero) indicates infinite. .TP .BI --pingquiet Do not print information for each packet received (default = off). Is quite usefull for high pingrates. .SH FILES .I sgsnemu.conf .RS The configuration file for .B sgsnemu. .RE .I .sgsnemu.pid .RS Process ID file. .RE .I ./ .RS Directory holding nonvolatile data. .RE .SH BUGS Report all bugs to the OpenGGSN bug tracking list at .I http://sourceforge.net/projects/sgsnemu/ .SH "SEE ALSO" .BR ggsn (8) .SH NOTES .LP Besides the long options documented in this man page .B sgsnemu also accepts a number of short options with the same functionality. Use .B sgsnemu --help for a full list of all the available options. The TUN/TAP driver is required for proper operation of .B sgsnemu. For linux kernels later than 2.4.7 the TUN/TAP driver is included in the kernel, but typically needs to be loaded manually with .B modprobe tun. For automatic loading the line .B alias char-major-10-200 tun can be added to .B /etc/modules.conf. For other platforms see .I http://vtun.sourceforge.net/tun/ for information on how to install and configure the tun driver. .B sgsnemu uses the GPRS Tunneling Protocol (GTP) as specified by the Third Generation Partnership Project (3GPP). 3GPP protocols specifications can be found at .I http://www.3gpp.org .SH COPYRIGHT Copyright (C) 2002, 2003, 2004 by Mondru AB. The contents of this file may be used under the terms of the GNU General Public License Version 2, provided that the above copyright notice and this permission notice is included in all copies or substantial portions of the software. .SH AUTHORS Jens Jakobsen openggsn-0.92/examples/000077500000000000000000000000001262356443100151105ustar00rootroot00000000000000openggsn-0.92/examples/firewall000077500000000000000000000030111262356443100166360ustar00rootroot00000000000000#!/bin/sh # # Firewall script for GGSN # # Uses $IFGN (eth0) as the Gn interface (Gn) and # $IFGI (eth1) as the Gi interface. # # SUMMARY # * All connections originating from GGSN are allowed. # * Incoming ssh, GTPv0 and GTPv1 is allowed on the Gn interface. # * Incoming ssh is allowed on the Gi interface. # * Forwarding is allowed to and from the Gi interface, but disallowed # to and from the Gn interface. # * Masquerede on Gi interface. IPTABLES="/sbin/iptables" IFGN="eth0" IFGI="eth1" $IPTABLES -P INPUT DROP $IPTABLES -P FORWARD ACCEPT $IPTABLES -P OUTPUT ACCEPT #Allow related and established on all interfaces (input) $IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT #Allow releated, established, GTP and ssh on $IFGN. Reject everything else. $IPTABLES -A INPUT -i $IFGN -p tcp -m tcp --dport 22 --syn -j ACCEPT $IPTABLES -A INPUT -i $IFGN -p udp -m udp --dport 2123 -j ACCEPT $IPTABLES -A INPUT -i $IFGN -p udp -m udp --dport 2152 -j ACCEPT $IPTABLES -A INPUT -i $IFGN -p udp -m udp --dport 3386 -j ACCEPT $IPTABLES -A INPUT -i $IFGN -j REJECT #Allow related, established and ssh. Drop everything else. $IPTABLES -A INPUT -i $IFGI -p tcp -m tcp --dport 22 --syn -j ACCEPT $IPTABLES -A INPUT -i $IFGI -j DROP # Masquerade everything going out on $IFGI $IPTABLES -t nat -A POSTROUTING -o $IFGI -j MASQUERADE #Allow everything on loopback interface. $IPTABLES -A INPUT -i lo -j ACCEPT # Drop everything to and from $IFGN (forward) $IPTABLES -A FORWARD -i $IFGN -j DROP $IPTABLES -A FORWARD -o $IFGN -j DROP openggsn-0.92/examples/ggsn.conf000066400000000000000000000044761262356443100167300ustar00rootroot00000000000000############################################################################## # # Sample ggsn configuration file # ############################################################################## # TAG: fg # Include this flag if process is to run in the foreground # #fg # TAG: debug # Include this flag to include debug information. #debug # TAG: conf # Configuration file to use. This file is the configuration file, # so changing this parameter in the configuration file does not make # sense. Use it on the command line instead. # TAG: pidfile # File to store information about the process id of the program. # The program must have write access to this file/directory. #pidfile /var/run/ggsn.pid # TAG: statedir # Directory to use for nonvolatile storage. # The program must have write access to this directory. #statedir /var/lib/ggsn/ # TAG: listen # Specifies the local IP address to listen to #listen 10.0.0.240 # TAG: net # IP network address of external packet data network # Used to set up network interface. #net 192.168.0.0/24 # TAG: ipup # Script executed after network interface has been brought up. # Executed with the following parameters: #ipup /etc/ggsn/ip-up # TAG: ipdown # Script executed after network interface has been taken down. # Executed with the following parameters: #ipdown /etc/ggsn/ip-down # TAG: dynip # Dynamic IP address pool. # Used for allocation of dynamic IP address when address is not given # by HLR. # If this option is not given then the net option is used as a substitute. #dynip 192.168.0.0/24 # TAG: statip # Use of this tag is currently UNSUPPORTED # Static IP address pool. # Used for allocation of static IP address by means of HLR. #statip 192.168.1.0/24 # TAG: pcodns1 # Protocol configuration option domain name system server 1. #pcodns1 0.0.0.0 # TAG: pcodns2 # Protocol configuration option domain name system server 2. #pcodns2 0.0.0.0 # TAG: timelimit # Exit after timelimit seconds. # Setting timelimit to zero will cause the program not to exit. #timelimit 0 # TAG: apn # Use of this tag is EXPERIMENTAL # Access point name to connect to when run in client mode. #apn internet # TAG: qos # Use of this tag is EXPERIMENTAL # Requested Quality of Service used when run in client mode. # 3 bytes corresponding to ???? #qos 0x0b921f openggsn-0.92/examples/ggsn.init000066400000000000000000000031401262356443100167310ustar00rootroot00000000000000#!/bin/sh # # ggsn This shell script takes care of starting and stopping # ggsn. # # chkconfig: - 65 35 # description: ggsn is a Gateway GPRS Support Node. # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network if [ -f /etc/sysconfig/ggsn ]; then . /etc/sysconfig/ggsn fi # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 [ -f /usr/bin/ggsn ] || exit 0 [ -f /etc/ggsn.conf ] || exit 0 RETVAL=0 prog="ggsn" start() { # Start daemons. echo -n $"Starting $prog: " # Load tun module /sbin/modprobe tun >/dev/null 2>&1 # Enable routing of packets: WARNING!!! # Users should enable this explicitly # echo 1 > /proc/sys/net/ipv4/ip_forward # Check for runtime directory of nonvolatile data if [ ! -d /var/lib/ggsn ]; then mkdir /var/lib/ggsn fi # Check for GTP restart counter if [ ! -d /var/lib/ggsn/gsn_restart ]; then echo 0 > /var/lib/ggsn/gsn_restart fi daemon /usr/bin/ggsn RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ggsn return $RETVAL } stop() { # Stop daemons. echo -n $"Shutting down $prog: " killproc ggsn RETVAL=$? echo [ $RETVAL = 0 ] && rm -f /var/lock/subsys/ggsn /var/run/ggsn.pid return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart|reload) stop start RETVAL=$? ;; condrestart) if [ -f /var/lock/subsys/ggsn ] ; then stop start RETVAL=$? fi ;; status) status ggsn RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|condrestart|status}" exit 1 esac exit $RETVAL openggsn-0.92/examples/sgsnemu.conf000066400000000000000000000062331262356443100174440ustar00rootroot00000000000000############################################################################## # # Sample sgsnemu configuration file # ############################################################################## # TAG: debug # Include this flag to include debug information. #debug # TAG: conf # Configuration file to use. This file is the configuration file, # so changing this parameter in the configuration file does not make # sense. Use it on the command line instead. # TAG: pidfile # File to store information about the pricess id of the program. # The program must have write access to this file/directory. #pidfile ./sgsnemu.pid # TAG: statedir # Directory to use for nonvolatile storage. # The program must have write access to this directory. #statedir ./ # TAG: dns # DNS server to use for ns lookups. # If this tag is not set the system default DNS will be used. #dns 10.1.2.3 # TAG: listen # Specifies the local IP address to listen to #listen 10.0.0.217 # TAG: remote # Specifies the remote IP address to connect to # If DNS is setup correctly it should be possible to specify the # access point name (APN) as the remote address. #remote 10.0.0.240 # TAG: contexts # Number of contexts to establish from the emulator to the ggsn. # Set this tag to zero to not establish any contexts. #contexts 1 # TAG: timelimit # Disconnect contexts after timelimit seconds, and exit the program. # Setting timelimit to zero will cause the program not to disconnect. #timelimit 0 # TAG: apn # Access point name to connect to when run in client mode. #apn internet # TAG: selmode # Selection mode to use when connecting to GGSN. #selmode 0x01 # TAG: imsi # IMSI number used when run in client mode. #imsi 2400101234567890 # TAG: msisdn # MSISDN number used when run in client mode. #msisdn 46702123456 # TAG: qos # Requested Quality of Service used when run in client mode. # 3 bytes corresponding to ???? #qos 0x0b921f # TAG: uid # User ID used when run in client mode. #uid mig # TAG: pwd # Password used when run in client mode. #pwd hemlig # TAG: createif # Use this flag if you want to set up a local network interface after # a PDP context has been established. #createif # TAG: defaultroute # Use this flag if you want to add a default route after a network interface # had been established. #defaultroute # TAG: ipup # Script executed after network interface has been brought up. # Executed with the following parameters: #ipup /etc/sgsnemu/ip-up # TAG: ipdown # Script executed after network interface has been taken down. # Executed with the following parameters: #ipdown /etc/sgsnemu/ip-down # TAG: pinghost # Ping a remote host through a PDP context by using ICMP echo messages. # If more than one PDP context has been established the ICMP messages will # be distributed between all available contexts. #pinghost 192.168.0.0 # TAG: pingrate # Number of ping messages to send off each second. #pingrate 1 # TAG: pingsize # Size of ICMP echo message payload. Add 28 to get the full IP packet size. #pingsize 56 # TAG: pingcount # Number of ping messages to send off. #pingcount 0 # TAG: pingquiet # Do not print ping packet info. #pingquiet openggsn-0.92/ggsn/000077500000000000000000000000001262356443100142305ustar00rootroot00000000000000openggsn-0.92/ggsn/Makefile.am000066400000000000000000000005101262356443100162600ustar00rootroot00000000000000bin_PROGRAMS = ggsn AM_LDFLAGS = @EXEC_LDFLAGS@ AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a ggsn_SOURCES = ggsn.c cmdline.c cmdline.h openggsn-0.92/ggsn/cmdline.c000066400000000000000000001052041262356443100160110ustar00rootroot00000000000000/* File autogenerated by gengetopt version 2.22.6 generated with the following command: gengetopt --conf-parser The developers of gengetopt consider the fixed text that goes in all gengetopt output files to be in the public domain: we make no copyright claims on it. */ /* If we use autoconf. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifndef FIX_UNUSED #define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ #endif #include #include "cmdline.h" const char *gengetopt_args_info_purpose = ""; const char *gengetopt_args_info_usage = "Usage: " CMDLINE_PARSER_PACKAGE " [OPTIONS]..."; const char *gengetopt_args_info_versiontext = ""; const char *gengetopt_args_info_description = ""; const char *gengetopt_args_info_help[] = { " -h, --help Print help and exit", " -V, --version Print version and exit", " -f, --fg Run in foreground (default=off)", " -d, --debug Run in debug mode (default=off)", " -c, --conf=STRING Read configuration file (default=`/etc/ggsn.conf')", " --pidfile=STRING Filename of process id file\n (default=`/var/run/ggsn.pid')", " --statedir=STRING Directory of nonvolatile data\n (default=`/var/lib/ggsn/')", " -l, --listen=STRING Local interface", " -n, --net=STRING Network (default=`192.168.0.0/24')", " --ipup=STRING Script to run after link-up", " --ipdown=STRING Script to run after link-down", " --dynip=STRING Dynamic IP address pool", " --statip=STRING Static IP address pool", " --pcodns1=STRING PCO DNS Server 1 (default=`0.0.0.0')", " --pcodns2=STRING PCO DNS Server 2 (default=`0.0.0.0')", " --timelimit=INT Exit after timelimit seconds (default=`0')", " -a, --apn=STRING Access point name (default=`internet')", " -q, --qos=INT Requested quality of service (default=`0x0b921f')", " --logfile=STRING Logfile for errors", " --loglevel=STRING Global log ldevel (default=`error')", 0 }; typedef enum {ARG_NO , ARG_FLAG , ARG_STRING , ARG_INT } cmdline_parser_arg_type; static void clear_given (struct gengetopt_args_info *args_info); static void clear_args (struct gengetopt_args_info *args_info); static int cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, struct cmdline_parser_params *params, const char *additional_error); struct line_list { char * string_arg; struct line_list * next; }; static struct line_list *cmd_line_list = 0; static struct line_list *cmd_line_list_tmp = 0; static void free_cmd_list(void) { /* free the list of a previous call */ if (cmd_line_list) { while (cmd_line_list) { cmd_line_list_tmp = cmd_line_list; cmd_line_list = cmd_line_list->next; free (cmd_line_list_tmp->string_arg); free (cmd_line_list_tmp); } } } static char * gengetopt_strdup (const char *s); static void clear_given (struct gengetopt_args_info *args_info) { args_info->help_given = 0 ; args_info->version_given = 0 ; args_info->fg_given = 0 ; args_info->debug_given = 0 ; args_info->conf_given = 0 ; args_info->pidfile_given = 0 ; args_info->statedir_given = 0 ; args_info->listen_given = 0 ; args_info->net_given = 0 ; args_info->ipup_given = 0 ; args_info->ipdown_given = 0 ; args_info->dynip_given = 0 ; args_info->statip_given = 0 ; args_info->pcodns1_given = 0 ; args_info->pcodns2_given = 0 ; args_info->timelimit_given = 0 ; args_info->apn_given = 0 ; args_info->qos_given = 0 ; args_info->logfile_given = 0 ; args_info->loglevel_given = 0 ; } static void clear_args (struct gengetopt_args_info *args_info) { FIX_UNUSED (args_info); args_info->fg_flag = 0; args_info->debug_flag = 0; args_info->conf_arg = gengetopt_strdup ("/etc/ggsn.conf"); args_info->conf_orig = NULL; args_info->pidfile_arg = gengetopt_strdup ("/var/run/ggsn.pid"); args_info->pidfile_orig = NULL; args_info->statedir_arg = gengetopt_strdup ("/var/lib/ggsn/"); args_info->statedir_orig = NULL; args_info->listen_arg = NULL; args_info->listen_orig = NULL; args_info->net_arg = gengetopt_strdup ("192.168.0.0/24"); args_info->net_orig = NULL; args_info->ipup_arg = NULL; args_info->ipup_orig = NULL; args_info->ipdown_arg = NULL; args_info->ipdown_orig = NULL; args_info->dynip_arg = NULL; args_info->dynip_orig = NULL; args_info->statip_arg = NULL; args_info->statip_orig = NULL; args_info->pcodns1_arg = gengetopt_strdup ("0.0.0.0"); args_info->pcodns1_orig = NULL; args_info->pcodns2_arg = gengetopt_strdup ("0.0.0.0"); args_info->pcodns2_orig = NULL; args_info->timelimit_arg = 0; args_info->timelimit_orig = NULL; args_info->apn_arg = gengetopt_strdup ("internet"); args_info->apn_orig = NULL; args_info->qos_arg = 0x0b921f; args_info->qos_orig = NULL; args_info->logfile_arg = NULL; args_info->logfile_orig = NULL; args_info->loglevel_arg = gengetopt_strdup ("error"); args_info->loglevel_orig = NULL; } static void init_args_info(struct gengetopt_args_info *args_info) { args_info->help_help = gengetopt_args_info_help[0] ; args_info->version_help = gengetopt_args_info_help[1] ; args_info->fg_help = gengetopt_args_info_help[2] ; args_info->debug_help = gengetopt_args_info_help[3] ; args_info->conf_help = gengetopt_args_info_help[4] ; args_info->pidfile_help = gengetopt_args_info_help[5] ; args_info->statedir_help = gengetopt_args_info_help[6] ; args_info->listen_help = gengetopt_args_info_help[7] ; args_info->net_help = gengetopt_args_info_help[8] ; args_info->ipup_help = gengetopt_args_info_help[9] ; args_info->ipdown_help = gengetopt_args_info_help[10] ; args_info->dynip_help = gengetopt_args_info_help[11] ; args_info->statip_help = gengetopt_args_info_help[12] ; args_info->pcodns1_help = gengetopt_args_info_help[13] ; args_info->pcodns2_help = gengetopt_args_info_help[14] ; args_info->timelimit_help = gengetopt_args_info_help[15] ; args_info->apn_help = gengetopt_args_info_help[16] ; args_info->qos_help = gengetopt_args_info_help[17] ; args_info->logfile_help = gengetopt_args_info_help[18] ; args_info->loglevel_help = gengetopt_args_info_help[19] ; } void cmdline_parser_print_version (void) { printf ("%s %s\n", (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), CMDLINE_PARSER_VERSION); if (strlen(gengetopt_args_info_versiontext) > 0) printf("\n%s\n", gengetopt_args_info_versiontext); } static void print_help_common(void) { cmdline_parser_print_version (); if (strlen(gengetopt_args_info_purpose) > 0) printf("\n%s\n", gengetopt_args_info_purpose); if (strlen(gengetopt_args_info_usage) > 0) printf("\n%s\n", gengetopt_args_info_usage); printf("\n"); if (strlen(gengetopt_args_info_description) > 0) printf("%s\n\n", gengetopt_args_info_description); } void cmdline_parser_print_help (void) { int i = 0; print_help_common(); while (gengetopt_args_info_help[i]) printf("%s\n", gengetopt_args_info_help[i++]); } void cmdline_parser_init (struct gengetopt_args_info *args_info) { clear_given (args_info); clear_args (args_info); init_args_info (args_info); } void cmdline_parser_params_init(struct cmdline_parser_params *params) { if (params) { params->override = 0; params->initialize = 1; params->check_required = 1; params->check_ambiguity = 0; params->print_errors = 1; } } struct cmdline_parser_params * cmdline_parser_params_create(void) { struct cmdline_parser_params *params = (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); cmdline_parser_params_init(params); return params; } static void free_string_field (char **s) { if (*s) { free (*s); *s = 0; } } static void cmdline_parser_release (struct gengetopt_args_info *args_info) { free_string_field (&(args_info->conf_arg)); free_string_field (&(args_info->conf_orig)); free_string_field (&(args_info->pidfile_arg)); free_string_field (&(args_info->pidfile_orig)); free_string_field (&(args_info->statedir_arg)); free_string_field (&(args_info->statedir_orig)); free_string_field (&(args_info->listen_arg)); free_string_field (&(args_info->listen_orig)); free_string_field (&(args_info->net_arg)); free_string_field (&(args_info->net_orig)); free_string_field (&(args_info->ipup_arg)); free_string_field (&(args_info->ipup_orig)); free_string_field (&(args_info->ipdown_arg)); free_string_field (&(args_info->ipdown_orig)); free_string_field (&(args_info->dynip_arg)); free_string_field (&(args_info->dynip_orig)); free_string_field (&(args_info->statip_arg)); free_string_field (&(args_info->statip_orig)); free_string_field (&(args_info->pcodns1_arg)); free_string_field (&(args_info->pcodns1_orig)); free_string_field (&(args_info->pcodns2_arg)); free_string_field (&(args_info->pcodns2_orig)); free_string_field (&(args_info->timelimit_orig)); free_string_field (&(args_info->apn_arg)); free_string_field (&(args_info->apn_orig)); free_string_field (&(args_info->qos_orig)); free_string_field (&(args_info->logfile_arg)); free_string_field (&(args_info->logfile_orig)); free_string_field (&(args_info->loglevel_arg)); free_string_field (&(args_info->loglevel_orig)); clear_given (args_info); } static void write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) { FIX_UNUSED (values); if (arg) { fprintf(outfile, "%s=\"%s\"\n", opt, arg); } else { fprintf(outfile, "%s\n", opt); } } int cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) { int i = 0; if (!outfile) { fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); return EXIT_FAILURE; } if (args_info->help_given) write_into_file(outfile, "help", 0, 0 ); if (args_info->version_given) write_into_file(outfile, "version", 0, 0 ); if (args_info->fg_given) write_into_file(outfile, "fg", 0, 0 ); if (args_info->debug_given) write_into_file(outfile, "debug", 0, 0 ); if (args_info->conf_given) write_into_file(outfile, "conf", args_info->conf_orig, 0); if (args_info->pidfile_given) write_into_file(outfile, "pidfile", args_info->pidfile_orig, 0); if (args_info->statedir_given) write_into_file(outfile, "statedir", args_info->statedir_orig, 0); if (args_info->listen_given) write_into_file(outfile, "listen", args_info->listen_orig, 0); if (args_info->net_given) write_into_file(outfile, "net", args_info->net_orig, 0); if (args_info->ipup_given) write_into_file(outfile, "ipup", args_info->ipup_orig, 0); if (args_info->ipdown_given) write_into_file(outfile, "ipdown", args_info->ipdown_orig, 0); if (args_info->dynip_given) write_into_file(outfile, "dynip", args_info->dynip_orig, 0); if (args_info->statip_given) write_into_file(outfile, "statip", args_info->statip_orig, 0); if (args_info->pcodns1_given) write_into_file(outfile, "pcodns1", args_info->pcodns1_orig, 0); if (args_info->pcodns2_given) write_into_file(outfile, "pcodns2", args_info->pcodns2_orig, 0); if (args_info->timelimit_given) write_into_file(outfile, "timelimit", args_info->timelimit_orig, 0); if (args_info->apn_given) write_into_file(outfile, "apn", args_info->apn_orig, 0); if (args_info->qos_given) write_into_file(outfile, "qos", args_info->qos_orig, 0); if (args_info->logfile_given) write_into_file(outfile, "logfile", args_info->logfile_orig, 0); if (args_info->loglevel_given) write_into_file(outfile, "loglevel", args_info->loglevel_orig, 0); i = EXIT_SUCCESS; return i; } int cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) { FILE *outfile; int i = 0; outfile = fopen(filename, "w"); if (!outfile) { fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); return EXIT_FAILURE; } i = cmdline_parser_dump(outfile, args_info); fclose (outfile); return i; } void cmdline_parser_free (struct gengetopt_args_info *args_info) { cmdline_parser_release (args_info); } /** @brief replacement of strdup, which is not standard */ char * gengetopt_strdup (const char *s) { char *result = 0; if (!s) return result; result = (char*)malloc(strlen(s) + 1); if (result == (char*)0) return (char*)0; strcpy(result, s); return result; } int cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) { return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); } int cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, struct cmdline_parser_params *params) { int result; result = cmdline_parser_internal (argc, argv, args_info, params, 0); if (result == EXIT_FAILURE) { cmdline_parser_free (args_info); exit (EXIT_FAILURE); } return result; } int cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) { int result; struct cmdline_parser_params params; params.override = override; params.initialize = initialize; params.check_required = check_required; params.check_ambiguity = 0; params.print_errors = 1; result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); if (result == EXIT_FAILURE) { cmdline_parser_free (args_info); exit (EXIT_FAILURE); } return result; } int cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) { FIX_UNUSED (args_info); FIX_UNUSED (prog_name); return EXIT_SUCCESS; } static char *package_name = 0; /** * @brief updates an option * @param field the generic pointer to the field to update * @param orig_field the pointer to the orig field * @param field_given the pointer to the number of occurrence of this option * @param prev_given the pointer to the number of occurrence already seen * @param value the argument for this option (if null no arg was specified) * @param possible_values the possible values for this option (if specified) * @param default_value the default value (in case the option only accepts fixed values) * @param arg_type the type of this option * @param check_ambiguity @see cmdline_parser_params.check_ambiguity * @param override @see cmdline_parser_params.override * @param no_free whether to free a possible previous value * @param multiple_option whether this is a multiple option * @param long_opt the corresponding long option * @param short_opt the corresponding short option (or '-' if none) * @param additional_error possible further error specification */ static int update_arg(void *field, char **orig_field, unsigned int *field_given, unsigned int *prev_given, char *value, const char *possible_values[], const char *default_value, cmdline_parser_arg_type arg_type, int check_ambiguity, int override, int no_free, int multiple_option, const char *long_opt, char short_opt, const char *additional_error) { char *stop_char = 0; const char *val = value; int found; char **string_field; FIX_UNUSED (field); stop_char = 0; found = 0; if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) { if (short_opt != '-') fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", package_name, long_opt, short_opt, (additional_error ? additional_error : "")); else fprintf (stderr, "%s: `--%s' option given more than once%s\n", package_name, long_opt, (additional_error ? additional_error : "")); return 1; /* failure */ } FIX_UNUSED (default_value); if (field_given && *field_given && ! override) return 0; if (prev_given) (*prev_given)++; if (field_given) (*field_given)++; if (possible_values) val = possible_values[found]; switch(arg_type) { case ARG_FLAG: *((int *)field) = !*((int *)field); break; case ARG_INT: if (val) *((int *)field) = strtol (val, &stop_char, 0); break; case ARG_STRING: if (val) { string_field = (char **)field; if (!no_free && *string_field) free (*string_field); /* free previous string */ *string_field = gengetopt_strdup (val); } break; default: break; }; /* check numeric conversion */ switch(arg_type) { case ARG_INT: if (val && !(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); return 1; /* failure */ } break; default: ; }; /* store the original value */ switch(arg_type) { case ARG_NO: case ARG_FLAG: break; default: if (value && orig_field) { if (no_free) { *orig_field = value; } else { if (*orig_field) free (*orig_field); /* free previous string */ *orig_field = gengetopt_strdup (value); } } }; return 0; /* OK */ } int cmdline_parser_internal ( int argc, char **argv, struct gengetopt_args_info *args_info, struct cmdline_parser_params *params, const char *additional_error) { int c; /* Character of the parsed option. */ int error_occurred = 0; struct gengetopt_args_info local_args_info; int override; int initialize; int check_required; int check_ambiguity; package_name = argv[0]; override = params->override; initialize = params->initialize; check_required = params->check_required; check_ambiguity = params->check_ambiguity; if (initialize) cmdline_parser_init (args_info); cmdline_parser_init (&local_args_info); optarg = 0; optind = 0; opterr = params->print_errors; optopt = '?'; while (1) { int option_index = 0; static struct option long_options[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "fg", 0, NULL, 'f' }, { "debug", 0, NULL, 'd' }, { "conf", 1, NULL, 'c' }, { "pidfile", 1, NULL, 0 }, { "statedir", 1, NULL, 0 }, { "listen", 1, NULL, 'l' }, { "net", 1, NULL, 'n' }, { "ipup", 1, NULL, 0 }, { "ipdown", 1, NULL, 0 }, { "dynip", 1, NULL, 0 }, { "statip", 1, NULL, 0 }, { "pcodns1", 1, NULL, 0 }, { "pcodns2", 1, NULL, 0 }, { "timelimit", 1, NULL, 0 }, { "apn", 1, NULL, 'a' }, { "qos", 1, NULL, 'q' }, { "logfile", 1, NULL, 0 }, { "loglevel", 1, NULL, 0 }, { 0, 0, 0, 0 } }; c = getopt_long (argc, argv, "hVfdc:l:n:a:q:", long_options, &option_index); if (c == -1) break; /* Exit from `while (1)' loop. */ switch (c) { case 'h': /* Print help and exit. */ cmdline_parser_print_help (); cmdline_parser_free (&local_args_info); exit (EXIT_SUCCESS); case 'V': /* Print version and exit. */ cmdline_parser_print_version (); cmdline_parser_free (&local_args_info); exit (EXIT_SUCCESS); case 'f': /* Run in foreground. */ if (update_arg((void *)&(args_info->fg_flag), 0, &(args_info->fg_given), &(local_args_info.fg_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "fg", 'f', additional_error)) goto failure; break; case 'd': /* Run in debug mode. */ if (update_arg((void *)&(args_info->debug_flag), 0, &(args_info->debug_given), &(local_args_info.debug_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "debug", 'd', additional_error)) goto failure; break; case 'c': /* Read configuration file. */ if (update_arg( (void *)&(args_info->conf_arg), &(args_info->conf_orig), &(args_info->conf_given), &(local_args_info.conf_given), optarg, 0, "/etc/ggsn.conf", ARG_STRING, check_ambiguity, override, 0, 0, "conf", 'c', additional_error)) goto failure; break; case 'l': /* Local interface. */ if (update_arg( (void *)&(args_info->listen_arg), &(args_info->listen_orig), &(args_info->listen_given), &(local_args_info.listen_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "listen", 'l', additional_error)) goto failure; break; case 'n': /* Network. */ if (update_arg( (void *)&(args_info->net_arg), &(args_info->net_orig), &(args_info->net_given), &(local_args_info.net_given), optarg, 0, "192.168.0.0/24", ARG_STRING, check_ambiguity, override, 0, 0, "net", 'n', additional_error)) goto failure; break; case 'a': /* Access point name. */ if (update_arg( (void *)&(args_info->apn_arg), &(args_info->apn_orig), &(args_info->apn_given), &(local_args_info.apn_given), optarg, 0, "internet", ARG_STRING, check_ambiguity, override, 0, 0, "apn", 'a', additional_error)) goto failure; break; case 'q': /* Requested quality of service. */ if (update_arg( (void *)&(args_info->qos_arg), &(args_info->qos_orig), &(args_info->qos_given), &(local_args_info.qos_given), optarg, 0, "0x0b921f", ARG_INT, check_ambiguity, override, 0, 0, "qos", 'q', additional_error)) goto failure; break; case 0: /* Long option with no short option */ /* Filename of process id file. */ if (strcmp (long_options[option_index].name, "pidfile") == 0) { if (update_arg( (void *)&(args_info->pidfile_arg), &(args_info->pidfile_orig), &(args_info->pidfile_given), &(local_args_info.pidfile_given), optarg, 0, "/var/run/ggsn.pid", ARG_STRING, check_ambiguity, override, 0, 0, "pidfile", '-', additional_error)) goto failure; } /* Directory of nonvolatile data. */ else if (strcmp (long_options[option_index].name, "statedir") == 0) { if (update_arg( (void *)&(args_info->statedir_arg), &(args_info->statedir_orig), &(args_info->statedir_given), &(local_args_info.statedir_given), optarg, 0, "/var/lib/ggsn/", ARG_STRING, check_ambiguity, override, 0, 0, "statedir", '-', additional_error)) goto failure; } /* Script to run after link-up. */ else if (strcmp (long_options[option_index].name, "ipup") == 0) { if (update_arg( (void *)&(args_info->ipup_arg), &(args_info->ipup_orig), &(args_info->ipup_given), &(local_args_info.ipup_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "ipup", '-', additional_error)) goto failure; } /* Script to run after link-down. */ else if (strcmp (long_options[option_index].name, "ipdown") == 0) { if (update_arg( (void *)&(args_info->ipdown_arg), &(args_info->ipdown_orig), &(args_info->ipdown_given), &(local_args_info.ipdown_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "ipdown", '-', additional_error)) goto failure; } /* Dynamic IP address pool. */ else if (strcmp (long_options[option_index].name, "dynip") == 0) { if (update_arg( (void *)&(args_info->dynip_arg), &(args_info->dynip_orig), &(args_info->dynip_given), &(local_args_info.dynip_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "dynip", '-', additional_error)) goto failure; } /* Static IP address pool. */ else if (strcmp (long_options[option_index].name, "statip") == 0) { if (update_arg( (void *)&(args_info->statip_arg), &(args_info->statip_orig), &(args_info->statip_given), &(local_args_info.statip_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "statip", '-', additional_error)) goto failure; } /* PCO DNS Server 1. */ else if (strcmp (long_options[option_index].name, "pcodns1") == 0) { if (update_arg( (void *)&(args_info->pcodns1_arg), &(args_info->pcodns1_orig), &(args_info->pcodns1_given), &(local_args_info.pcodns1_given), optarg, 0, "0.0.0.0", ARG_STRING, check_ambiguity, override, 0, 0, "pcodns1", '-', additional_error)) goto failure; } /* PCO DNS Server 2. */ else if (strcmp (long_options[option_index].name, "pcodns2") == 0) { if (update_arg( (void *)&(args_info->pcodns2_arg), &(args_info->pcodns2_orig), &(args_info->pcodns2_given), &(local_args_info.pcodns2_given), optarg, 0, "0.0.0.0", ARG_STRING, check_ambiguity, override, 0, 0, "pcodns2", '-', additional_error)) goto failure; } /* Exit after timelimit seconds. */ else if (strcmp (long_options[option_index].name, "timelimit") == 0) { if (update_arg( (void *)&(args_info->timelimit_arg), &(args_info->timelimit_orig), &(args_info->timelimit_given), &(local_args_info.timelimit_given), optarg, 0, "0", ARG_INT, check_ambiguity, override, 0, 0, "timelimit", '-', additional_error)) goto failure; } /* Logfile for errors. */ else if (strcmp (long_options[option_index].name, "logfile") == 0) { if (update_arg( (void *)&(args_info->logfile_arg), &(args_info->logfile_orig), &(args_info->logfile_given), &(local_args_info.logfile_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "logfile", '-', additional_error)) goto failure; } /* Global log ldevel. */ else if (strcmp (long_options[option_index].name, "loglevel") == 0) { if (update_arg( (void *)&(args_info->loglevel_arg), &(args_info->loglevel_orig), &(args_info->loglevel_given), &(local_args_info.loglevel_given), optarg, 0, "error", ARG_STRING, check_ambiguity, override, 0, 0, "loglevel", '-', additional_error)) goto failure; } break; case '?': /* Invalid option. */ /* `getopt_long' already printed an error message. */ goto failure; default: /* bug: option not considered. */ fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); abort (); } /* switch */ } /* while */ cmdline_parser_release (&local_args_info); if ( error_occurred ) return (EXIT_FAILURE); return 0; failure: cmdline_parser_release (&local_args_info); return (EXIT_FAILURE); } #ifndef CONFIG_FILE_LINE_SIZE #define CONFIG_FILE_LINE_SIZE 2048 #endif #define ADDITIONAL_ERROR " in configuration file " #define CONFIG_FILE_LINE_BUFFER_SIZE (CONFIG_FILE_LINE_SIZE+3) /* 3 is for "--" and "=" */ static int _cmdline_parser_configfile (const char *filename, int *my_argc) { FILE* file; char my_argv[CONFIG_FILE_LINE_BUFFER_SIZE+1]; char linebuf[CONFIG_FILE_LINE_SIZE]; int line_num = 0; int result = 0, equal; char *fopt, *farg; char *str_index; size_t len, next_token; char delimiter; if ((file = fopen(filename, "r")) == 0) { fprintf (stderr, "%s: Error opening configuration file '%s'\n", CMDLINE_PARSER_PACKAGE, filename); return EXIT_FAILURE; } while ((fgets(linebuf, CONFIG_FILE_LINE_SIZE, file)) != 0) { ++line_num; my_argv[0] = '\0'; len = strlen(linebuf); if (len > (CONFIG_FILE_LINE_BUFFER_SIZE-1)) { fprintf (stderr, "%s:%s:%d: Line too long in configuration file\n", CMDLINE_PARSER_PACKAGE, filename, line_num); result = EXIT_FAILURE; break; } /* find first non-whitespace character in the line */ next_token = strspn (linebuf, " \t\r\n"); str_index = linebuf + next_token; if ( str_index[0] == '\0' || str_index[0] == '#') continue; /* empty line or comment line is skipped */ fopt = str_index; /* truncate fopt at the end of the first non-valid character */ next_token = strcspn (fopt, " \t\r\n="); if (fopt[next_token] == '\0') /* the line is over */ { farg = 0; equal = 0; goto noarg; } /* remember if equal sign is present */ equal = (fopt[next_token] == '='); fopt[next_token++] = '\0'; /* advance pointers to the next token after the end of fopt */ next_token += strspn (fopt + next_token, " \t\r\n"); /* check for the presence of equal sign, and if so, skip it */ if ( !equal ) if ((equal = (fopt[next_token] == '='))) { next_token++; next_token += strspn (fopt + next_token, " \t\r\n"); } str_index += next_token; /* find argument */ farg = str_index; if ( farg[0] == '\"' || farg[0] == '\'' ) { /* quoted argument */ str_index = strchr (++farg, str_index[0] ); /* skip opening quote */ if (! str_index) { fprintf (stderr, "%s:%s:%d: unterminated string in configuration file\n", CMDLINE_PARSER_PACKAGE, filename, line_num); result = EXIT_FAILURE; break; } } else { /* read up the remaining part up to a delimiter */ next_token = strcspn (farg, " \t\r\n#\'\""); str_index += next_token; } /* truncate farg at the delimiter and store it for further check */ delimiter = *str_index, *str_index++ = '\0'; /* everything but comment is illegal at the end of line */ if (delimiter != '\0' && delimiter != '#') { str_index += strspn(str_index, " \t\r\n"); if (*str_index != '\0' && *str_index != '#') { fprintf (stderr, "%s:%s:%d: malformed string in configuration file\n", CMDLINE_PARSER_PACKAGE, filename, line_num); result = EXIT_FAILURE; break; } } noarg: if (!strcmp(fopt,"include")) { if (farg && *farg) { result = _cmdline_parser_configfile(farg, my_argc); } else { fprintf(stderr, "%s:%s:%d: include requires a filename argument.\n", CMDLINE_PARSER_PACKAGE, filename, line_num); } continue; } len = strlen(fopt); strcat (my_argv, len > 1 ? "--" : "-"); strcat (my_argv, fopt); if (len > 1 && ((farg && *farg) || equal)) strcat (my_argv, "="); if (farg && *farg) strcat (my_argv, farg); ++(*my_argc); cmd_line_list_tmp = (struct line_list *) malloc (sizeof (struct line_list)); cmd_line_list_tmp->next = cmd_line_list; cmd_line_list = cmd_line_list_tmp; cmd_line_list->string_arg = gengetopt_strdup(my_argv); } /* while */ if (file) fclose(file); return result; } int cmdline_parser_configfile ( const char *filename, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) { struct cmdline_parser_params params; params.override = override; params.initialize = initialize; params.check_required = check_required; params.check_ambiguity = 0; params.print_errors = 1; return cmdline_parser_config_file (filename, args_info, ¶ms); } int cmdline_parser_config_file (const char *filename, struct gengetopt_args_info *args_info, struct cmdline_parser_params *params) { int i, result; int my_argc = 1; char **my_argv_arg; char *additional_error; /* store the program name */ cmd_line_list_tmp = (struct line_list *) malloc (sizeof (struct line_list)); cmd_line_list_tmp->next = cmd_line_list; cmd_line_list = cmd_line_list_tmp; cmd_line_list->string_arg = gengetopt_strdup (CMDLINE_PARSER_PACKAGE); result = _cmdline_parser_configfile(filename, &my_argc); if (result != EXIT_FAILURE) { my_argv_arg = (char **) malloc((my_argc+1) * sizeof(char *)); cmd_line_list_tmp = cmd_line_list; for (i = my_argc - 1; i >= 0; --i) { my_argv_arg[i] = cmd_line_list_tmp->string_arg; cmd_line_list_tmp = cmd_line_list_tmp->next; } my_argv_arg[my_argc] = 0; additional_error = (char *)malloc(strlen(filename) + strlen(ADDITIONAL_ERROR) + 1); strcpy (additional_error, ADDITIONAL_ERROR); strcat (additional_error, filename); result = cmdline_parser_internal (my_argc, my_argv_arg, args_info, params, additional_error); free (additional_error); free (my_argv_arg); } free_cmd_list(); if (result == EXIT_FAILURE) { cmdline_parser_free (args_info); exit (EXIT_FAILURE); } return result; } openggsn-0.92/ggsn/cmdline.ggo000066400000000000000000000034161262356443100163450ustar00rootroot00000000000000# OpenGGSN - Gateway GPRS Support Node # Copyright (C) 2002, 2003, 2004 Mondru AB. # # The contents of this file may be used under the terms of the GNU # General Public License Version 2, provided that the above copyright # notice and this permission notice is included in all copies or # substantial portions of the software. # # Use "gengetopt --conf-parser < cmdline.ggo" # to generate cmdline.c and cmdline.h option "fg" f "Run in foreground" flag off option "debug" d "Run in debug mode" flag off option "conf" c "Read configuration file" string default="/etc/ggsn.conf" no option "pidfile" - "Filename of process id file" string default="/var/run/ggsn.pid" no option "statedir" - "Directory of nonvolatile data" string default="/var/lib/ggsn/" no option "listen" l "Local interface" string no option "net" n "Network" string default="192.168.0.0/24" no option "ipup" - "Script to run after link-up" string no option "ipdown" - "Script to run after link-down" string no option "dynip" - "Dynamic IP address pool" string no option "statip" - "Static IP address pool" string no option "pcodns1" - "PCO DNS Server 1" string default="0.0.0.0" no option "pcodns2" - "PCO DNS Server 2" string default="0.0.0.0" no option "timelimit" - "Exit after timelimit seconds" int default="0" no option "apn" a "Access point name" string default="internet" no option "qos" q "Requested quality of service" int default="0x0b921f" no option "logfile" - "Logfile for errors" string no option "loglevel" - "Global log ldevel" string default="error" no openggsn-0.92/ggsn/cmdline.h000066400000000000000000000312251262356443100160170ustar00rootroot00000000000000/** @file cmdline.h * @brief The header file for the command line option parser * generated by GNU Gengetopt version 2.22.6 * http://www.gnu.org/software/gengetopt. * DO NOT modify this file, since it can be overwritten * @author GNU Gengetopt by Lorenzo Bettini */ #ifndef CMDLINE_H #define CMDLINE_H /* If we use autoconf. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* for FILE */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef CMDLINE_PARSER_PACKAGE /** @brief the program name (used for printing errors) */ #define CMDLINE_PARSER_PACKAGE PACKAGE #endif #ifndef CMDLINE_PARSER_PACKAGE_NAME /** @brief the complete program name (used for help and version) */ #ifdef PACKAGE_NAME #define CMDLINE_PARSER_PACKAGE_NAME PACKAGE_NAME #else #define CMDLINE_PARSER_PACKAGE_NAME PACKAGE #endif #endif #ifndef CMDLINE_PARSER_VERSION /** @brief the program version */ #define CMDLINE_PARSER_VERSION VERSION #endif /** @brief Where the command line options are stored */ struct gengetopt_args_info { const char *help_help; /**< @brief Print help and exit help description. */ const char *version_help; /**< @brief Print version and exit help description. */ int fg_flag; /**< @brief Run in foreground (default=off). */ const char *fg_help; /**< @brief Run in foreground help description. */ int debug_flag; /**< @brief Run in debug mode (default=off). */ const char *debug_help; /**< @brief Run in debug mode help description. */ char * conf_arg; /**< @brief Read configuration file (default='/etc/ggsn.conf'). */ char * conf_orig; /**< @brief Read configuration file original value given at command line. */ const char *conf_help; /**< @brief Read configuration file help description. */ char * pidfile_arg; /**< @brief Filename of process id file (default='/var/run/ggsn.pid'). */ char * pidfile_orig; /**< @brief Filename of process id file original value given at command line. */ const char *pidfile_help; /**< @brief Filename of process id file help description. */ char * statedir_arg; /**< @brief Directory of nonvolatile data (default='/var/lib/ggsn/'). */ char * statedir_orig; /**< @brief Directory of nonvolatile data original value given at command line. */ const char *statedir_help; /**< @brief Directory of nonvolatile data help description. */ char * listen_arg; /**< @brief Local interface. */ char * listen_orig; /**< @brief Local interface original value given at command line. */ const char *listen_help; /**< @brief Local interface help description. */ char * net_arg; /**< @brief Network (default='192.168.0.0/24'). */ char * net_orig; /**< @brief Network original value given at command line. */ const char *net_help; /**< @brief Network help description. */ char * ipup_arg; /**< @brief Script to run after link-up. */ char * ipup_orig; /**< @brief Script to run after link-up original value given at command line. */ const char *ipup_help; /**< @brief Script to run after link-up help description. */ char * ipdown_arg; /**< @brief Script to run after link-down. */ char * ipdown_orig; /**< @brief Script to run after link-down original value given at command line. */ const char *ipdown_help; /**< @brief Script to run after link-down help description. */ char * dynip_arg; /**< @brief Dynamic IP address pool. */ char * dynip_orig; /**< @brief Dynamic IP address pool original value given at command line. */ const char *dynip_help; /**< @brief Dynamic IP address pool help description. */ char * statip_arg; /**< @brief Static IP address pool. */ char * statip_orig; /**< @brief Static IP address pool original value given at command line. */ const char *statip_help; /**< @brief Static IP address pool help description. */ char * pcodns1_arg; /**< @brief PCO DNS Server 1 (default='0.0.0.0'). */ char * pcodns1_orig; /**< @brief PCO DNS Server 1 original value given at command line. */ const char *pcodns1_help; /**< @brief PCO DNS Server 1 help description. */ char * pcodns2_arg; /**< @brief PCO DNS Server 2 (default='0.0.0.0'). */ char * pcodns2_orig; /**< @brief PCO DNS Server 2 original value given at command line. */ const char *pcodns2_help; /**< @brief PCO DNS Server 2 help description. */ int timelimit_arg; /**< @brief Exit after timelimit seconds (default='0'). */ char * timelimit_orig; /**< @brief Exit after timelimit seconds original value given at command line. */ const char *timelimit_help; /**< @brief Exit after timelimit seconds help description. */ char * apn_arg; /**< @brief Access point name (default='internet'). */ char * apn_orig; /**< @brief Access point name original value given at command line. */ const char *apn_help; /**< @brief Access point name help description. */ int qos_arg; /**< @brief Requested quality of service (default='0x0b921f'). */ char * qos_orig; /**< @brief Requested quality of service original value given at command line. */ const char *qos_help; /**< @brief Requested quality of service help description. */ char * logfile_arg; /**< @brief Logfile for errors. */ char * logfile_orig; /**< @brief Logfile for errors original value given at command line. */ const char *logfile_help; /**< @brief Logfile for errors help description. */ char * loglevel_arg; /**< @brief Global log ldevel (default='error'). */ char * loglevel_orig; /**< @brief Global log ldevel original value given at command line. */ const char *loglevel_help; /**< @brief Global log ldevel help description. */ unsigned int help_given ; /**< @brief Whether help was given. */ unsigned int version_given ; /**< @brief Whether version was given. */ unsigned int fg_given ; /**< @brief Whether fg was given. */ unsigned int debug_given ; /**< @brief Whether debug was given. */ unsigned int conf_given ; /**< @brief Whether conf was given. */ unsigned int pidfile_given ; /**< @brief Whether pidfile was given. */ unsigned int statedir_given ; /**< @brief Whether statedir was given. */ unsigned int listen_given ; /**< @brief Whether listen was given. */ unsigned int net_given ; /**< @brief Whether net was given. */ unsigned int ipup_given ; /**< @brief Whether ipup was given. */ unsigned int ipdown_given ; /**< @brief Whether ipdown was given. */ unsigned int dynip_given ; /**< @brief Whether dynip was given. */ unsigned int statip_given ; /**< @brief Whether statip was given. */ unsigned int pcodns1_given ; /**< @brief Whether pcodns1 was given. */ unsigned int pcodns2_given ; /**< @brief Whether pcodns2 was given. */ unsigned int timelimit_given ; /**< @brief Whether timelimit was given. */ unsigned int apn_given ; /**< @brief Whether apn was given. */ unsigned int qos_given ; /**< @brief Whether qos was given. */ unsigned int logfile_given ; /**< @brief Whether logfile was given. */ unsigned int loglevel_given ; /**< @brief Whether loglevel was given. */ } ; /** @brief The additional parameters to pass to parser functions */ struct cmdline_parser_params { int override; /**< @brief whether to override possibly already present options (default 0) */ int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ int check_required; /**< @brief whether to check that all required options were provided (default 1) */ int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ } ; /** @brief the purpose string of the program */ extern const char *gengetopt_args_info_purpose; /** @brief the usage string of the program */ extern const char *gengetopt_args_info_usage; /** @brief the description string of the program */ extern const char *gengetopt_args_info_description; /** @brief all the lines making the help output */ extern const char *gengetopt_args_info_help[]; /** * The command line parser * @param argc the number of command line options * @param argv the command line options * @param args_info the structure where option information will be stored * @return 0 if everything went fine, NON 0 if an error took place */ int cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info); /** * The command line parser (version with additional parameters - deprecated) * @param argc the number of command line options * @param argv the command line options * @param args_info the structure where option information will be stored * @param override whether to override possibly already present options * @param initialize whether to initialize the option structure my_args_info * @param check_required whether to check that all required options were provided * @return 0 if everything went fine, NON 0 if an error took place * @deprecated use cmdline_parser_ext() instead */ int cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required); /** * The command line parser (version with additional parameters) * @param argc the number of command line options * @param argv the command line options * @param args_info the structure where option information will be stored * @param params additional parameters for the parser * @return 0 if everything went fine, NON 0 if an error took place */ int cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, struct cmdline_parser_params *params); /** * Save the contents of the option struct into an already open FILE stream. * @param outfile the stream where to dump options * @param args_info the option struct to dump * @return 0 if everything went fine, NON 0 if an error took place */ int cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info); /** * Save the contents of the option struct into a (text) file. * This file can be read by the config file parser (if generated by gengetopt) * @param filename the file where to save * @param args_info the option struct to save * @return 0 if everything went fine, NON 0 if an error took place */ int cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info); /** * Print the help */ void cmdline_parser_print_help(void); /** * Print the version */ void cmdline_parser_print_version(void); /** * Initializes all the fields a cmdline_parser_params structure * to their default values * @param params the structure to initialize */ void cmdline_parser_params_init(struct cmdline_parser_params *params); /** * Allocates dynamically a cmdline_parser_params structure and initializes * all its fields to their default values * @return the created and initialized cmdline_parser_params structure */ struct cmdline_parser_params *cmdline_parser_params_create(void); /** * Initializes the passed gengetopt_args_info structure's fields * (also set default values for options that have a default) * @param args_info the structure to initialize */ void cmdline_parser_init (struct gengetopt_args_info *args_info); /** * Deallocates the string fields of the gengetopt_args_info structure * (but does not deallocate the structure itself) * @param args_info the structure to deallocate */ void cmdline_parser_free (struct gengetopt_args_info *args_info); /** * The config file parser (deprecated version) * @param filename the name of the config file * @param args_info the structure where option information will be stored * @param override whether to override possibly already present options * @param initialize whether to initialize the option structure my_args_info * @param check_required whether to check that all required options were provided * @return 0 if everything went fine, NON 0 if an error took place * @deprecated use cmdline_parser_config_file() instead */ int cmdline_parser_configfile (const char *filename, struct gengetopt_args_info *args_info, int override, int initialize, int check_required); /** * The config file parser * @param filename the name of the config file * @param args_info the structure where option information will be stored * @param params additional parameters for the parser * @return 0 if everything went fine, NON 0 if an error took place */ int cmdline_parser_config_file (const char *filename, struct gengetopt_args_info *args_info, struct cmdline_parser_params *params); /** * Checks that all the required options were specified * @param args_info the structure to check * @param prog_name the name of the program that will be used to print * possible errors * @return */ int cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* CMDLINE_H */ openggsn-0.92/ggsn/ggsn.c000066400000000000000000000355461262356443100153470ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* ggsn.c * */ #ifdef __linux__ #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ #endif #include "../config.h" #include #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../lib/tun.h" #include "../lib/ippool.h" #include "../lib/syserr.h" #include "../gtp/pdp.h" #include "../gtp/gtp.h" #include "cmdline.h" int end = 0; int maxfd = 0; /* For select() */ struct in_addr listen_; struct in_addr netaddr, destaddr, net, mask; /* Network interface */ struct in_addr dns1, dns2; /* PCO DNS address */ char *ipup, *ipdown; /* Filename of scripts */ int debug; /* Print debug output */ struct ul255_t pco; struct ul255_t qos; struct ul255_t apn; struct gsn_t *gsn; /* GSN instance */ struct tun_t *tun; /* TUN instance */ struct ippool_t *ippool; /* Pool of IP addresses */ /* To exit gracefully. Used with GCC compilation flag -pg and gprof */ void signal_handler(int s) { DEBUGP(DGGSN, "Received signal %d, exiting.\n", s); end = 1; } /* Used to write process ID to file. Assume someone else will delete */ void log_pid(char *pidfile) { FILE *file; mode_t oldmask; oldmask = umask(022); file = fopen(pidfile, "w"); umask(oldmask); if (!file) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to create process ID file: %s!", pidfile); return; } fprintf(file, "%d\n", (int)getpid()); fclose(file); } #if defined(__sun__) int daemon(int nochdir, int noclose) { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) chdir("/"); if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } return (0); } #endif int delete_context(struct pdp_t *pdp) { DEBUGP(DGGSN, "Deleting PDP context\n"); if (pdp->peer) ippool_freeip(ippool, (struct ippoolm_t *)pdp->peer); else SYS_ERR(DGGSN, LOGL_ERROR, 0, "Peer not defined!"); return 0; } int create_context_ind(struct pdp_t *pdp) { struct in_addr addr; struct ippoolm_t *member; DEBUGP(DGGSN, "Received create PDP context request\n"); pdp->eua.l = 0; /* TODO: Indicates dynamic IP */ /* ulcpy(&pdp->qos_neg, &pdp->qos_req, sizeof(pdp->qos_req.v)); */ memcpy(pdp->qos_neg0, pdp->qos_req0, sizeof(pdp->qos_req0)); memcpy(&pdp->pco_neg, &pco, sizeof(pdp->pco_neg)); memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */ pdp->qos_neg.l = pdp->qos_req.l; if (pdp_euaton(&pdp->eua, &addr)) { addr.s_addr = 0; /* Request dynamic */ } if (ippool_newip(ippool, &member, &addr, 0)) { gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES); return 0; /* Allready in use, or no more available */ } pdp_ntoeua(&member->addr, &pdp->eua); pdp->peer = member; pdp->ipif = tun; /* TODO */ member->peer = pdp; gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ); return 0; /* Success */ } /* Callback for receiving messages from tun */ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) { struct ippoolm_t *ipm; struct in_addr dst; struct tun_packet_t *iph = (struct tun_packet_t *)pack; dst.s_addr = iph->dst; DEBUGP(DGGSN, "Received packet from tun!\n"); if (ippool_getip(ippool, &ipm, &dst)) { DEBUGP(DGGSN, "Received packet with no destination!!!\n"); return 0; } if (ipm->peer) /* Check if a peer protocol is defined */ gtp_data_req(gsn, (struct pdp_t *)ipm->peer, pack, len); return 0; } int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len) { DEBUGP(DGGSN, "encaps_tun. Packet received: forwarding to tun\n"); return tun_encaps((struct tun_t *)pdp->ipif, pack, len); } int main(int argc, char **argv) { /* gengeopt declarations */ struct gengetopt_args_info args_info; struct hostent *host; /* Handle keyboard interrupt SIGINT */ struct sigaction s; s.sa_handler = (void *)signal_handler; if ((0 != sigemptyset(&s.sa_mask)) && debug) printf("sigemptyset failed.\n"); s.sa_flags = SA_RESETHAND; if ((sigaction(SIGINT, &s, NULL) != 0) && debug) printf("Could not register SIGINT signal handler.\n"); fd_set fds; /* For select() */ struct timeval idleTime; /* How long to select() */ int timelimit; /* Number of seconds to be connected */ int starttime; /* Time program was started */ osmo_init_logging(&log_info); if (cmdline_parser(argc, argv, &args_info) != 0) exit(1); if (args_info.debug_flag) { printf("listen: %s\n", args_info.listen_arg); if (args_info.conf_arg) printf("conf: %s\n", args_info.conf_arg); printf("fg: %d\n", args_info.fg_flag); printf("debug: %d\n", args_info.debug_flag); printf("qos: %#08x\n", args_info.qos_arg); if (args_info.apn_arg) printf("apn: %s\n", args_info.apn_arg); if (args_info.net_arg) printf("net: %s\n", args_info.net_arg); if (args_info.dynip_arg) printf("dynip: %s\n", args_info.dynip_arg); if (args_info.statip_arg) printf("statip: %s\n", args_info.statip_arg); if (args_info.ipup_arg) printf("ipup: %s\n", args_info.ipup_arg); if (args_info.ipdown_arg) printf("ipdown: %s\n", args_info.ipdown_arg); if (args_info.pidfile_arg) printf("pidfile: %s\n", args_info.pidfile_arg); if (args_info.statedir_arg) printf("statedir: %s\n", args_info.statedir_arg); printf("timelimit: %d\n", args_info.timelimit_arg); } /* Try out our new parser */ if (cmdline_parser_configfile(args_info.conf_arg, &args_info, 0, 0, 0) != 0) exit(1); /* Open a log file */ if (args_info.logfile_arg) { struct log_target *tgt; int lvl; tgt = log_target_find(LOG_TGT_TYPE_FILE, args_info.logfile_arg); if (!tgt) { tgt = log_target_create_file(args_info.logfile_arg); if (!tgt) { LOGP(DGGSN, LOGL_ERROR, "Failed to create logfile: %s\n", args_info.logfile_arg); exit(1); } log_add_target(tgt); } log_set_all_filter(tgt, 1); log_set_use_color(tgt, 0); if (args_info.loglevel_arg) { lvl = log_parse_level(args_info.loglevel_arg); log_set_log_level(tgt, lvl); LOGP(DGGSN, LOGL_NOTICE, "Set file log level to %s\n", log_level_str(lvl)); } } if (args_info.debug_flag) { printf("cmdline_parser_configfile\n"); printf("listen: %s\n", args_info.listen_arg); printf("conf: %s\n", args_info.conf_arg); printf("fg: %d\n", args_info.fg_flag); printf("debug: %d\n", args_info.debug_flag); printf("qos: %#08x\n", args_info.qos_arg); if (args_info.apn_arg) printf("apn: %s\n", args_info.apn_arg); if (args_info.net_arg) printf("net: %s\n", args_info.net_arg); if (args_info.dynip_arg) printf("dynip: %s\n", args_info.dynip_arg); if (args_info.statip_arg) printf("statip: %s\n", args_info.statip_arg); if (args_info.ipup_arg) printf("ipup: %s\n", args_info.ipup_arg); if (args_info.ipdown_arg) printf("ipdown: %s\n", args_info.ipdown_arg); if (args_info.pidfile_arg) printf("pidfile: %s\n", args_info.pidfile_arg); if (args_info.statedir_arg) printf("statedir: %s\n", args_info.statedir_arg); printf("timelimit: %d\n", args_info.timelimit_arg); } /* Handle each option */ /* debug */ debug = args_info.debug_flag; /* listen */ /* Do hostname lookup to translate hostname to IP address */ /* Any port listening is not possible as a valid address is */ /* required for create_pdp_context_response messages */ if (args_info.listen_arg) { if (!(host = gethostbyname(args_info.listen_arg))) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Invalid listening address: %s!", args_info.listen_arg); exit(1); } else { memcpy(&listen_.s_addr, host->h_addr, host->h_length); } } else { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Listening address must be specified! " "Please use command line option --listen or " "edit %s configuration file\n", args_info.conf_arg); exit(1); } /* net */ /* Store net as in_addr net and mask */ if (args_info.net_arg) { if (ippool_aton(&net, &mask, args_info.net_arg, 0)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Invalid network address: %s!", args_info.net_arg); exit(1); } netaddr.s_addr = htonl(ntohl(net.s_addr) + 1); destaddr.s_addr = htonl(ntohl(net.s_addr) + 1); } else { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Network address must be specified: %s!", args_info.net_arg); exit(1); } /* dynip */ if (!args_info.dynip_arg) { if (ippool_new(&ippool, args_info.net_arg, NULL, 1, 0, IPPOOL_NONETWORK | IPPOOL_NOGATEWAY | IPPOOL_NOBROADCAST)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to allocate IP pool!"); exit(1); } } else { if (ippool_new(&ippool, args_info.dynip_arg, NULL, 1, 0, IPPOOL_NONETWORK | IPPOOL_NOGATEWAY | IPPOOL_NOBROADCAST)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to allocate IP pool!"); exit(1); } } /* DNS1 and DNS2 */ #ifdef HAVE_INET_ATON dns1.s_addr = 0; if (args_info.pcodns1_arg) { if (0 == inet_aton(args_info.pcodns1_arg, &dns1)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to convert pcodns1!"); exit(1); } } dns2.s_addr = 0; if (args_info.pcodns2_arg) { if (0 == inet_aton(args_info.pcodns2_arg, &dns2)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to convert pcodns2!"); exit(1); } } #else dns1.s_addr = 0; if (args_info.pcodns1_arg) { dns1.s_addr = inet_addr(args_info.pcodns1_arg); if (dns1.s_addr == -1) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to convert pcodns1!"); exit(1); } } dns2.s_addr = 0; if (args_info.pcodns2_arg) { dns2.s_addr = inet_addr(args_info.pcodns2_arg); if (dns2.s_addr == -1) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to convert pcodns2!"); exit(1); } } #endif pco.l = 20; pco.v[0] = 0x80; /* x0000yyy x=1, yyy=000: PPP */ pco.v[1] = 0x80; /* IPCP */ pco.v[2] = 0x21; pco.v[3] = 0x10; /* Length of contents */ pco.v[4] = 0x02; /* ACK */ pco.v[5] = 0x00; /* ID: Need to match request */ pco.v[6] = 0x00; /* Length */ pco.v[7] = 0x10; pco.v[8] = 0x81; /* DNS 1 */ pco.v[9] = 0x06; memcpy(&pco.v[10], &dns1, sizeof(dns1)); pco.v[14] = 0x83; pco.v[15] = 0x06; /* DNS 2 */ memcpy(&pco.v[16], &dns2, sizeof(dns2)); /* ipup */ ipup = args_info.ipup_arg; /* ipdown */ ipdown = args_info.ipdown_arg; /* Timelimit */ timelimit = args_info.timelimit_arg; starttime = time(NULL); /* qos */ qos.l = 3; qos.v[2] = (args_info.qos_arg) & 0xff; qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff; qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff; /* apn */ if (strlen(args_info.apn_arg) > (sizeof(apn.v) - 1)) { LOGP(DGGSN, LOGL_ERROR, "Invalid APN\n"); return -1; } apn.l = strlen(args_info.apn_arg) + 1; apn.v[0] = (char)strlen(args_info.apn_arg); strncpy((char *)&apn.v[1], args_info.apn_arg, sizeof(apn.v) - 1); /* foreground */ /* If flag not given run as a daemon */ if (!args_info.fg_flag) { FILE *f; int rc; /* Close the standard file descriptors. */ /* Is this really needed ? */ f = freopen("/dev/null", "w", stdout); if (f == NULL) { SYS_ERR(DGGSN, LOGL_NOTICE, 0, "Could not redirect stdout to /dev/null"); } f = freopen("/dev/null", "w", stderr); if (f == NULL) { SYS_ERR(DGGSN, LOGL_NOTICE, 0, "Could not redirect stderr to /dev/null"); } f = freopen("/dev/null", "r", stdin); if (f == NULL) { SYS_ERR(DGGSN, LOGL_NOTICE, 0, "Could not redirect stdin to /dev/null"); } rc = daemon(0, 0); if (rc != 0) { SYS_ERR(DGGSN, LOGL_ERROR, rc, "Could not daemonize"); exit(1); } } /* pidfile */ /* This has to be done after we have our final pid */ if (args_info.pidfile_arg) { log_pid(args_info.pidfile_arg); } DEBUGP(DGGSN, "gtpclient: Initialising GTP tunnel\n"); if (gtp_new(&gsn, args_info.statedir_arg, &listen_, GTP_MODE_GGSN)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to create gtp"); exit(1); } if (gsn->fd0 > maxfd) maxfd = gsn->fd0; if (gsn->fd1c > maxfd) maxfd = gsn->fd1c; if (gsn->fd1u > maxfd) maxfd = gsn->fd1u; gtp_set_cb_data_ind(gsn, encaps_tun); gtp_set_cb_delete_context(gsn, delete_context); gtp_set_cb_create_context_ind(gsn, create_context_ind); /* Create a tunnel interface */ DEBUGP(DGGSN, "Creating tun interface\n"); if (tun_new((struct tun_t **)&tun)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to create tun"); exit(1); } DEBUGP(DGGSN, "Setting tun IP address\n"); if (tun_setaddr(tun, &netaddr, &destaddr, &mask)) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to set tun IP address"); exit(1); } tun_set_cb_ind(tun, cb_tun_ind); if (tun->fd > maxfd) maxfd = tun->fd; if (ipup) tun_runscript(tun, ipup); /******************************************************************/ /* Main select loop */ /******************************************************************/ while ((((starttime + timelimit) > time(NULL)) || (0 == timelimit)) && (!end)) { FD_ZERO(&fds); if (tun) FD_SET(tun->fd, &fds); FD_SET(gsn->fd0, &fds); FD_SET(gsn->fd1c, &fds); FD_SET(gsn->fd1u, &fds); gtp_retranstimeout(gsn, &idleTime); switch (select(maxfd + 1, &fds, NULL, NULL, &idleTime)) { case -1: /* errno == EINTR : unblocked signal */ SYS_ERR(DGGSN, LOGL_ERROR, 0, "select() returned -1"); /* On error, select returns without modifying fds */ FD_ZERO(&fds); break; case 0: /* printf("Select returned 0\n"); */ gtp_retrans(gsn); /* Only retransmit if nothing else */ break; default: break; } if (tun->fd != -1 && FD_ISSET(tun->fd, &fds) && tun_decaps(tun) < 0) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "TUN read failed (fd)=(%d)", tun->fd); } if (FD_ISSET(gsn->fd0, &fds)) gtp_decaps0(gsn); if (FD_ISSET(gsn->fd1c, &fds)) gtp_decaps1c(gsn); if (FD_ISSET(gsn->fd1u, &fds)) gtp_decaps1u(gsn); } cmdline_parser_free(&args_info); ippool_free(ippool); gtp_free(gsn); tun_free(tun); return 1; } openggsn-0.92/gtp/000077500000000000000000000000001262356443100140645ustar00rootroot00000000000000openggsn-0.92/gtp/Makefile.am000066400000000000000000000004501262356443100161170ustar00rootroot00000000000000lib_LTLIBRARIES = libgtp.la include_HEADERS = gtp.h pdp.h gtpie.h AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) libgtp_la_SOURCES = gtp.c gtp.h gtpie.c gtpie.h pdp.c pdp.h lookupa.c lookupa.h queue.c queue.h libgtp_la_LIBADD = $(LIBOSMOCORE_LIBS) openggsn-0.92/gtp/gtp.c000066400000000000000000002721561262356443100150370ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * gtp.c: Contains all GTP functionality. Should be able to handle multiple * tunnels in the same program. * * TODO: * - Do we need to handle fragmentation? */ #ifdef __linux__ #define _GNU_SOURCE 1 #endif #include #include #if defined(__FreeBSD__) #include #endif #include "../config.h" #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include ISO C99 types */ #include "pdp.h" #include "gtp.h" #include "gtpie.h" #include "queue.h" /* According to section 14.2 of 3GPP TS 29.006 version 6.9.0 */ #define N3_REQUESTS 5 #define T3_REQUEST 3 /* Error reporting functions */ #define GTP_LOGPKG(pri, peer, pack, len, fmt, args...) \ logp2(DLGTP, pri, __FILE__, __LINE__, 0, \ "Packet from %s:%u, length: %d content: %s: " fmt, \ inet_ntoa((peer)->sin_addr), htons((peer)->sin_port), \ len, osmo_hexdump((const uint8_t *) pack, len), \ ##args); #define LOGP_WITH_ADDR(ss, level, addr, fmt, args...) \ LOGP(ss, level, "addr(%s:%d) " fmt, \ inet_ntoa((addr).sin_addr), htons((addr).sin_port), \ ##args); /* API Functions */ const char *gtp_version() { return VERSION; } /* gtp_new */ /* gtp_free */ int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi) { return pdp_newpdp(pdp, imsi, nsapi, NULL); } int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp) { return pdp_freepdp(pdp); } /* gtp_gpdu */ extern int gtp_fd(struct gsn_t *gsn) { return gsn->fd0; } /* gtp_decaps */ /* gtp_retrans */ /* gtp_retranstimeout */ int gtp_set_cb_unsup_ind(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer)) { gsn->cb_unsup_ind = cb; return 0; } int gtp_set_cb_extheader_ind(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer)) { gsn->cb_extheader_ind = cb; return 0; } /* API: Initialise delete context callback */ /* Called whenever a pdp context is deleted for any reason */ int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb) (struct pdp_t * pdp)) { gsn->cb_delete_context = cb; return 0; } int gtp_set_cb_conf(struct gsn_t *gsn, int (*cb) (int type, int cause, struct pdp_t * pdp, void *cbp)) { gsn->cb_conf = cb; return 0; } int gtp_set_cb_recovery(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer, uint8_t recovery)) { gsn->cb_recovery = cb; return 0; } extern int gtp_set_cb_data_ind(struct gsn_t *gsn, int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len)) { gsn->cb_data_ind = cb_data_ind; return 0; } /** * get_default_gtp() * Generate a GPRS Tunneling Protocol signalling packet header, depending * on GTP version and message type. pdp is used for teid/flow label. * *packet must be allocated by the calling function, and be large enough * to hold the packet header. * returns the length of the header. 0 on error. **/ static unsigned int get_default_gtp(int version, uint8_t type, void *packet) { struct gtp0_header *gtp0_default = (struct gtp0_header *)packet; struct gtp1_header_long *gtp1_default = (struct gtp1_header_long *)packet; switch (version) { case 0: /* Initialise "standard" GTP0 header */ memset(gtp0_default, 0, sizeof(struct gtp0_header)); gtp0_default->flags = 0x1e; gtp0_default->type = hton8(type); gtp0_default->spare1 = 0xff; gtp0_default->spare2 = 0xff; gtp0_default->spare3 = 0xff; gtp0_default->number = 0xff; return GTP0_HEADER_SIZE; case 1: /* Initialise "standard" GTP1 header */ /* 29.060: 8.2: S=1 and PN=0 */ /* 29.060 9.3.1: For GTP-U messages Echo Request, Echo Response */ /* and Supported Extension Headers Notification, the S field shall be */ /* set to 1 */ /* Currently extension headers are not supported */ memset(gtp1_default, 0, sizeof(struct gtp1_header_long)); gtp1_default->flags = 0x32; /* No extension, enable sequence, no N-PDU */ gtp1_default->type = hton8(type); return GTP1_HEADER_SIZE_LONG; default: LOGP(DLGTP, LOGL_ERROR, "Unknown GTP packet version: %d\n", version); return 0; } } /** * get_seq() * Get sequence number of a packet. * Returns 0 on error **/ static uint16_t get_seq(void *pack) { union gtp_packet *packet = (union gtp_packet *)pack; if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ return ntoh16(packet->gtp0.h.seq); } else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */ return ntoh16(packet->gtp1l.h.seq); } else { LOGP(DLGTP, LOGL_ERROR, "Unknown packet flag: %u\n", packet->flags); return 0; } } /** * get_tid() * Get tunnel identifier of a packet. * Returns 0 on error **/ static uint64_t get_tid(void *pack) { union gtp_packet *packet = (union gtp_packet *)pack; if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ return be64toh(packet->gtp0.h.tid); } return 0; } /** * get_hlen() * Get the header length of a packet. * Returns 0 on error **/ static uint16_t get_hlen(void *pack) { union gtp_packet *packet = (union gtp_packet *)pack; if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ return GTP0_HEADER_SIZE; } else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */ return GTP1_HEADER_SIZE_LONG; } else if ((packet->flags & 0xe7) == 0x20) { /* Short version 1 */ return GTP1_HEADER_SIZE_SHORT; } else { LOGP(DLGTP, LOGL_ERROR, "Unknown packet flag: %u\n", packet->flags); return 0; } } /** * get_tei() * Get the tunnel endpoint identifier (flow label) of a packet. * Returns 0xffffffff on error. **/ static uint32_t get_tei(void *pack) { union gtp_packet *packet = (union gtp_packet *)pack; if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ return ntoh16(packet->gtp0.h.flow); } else if ((packet->flags & 0xe0) == 0x20) { /* Version 1 */ return ntoh32(packet->gtp1l.h.tei); } else { LOGP(DLGTP, LOGL_ERROR, "Unknown packet flag: %u\n", packet->flags); return 0xffffffff; } } /* *********************************************************** * Reliable delivery of signalling messages * * Sequence numbers are used for both signalling messages and * data messages. * * For data messages each tunnel maintains a sequence counter, * which is incremented by one each time a new data message * is sent. The sequence number starts at (0) zero at tunnel * establishment, and wraps around at 65535 (29.060 9.3.1.1 * and 09.60 8.1.1.1). The sequence numbers are either ignored, * or can be used to check the validity of the message in the * receiver, or for reordering af packets. * * For signalling messages the sequence number is used by * signalling messages for which a response is defined. A response * message should copy the sequence from the corresponding request * message. The sequence number "unambiguously" identifies a request * message within a given path, with a path being defined as a set of * two endpoints (29.060 8.2, 29.060 7.6, 09.60 7.8). "All request * messages shall be responded to, and all response messages associated * with a certain request shall always include the same information" * * We take this to mean that the GSN transmitting a request is free to * choose the sequence number, as long as it is unique within a given path. * It means that we are allowed to count backwards, or roll over at 17 * if we prefer that. It also means that we can use the same counter for * all paths. This has the advantage that the transmitted request sequence * numbers are unique within each GSN, and also we dont have to mess around * with path setup and teardown. * * If a response message is lost, the request will be retransmitted, and * the receiving GSN will receive a "duplicated" request. The standard * requires the receiving GSN to send a response, with the same information * as in the original response. For most messages this happens automatically: * * Echo: Automatically dublicates the original response * Create pdp context: The SGSN may send create context request even if * a context allready exist (imsi+nsapi?). This means that the reply will automatically dublicate the original response. It might however have * side effects in the application which is asked twice to validate * the login. * Update pdp context: Automatically dublicates the original response??? * Delete pdp context. Automatically in gtp0, but in gtp1 will generate * a nonexist reply message. * * The correct solution will be to make a queue containing response messages. * This queue should be checked whenever a request is received. If the * response is allready in the queue that response should be transmitted. * It should be possible to find messages in this queue on the basis of * the sequence number and peer GSN IP address (The sequense number is unique * within each path). This need to be implemented by a hash table. Furthermore * it should be possibly to delete messages based on a timeout. This can be * achieved by means of a linked list. The timeout value need to be larger * than T3-RESPONSE * N3-REQUESTS (recommended value 5). These timers are * set in the peer GSN, so there is no way to know these parameters. On the * other hand the timeout value need to be so small that we do not receive * wraparound sequence numbere before the message is deleted. 60 seconds is * probably not a bad choise. * * This queue however is first really needed from gtp1. * * gtp_req: * Send off a signalling message with appropiate sequence * number. Store packet in queue. * gtp_conf: * Remove an incoming confirmation from the queue * gtp_resp: * Send off a response to a request. Use the same sequence * number in the response as in the request. * gtp_notification: * Send off a notification message. This is neither a request nor * a response. Both TEI and SEQ are zero. * gtp_retrans: * Retransmit any outstanding packets which have exceeded * a predefined timeout. *************************************************************/ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp, union gtp_packet *packet, int len, struct in_addr *inetaddr, void *cbp) { struct sockaddr_in addr; struct qmsg_t *qmsg; int fd; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr = *inetaddr; #if defined(__FreeBSD__) || defined(__APPLE__) addr.sin_len = sizeof(addr); #endif if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ addr.sin_port = htons(GTP0_PORT); packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE); packet->gtp0.h.seq = hton16(gsn->seq_next); if (pdp) { packet->gtp0.h.tid = htobe64(pdp_gettid(pdp->imsi, pdp->nsapi)); } if (pdp && ((packet->gtp0.h.type == GTP_GPDU) || (packet->gtp0.h.type == GTP_ERROR))) packet->gtp0.h.flow = hton16(pdp->flru); else if (pdp) packet->gtp0.h.flow = hton16(pdp->flrc); fd = gsn->fd0; } else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */ addr.sin_port = htons(GTP1C_PORT); packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT); packet->gtp1l.h.seq = hton16(gsn->seq_next); if (pdp && ((packet->gtp1l.h.type == GTP_GPDU) || (packet->gtp1l.h.type == GTP_ERROR))) packet->gtp1l.h.tei = hton32(pdp->teid_gn); else if (pdp) packet->gtp1l.h.tei = hton32(pdp->teic_gn); fd = gsn->fd1c; } else { LOGP(DLGTP, LOGL_ERROR, "Unknown packet flag: %u\n", packet->flags); return -1; } if (sendto(fd, packet, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { gsn->err_sendto++; LOGP(DLGTP, LOGL_ERROR, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd, (unsigned long)&packet, len, strerror(errno)); return -1; } /* Use new queue structure */ if (queue_newmsg(gsn->queue_req, &qmsg, &addr, gsn->seq_next)) { gsn->err_queuefull++; LOGP(DLGTP, LOGL_ERROR, "Retransmit queue is full\n"); } else { memcpy(&qmsg->p, packet, sizeof(union gtp_packet)); qmsg->l = len; qmsg->timeout = time(NULL) + T3_REQUEST; /* When to timeout */ qmsg->retrans = 0; /* No retransmissions so far */ qmsg->cbp = cbp; qmsg->type = ntoh8(packet->gtp0.h.type); qmsg->fd = fd; } gsn->seq_next++; /* Count up this time */ return 0; } /* gtp_conf * Remove signalling packet from retransmission queue. * return 0 on success, EOF if packet was not found */ int gtp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, union gtp_packet *packet, int len, uint8_t * type, void **cbp) { uint16_t seq; if ((packet->gtp0.h.flags & 0xe0) == 0x00) seq = ntoh16(packet->gtp0.h.seq); else if ((packet->gtp1l.h.flags & 0xe2) == 0x22) seq = ntoh16(packet->gtp1l.h.seq); else { GTP_LOGPKG(LOGL_ERROR, peer, packet, len, "Unknown GTP packet version\n"); return EOF; } if (queue_freemsg_seq(gsn->queue_req, peer, seq, type, cbp)) { gsn->err_seq++; GTP_LOGPKG(LOGL_ERROR, peer, packet, len, "Confirmation packet not found in queue\n"); return EOF; } return 0; } int gtp_retrans(struct gsn_t *gsn) { /* Retransmit any outstanding packets */ /* Remove from queue if maxretrans exceeded */ time_t now; struct qmsg_t *qmsg; now = time(NULL); /*printf("Retrans: New beginning %d\n", (int) now); */ /* get first element in queue, as long as the timeout of that * element has expired */ while ((!queue_getfirst(gsn->queue_req, &qmsg)) && (qmsg->timeout <= now)) { /*printf("Retrans timeout found: %d\n", (int) time(NULL)); */ if (qmsg->retrans > N3_REQUESTS) { /* To many retrans */ if (gsn->cb_conf) gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp); queue_freemsg(gsn->queue_req, qmsg); } else { if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0, (struct sockaddr *)&qmsg->peer, sizeof(struct sockaddr_in)) < 0) { gsn->err_sendto++; LOGP(DLGTP, LOGL_ERROR, "Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s\n", gsn->fd0, (unsigned long)&qmsg->p, qmsg->l, strerror(errno)); } queue_back(gsn->queue_req, qmsg); qmsg->timeout = now + T3_REQUEST; qmsg->retrans++; } } /* Also clean up reply timeouts */ while ((!queue_getfirst(gsn->queue_resp, &qmsg)) && (qmsg->timeout < now)) { /*printf("Retrans (reply) timeout found: %d\n", (int) time(NULL)); */ queue_freemsg(gsn->queue_resp, qmsg); } return 0; } int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) { time_t now, later; struct qmsg_t *qmsg; if (queue_getfirst(gsn->queue_req, &qmsg)) { timeout->tv_sec = 10; timeout->tv_usec = 0; } else { now = time(NULL); later = qmsg->timeout; timeout->tv_sec = later - now; timeout->tv_usec = 0; if (timeout->tv_sec < 0) timeout->tv_sec = 0; /* No negative allowed */ if (timeout->tv_sec > 10) timeout->tv_sec = 10; /* Max sleep for 10 sec */ } return 0; } int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp, union gtp_packet *packet, int len, struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid) { struct qmsg_t *qmsg; if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE); packet->gtp0.h.seq = hton16(seq); packet->gtp0.h.tid = htobe64(tid); if (pdp && ((packet->gtp0.h.type == GTP_GPDU) || (packet->gtp0.h.type == GTP_ERROR))) packet->gtp0.h.flow = hton16(pdp->flru); else if (pdp) packet->gtp0.h.flow = hton16(pdp->flrc); } else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */ packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT); packet->gtp1l.h.seq = hton16(seq); if (pdp && (fd == gsn->fd1u)) packet->gtp1l.h.tei = hton32(pdp->teid_gn); else if (pdp) packet->gtp1l.h.tei = hton32(pdp->teic_gn); } else { LOGP(DLGTP, LOGL_ERROR, "Unknown packet flag: %u\n", packet->flags); return -1; } if (fcntl(fd, F_SETFL, 0)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } if (sendto(fd, packet, len, 0, (struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) { gsn->err_sendto++; LOGP(DLGTP, LOGL_ERROR, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd, (unsigned long)&packet, len, strerror(errno)); return -1; } /* Use new queue structure */ if (queue_newmsg(gsn->queue_resp, &qmsg, peer, seq)) { gsn->err_queuefull++; LOGP(DLGTP, LOGL_ERROR, "Retransmit queue is full\n"); } else { memcpy(&qmsg->p, packet, sizeof(union gtp_packet)); qmsg->l = len; qmsg->timeout = time(NULL) + 60; /* When to timeout */ qmsg->retrans = 0; /* No retransmissions so far */ qmsg->cbp = NULL; qmsg->type = 0; qmsg->fd = fd; } return 0; } int gtp_notification(struct gsn_t *gsn, int version, union gtp_packet *packet, int len, struct sockaddr_in *peer, int fd, uint16_t seq) { struct sockaddr_in addr; memcpy(&addr, peer, sizeof(addr)); /* In GTP0 notifications are treated as replies. In GTP1 they are requests for which there is no reply */ if (fd == gsn->fd1c) addr.sin_port = htons(GTP1C_PORT); else if (fd == gsn->fd1u) addr.sin_port = htons(GTP1C_PORT); if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE); packet->gtp0.h.seq = hton16(seq); } else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */ packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT); packet->gtp1l.h.seq = hton16(seq); } else { LOGP(DLGTP, LOGL_ERROR, "Unknown packet flag: %u\n", packet->flags); return -1; } if (fcntl(fd, F_SETFL, 0)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } if (sendto(fd, packet, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { gsn->err_sendto++; LOGP(DLGTP, LOGL_ERROR, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd, (unsigned long)&packet, len, strerror(errno)); return -1; } return 0; } int gtp_dublicate(struct gsn_t *gsn, int version, struct sockaddr_in *peer, uint16_t seq) { struct qmsg_t *qmsg; if (queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) { return EOF; /* Notfound */ } if (fcntl(qmsg->fd, F_SETFL, 0)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0, (struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) { gsn->err_sendto++; LOGP(DLGTP, LOGL_ERROR, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", qmsg->fd, (unsigned long)&qmsg->p, qmsg->l, strerror(errno)); } return 0; } /* Perform restoration and recovery error handling as described in 29.060 */ static void log_restart(struct gsn_t *gsn) { FILE *f; int i, rc; int counter = 0; char filename[NAMESIZE]; filename[NAMESIZE - 1] = 0; /* No null term. guarantee by strncpy */ strncpy(filename, gsn->statedir, NAMESIZE - 1); strncat(filename, RESTART_FILE, NAMESIZE - 1 - sizeof(RESTART_FILE)); i = umask(022); /* We try to open file. On failure we will later try to create file */ if (!(f = fopen(filename, "r"))) { LOGP(DLGTP, LOGL_NOTICE, "State information file (%s) not found. Creating new file.\n", filename); } else { umask(i); rc = fscanf(f, "%d", &counter); if (rc != 1) { LOGP(DLGTP, LOGL_ERROR, "fscanf failed to read counter value\n"); return; } if (fclose(f)) { LOGP(DLGTP, LOGL_ERROR, "fclose failed: Error = %s\n", strerror(errno)); } } gsn->restart_counter = (unsigned char)counter; gsn->restart_counter++; if (!(f = fopen(filename, "w"))) { LOGP(DLGTP, LOGL_ERROR, "fopen(path=%s, mode=%s) failed: Error = %s\n", filename, "w", strerror(errno)); return; } umask(i); fprintf(f, "%d\n", gsn->restart_counter); if (fclose(f)) { LOGP(DLGTP, LOGL_ERROR, "fclose failed: Error = %s\n", strerror(errno)); return; } } int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, int mode) { struct sockaddr_in addr; LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started\n"); *gsn = calloc(sizeof(struct gsn_t), 1); /* TODO */ (*gsn)->statedir = statedir; log_restart(*gsn); /* Initialise sequence number */ (*gsn)->seq_next = (*gsn)->restart_counter * 1024; /* Initialise request retransmit queue */ queue_new(&(*gsn)->queue_req); queue_new(&(*gsn)->queue_resp); /* Initialise pdp table */ pdp_init(); /* Initialise call back functions */ (*gsn)->cb_create_context_ind = 0; (*gsn)->cb_delete_context = 0; (*gsn)->cb_unsup_ind = 0; (*gsn)->cb_conf = 0; (*gsn)->cb_data_ind = 0; /* Store function parameters */ (*gsn)->gsnc = *listen; (*gsn)->gsnu = *listen; (*gsn)->mode = mode; /* Create GTP version 0 socket */ if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { (*gsn)->err_socket++; LOGP(DLGTP, LOGL_ERROR, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n", AF_INET, SOCK_DGRAM, 0, strerror(errno)); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr = *listen; /* Same IP for user traffic and signalling */ addr.sin_port = htons(GTP0_PORT); #if defined(__FreeBSD__) || defined(__APPLE__) addr.sin_len = sizeof(addr); #endif if (bind((*gsn)->fd0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { (*gsn)->err_socket++; LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr, "bind(fd0=%d) failed: Error = %s\n", (*gsn)->fd0, strerror(errno)); return -1; } /* Create GTP version 1 control plane socket */ if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { (*gsn)->err_socket++; LOGP(DLGTP, LOGL_ERROR, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n", AF_INET, SOCK_DGRAM, 0, strerror(errno)); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr = *listen; /* Same IP for user traffic and signalling */ addr.sin_port = htons(GTP1C_PORT); #if defined(__FreeBSD__) || defined(__APPLE__) addr.sin_len = sizeof(addr); #endif if (bind((*gsn)->fd1c, (struct sockaddr *)&addr, sizeof(addr)) < 0) { (*gsn)->err_socket++; LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr, "bind(fd1c=%d) failed: Error = %s\n", (*gsn)->fd1c, strerror(errno)); return -1; } /* Create GTP version 1 user plane socket */ if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { (*gsn)->err_socket++; LOGP(DLGTP, LOGL_ERROR, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n", AF_INET, SOCK_DGRAM, 0, strerror(errno)); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr = *listen; /* Same IP for user traffic and signalling */ addr.sin_port = htons(GTP1U_PORT); #if defined(__FreeBSD__) || defined(__APPLE__) addr.sin_len = sizeof(addr); #endif if (bind((*gsn)->fd1u, (struct sockaddr *)&addr, sizeof(addr)) < 0) { (*gsn)->err_socket++; LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr, "bind(fd1u=%d) failed: Error = %s\n", (*gsn)->fd1u, strerror(errno)); return -1; } return 0; } int gtp_free(struct gsn_t *gsn) { /* Clean up retransmit queues */ queue_free(gsn->queue_req); queue_free(gsn->queue_resp); close(gsn->fd0); close(gsn->fd1c); close(gsn->fd1u); free(gsn); return 0; } /* *********************************************************** * Path management messages * Messages: echo and version not supported. * A path is connection between two UDP/IP endpoints * * A path is either using GTP0 or GTP1. A path can be * established by any kind of GTP message?? * Which source port to use? * GTP-C request destination port is 2123/3386 * GTP-U request destination port is 2152/3386 * T-PDU destination port is 2152/3386. * For the above messages the source port is locally allocated. * For response messages src=rx-dst and dst=rx-src. * For simplicity we should probably use 2123+2152/3386 as * src port even for the cases where src can be locally * allocated. This also means that we have to listen only to * the same ports. * For response messages we need to be able to respond to * the relevant src port even if it is locally allocated by * the peer. * * The need for path management! * We might need to keep a list of active paths. This might * be in the form of remote IP address + UDP port numbers. * (We will consider a path astablished if we have a context * with the node in question) *************************************************************/ /* Send off an echo request */ int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp, struct in_addr *inetaddr) { union gtp_packet packet; unsigned int length = get_default_gtp(version, GTP_ECHO_REQ, &packet); return gtp_req(gsn, version, NULL, &packet, length, inetaddr, cbp); } /* Send off an echo reply */ int gtp_echo_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { union gtp_packet packet; unsigned int length = get_default_gtp(version, GTP_ECHO_RSP, &packet); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, gsn->restart_counter); return gtp_resp(version, gsn, NULL, &packet, length, peer, fd, get_seq(pack), get_tid(pack)); } /* Handle a received echo request */ int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { /* Check if it was a dublicate request */ if (!gtp_dublicate(gsn, 0, peer, get_seq(pack))) return 0; /* Send off reply to request */ return gtp_echo_resp(gsn, version, peer, fd, pack, len); } /* Handle a received echo reply */ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len) { union gtpie_member *ie[GTPIE_SIZE]; unsigned char recovery; void *cbp = NULL; uint8_t type = 0; int hlen = get_hlen(pack); /* Remove packet from queue */ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; /* Extract information elements into a pointer array */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp); return EOF; } if (gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp); return EOF; } /* Echo reply packages does not have a cause information element */ /* Instead we return the recovery number in the callback function */ if (gsn->cb_conf) gsn->cb_conf(type, recovery, NULL, cbp); if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); return 0; } /* Send off a Version Not Supported message */ /* This message is somewhat special in that it actually is a * response to some other message with unsupported GTP version * For this reason it has parameters like a response, and does * its own message transmission. No signalling queue is used * The reply is sent to the peer IP and peer UDP. This means that * the peer will be receiving a GTP0 message on a GTP1 port! * In practice however this will never happen as a GTP0 GSN will * only listen to the GTP0 port, and therefore will never receive * anything else than GTP0 */ int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { union gtp_packet packet; /* GTP 1 is the highest supported protocol */ unsigned int length = get_default_gtp(1, GTP_NOT_SUPPORTED, &packet); return gtp_notification(gsn, version, &packet, length, peer, fd, 0); } /* Handle a Version Not Supported message */ int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer, void *pack, unsigned len) { if (gsn->cb_unsup_ind) gsn->cb_unsup_ind(peer); return 0; } /* Send off an Supported Extension Headers Notification */ int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { union gtp_packet packet; unsigned int length = get_default_gtp(version, GTP_SUPP_EXT_HEADER, &packet); uint8_t pdcp_pdu = GTP_EXT_PDCP_PDU; if (version < 1) return 0; /* We report back that we support only PDCP PDU headers */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EXT_HEADER_T, sizeof(pdcp_pdu), &pdcp_pdu); return gtp_notification(gsn, version, &packet, length, peer, fd, get_seq(pack)); } /* Handle a Supported Extension Headers Notification */ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer, void *pack, unsigned len) { if (gsn->cb_extheader_ind) gsn->cb_extheader_ind(peer); return 0; } /* *********************************************************** * Session management messages * Messages: create, update and delete PDP context * * Information storage * Information storage for each PDP context is defined in * 23.060 section 13.3. Includes IMSI, MSISDN, APN, PDP-type, * PDP-address (IP address), sequence numbers, charging ID. * For the SGSN it also includes radio related mobility * information. *************************************************************/ /* API: Send Create PDP Context Request (7.3.1) */ extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp) { union gtp_packet packet; unsigned int length = get_default_gtp(pdp->version, GTP_CREATE_PDP_REQ, &packet); struct pdp_t *linked_pdp = NULL; /* TODO: Secondary PDP Context Activation Procedure */ /* In secondary activation procedure the PDP context is identified by tei in the header. The following fields are omitted: Selection mode, IMSI, MSISDN, End User Address, Access Point Name and Protocol Configuration Options */ if (pdp->secondary) { if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { LOGP(DLGTP, LOGL_ERROR, "Unknown linked PDP context: %u\n", pdp->teic_own); return EOF; } } if (pdp->version == 0) { gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, sizeof(pdp->qos_req0), pdp->qos_req0); } /* Section 7.7.2 */ if (pdp->version == 1) { if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_IMSI, sizeof(pdp->imsi), (uint8_t *) & pdp->imsi); } /* Section 7.7.3 Routing Area Information */ if (pdp->rai_given == 1) gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_RAI, pdp->rai.l, (uint8_t *) & pdp->rai.v); /* Section 7.7.11 */ if (pdp->norecovery_given == 0) gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, gsn->restart_counter); /* Section 7.7.12 */ if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_SELECTION_MODE, pdp->selmode); if (pdp->version == 0) { gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI, pdp->fllu); gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C, pdp->fllc); } /* Section 7.7.13 */ if (pdp->version == 1) { gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI, pdp->teid_own); /* Section 7.7.14 */ if (!pdp->teic_confirmed) gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, pdp->teic_own); /* Section 7.7.17 */ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); /* Section 7.7.17 */ if (pdp->secondary) /* Secondary PDP Context Activation Procedure */ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, linked_pdp->nsapi); /* Section 7.7.23 */ if (pdp->cch_pdp) /* Only include charging if flags are set */ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_CHARGING_C, pdp->cch_pdp); } /* TODO gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF, pdp->traceref); gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE, pdp->tracetype); */ /* Section 7.7.27 */ if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, pdp->eua.l, pdp->eua.v); /* Section 7.7.30 */ if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_APN, pdp->apn_use.l, pdp->apn_use.v); /* Section 7.7.31 */ if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ if (pdp->pco_req.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO, pdp->pco_req.l, pdp->pco_req.v); /* Section 7.7.32 */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlc.l, pdp->gsnlc.v); /* Section 7.7.32 */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlu.l, pdp->gsnlu.v); /* Section 7.7.33 */ if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MSISDN, pdp->msisdn.l, pdp->msisdn.v); /* Section 7.7.34 */ if (pdp->version == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE, pdp->qos_req.l, pdp->qos_req.v); /* Section 7.7.36 */ if ((pdp->version == 1) && pdp->tft.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TFT, pdp->tft.l, pdp->tft.v); /* Section 7.7.41 */ if ((pdp->version == 1) && pdp->triggerid.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TRIGGER_ID, pdp->triggerid.l, pdp->triggerid.v); /* Section 7.7.42 */ if ((pdp->version == 1) && pdp->omcid.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID, pdp->omcid.l, pdp->omcid.v); /* new R7 fields */ if (pdp->rattype_given == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_RAT_TYPE, pdp->rattype.l, pdp->rattype.v); if (pdp->userloc_given == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_USER_LOC, pdp->userloc.l, pdp->userloc.v); if (pdp->mstz_given == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MS_TZ, pdp->mstz.l, pdp->mstz.v); if (pdp->imeisv_given == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_IMEI_SV, pdp->imeisv.l, pdp->imeisv.v); /* TODO hisaddr0 */ gtp_req(gsn, pdp->version, pdp, &packet, length, &pdp->hisaddr0, cbp); return 0; } /* API: Application response to context indication */ int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause) { /* Now send off a reply to the peer */ gtp_create_pdp_resp(gsn, pdp->version, pdp, cause); if (cause != GTPCAUSE_ACC_REQ) { pdp_freepdp(pdp); } return 0; } /* API: Register create context indication callback */ int gtp_set_cb_create_context_ind(struct gsn_t *gsn, int (*cb_create_context_ind) (struct pdp_t * pdp)) { gsn->cb_create_context_ind = cb_create_context_ind; return 0; } /* Send Create PDP Context Response */ int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp, uint8_t cause) { union gtp_packet packet; unsigned int length = get_default_gtp(version, GTP_CREATE_PDP_RSP, &packet); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause); if (cause == GTPCAUSE_ACC_REQ) { if (version == 0) gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, sizeof(pdp->qos_neg0), pdp->qos_neg0); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_REORDER, pdp->reorder); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, gsn->restart_counter); if (version == 0) { gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI, pdp->fllu); gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C, pdp->fllc); } if (version == 1) { gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI, pdp->teid_own); gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, pdp->teic_own); } /* TODO: We use teic_own as charging ID */ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID, pdp->teic_own); gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, pdp->eua.l, pdp->eua.v); if (pdp->pco_neg.l) { /* Optional PCO */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO, pdp->pco_neg.l, pdp->pco_neg.v); } gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlc.l, pdp->gsnlc.v); gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlu.l, pdp->gsnlu.v); if (version == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE, pdp->qos_neg.l, pdp->qos_neg.v); /* TODO: Charging gateway address */ } return gtp_resp(version, gsn, pdp, &packet, length, &pdp->sa_peer, pdp->fd, pdp->seq, pdp->tid); } /* Handle Create PDP Context Request */ int gtp_create_pdp_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { struct pdp_t *pdp, *pdp_old; struct pdp_t pdp_buf; union gtpie_member *ie[GTPIE_SIZE]; uint8_t recovery; uint16_t seq = get_seq(pack); int hlen = get_hlen(pack); uint8_t linked_nsapi = 0; struct pdp_t *linked_pdp = NULL; if (!gtp_dublicate(gsn, version, peer, seq)) return 0; pdp = &pdp_buf; memset(pdp, 0, sizeof(struct pdp_t)); if (version == 0) { uint64_t tid = be64toh(((union gtp_packet *)pack)->gtp0.h.tid); pdp_set_imsi_nsapi(pdp, tid); } pdp->seq = seq; pdp->sa_peer = *peer; pdp->fd = fd; pdp->version = version; /* Decode information elements */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (0 == version) return EOF; else return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_INVALID_MESSAGE); } if (version == 1) { /* Linked NSAPI (conditional) */ /* If included this is the Secondary PDP Context Activation Procedure */ /* In secondary activation IMSI is not included, so the context must be */ /* identified by the tei */ if (!gtpie_gettv1(ie, GTPIE_NSAPI, 1, &linked_nsapi)) { /* Find the primary PDP context */ if (pdp_getgtp1(&linked_pdp, get_tei(pack))) { gsn->incorrect++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Incorrect optional information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_OPT_IE_INCORRECT); } /* Check that the primary PDP context matches linked nsapi */ if (linked_pdp->nsapi != linked_nsapi) { gsn->incorrect++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Incorrect optional information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_OPT_IE_INCORRECT); } /* Copy parameters from primary context */ pdp->selmode = linked_pdp->selmode; pdp->imsi = linked_pdp->imsi; pdp->msisdn = linked_pdp->msisdn; pdp->eua = linked_pdp->eua; pdp->pco_req = linked_pdp->pco_req; pdp->apn_req = linked_pdp->apn_req; pdp->teic_gn = linked_pdp->teic_gn; pdp->secondary = 1; } } /* if (version == 1) */ if (version == 0) { if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, pdp->qos_req0, sizeof(pdp->qos_req0))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } if ((version == 1) && (!linked_pdp)) { /* Not Secondary PDP Context Activation Procedure */ /* IMSI (conditional) */ if (gtpie_gettv0 (ie, GTPIE_IMSI, 0, &pdp->imsi, sizeof(pdp->imsi))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } /* Recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); } /* Selection mode (conditional) */ if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ if (gtpie_gettv0(ie, GTPIE_SELECTION_MODE, 0, &pdp->selmode, sizeof(pdp->selmode))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } if (version == 0) { if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } if (version == 1) { /* TEID (mandatory) */ if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } /* TEIC (conditional) */ if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } /* NSAPI (mandatory) */ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &pdp->nsapi)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } /* Charging Characteriatics (optional) */ /* Trace reference (optional) */ /* Trace type (optional) */ /* Charging Characteriatics (optional) */ if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ /* End User Address (conditional) */ if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l, &pdp->eua.v, sizeof(pdp->eua.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } /* APN */ if (gtpie_gettlv(ie, GTPIE_APN, 0, &pdp->apn_req.l, &pdp->apn_req.v, sizeof(pdp->apn_req.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } /* Extract protocol configuration options (optional) */ if (!gtpie_gettlv(ie, GTPIE_PCO, 0, &pdp->pco_req.l, &pdp->pco_req.v, sizeof(pdp->pco_req.v))) { } } /* SGSN address for signalling (mandatory) */ if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l, &pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } /* SGSN address for user traffic (mandatory) */ if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l, &pdp->gsnru.v, sizeof(pdp->gsnru.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ /* MSISDN (conditional) */ if (gtpie_gettlv(ie, GTPIE_MSISDN, 0, &pdp->msisdn.l, &pdp->msisdn.v, sizeof(pdp->msisdn.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } } if (version == 1) { /* QoS (mandatory) */ if (gtpie_gettlv(ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_req.l, &pdp->qos_req.v, sizeof(pdp->qos_req.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } /* TFT (conditional) */ if (gtpie_gettlv(ie, GTPIE_TFT, 0, &pdp->tft.l, &pdp->tft.v, sizeof(pdp->tft.v))) { } /* Trigger ID */ /* OMC identity */ } /* Initialize our own IP addresses */ in_addr2gsna(&pdp->gsnlc, &gsn->gsnc); in_addr2gsna(&pdp->gsnlu, &gsn->gsnu); DEBUGP(DLGTP, "gtp_create_pdp_ind: Before pdp_tidget\n"); if (!pdp_getimsi(&pdp_old, pdp->imsi, pdp->nsapi)) { /* Found old pdp with same tid. Now the voodoo begins! */ /* 09.60 / 29.060 allows create on existing context to "steal" */ /* the context which was allready established */ /* We check that the APN, selection mode and MSISDN is the same */ DEBUGP(DLGTP, "gtp_create_pdp_ind: Old context found\n"); if ((pdp->apn_req.l == pdp_old->apn_req.l) && (!memcmp (pdp->apn_req.v, pdp_old->apn_req.v, pdp->apn_req.l)) && (pdp->selmode == pdp_old->selmode) && (pdp->msisdn.l == pdp_old->msisdn.l) && (!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l))) { /* OK! We are dealing with the same APN. We will copy new * parameters to the old pdp and send off confirmation * We ignore the following information elements: * QoS: MS will get originally negotiated QoS. * End user address (EUA). MS will get old EUA anyway. * Protocol configuration option (PCO): Only application can verify */ DEBUGP(DLGTP, "gtp_create_pdp_ind: Old context found\n"); /* Copy remote flow label */ pdp_old->flru = pdp->flru; pdp_old->flrc = pdp->flrc; /* Copy remote tei */ pdp_old->teid_gn = pdp->teid_gn; pdp_old->teic_gn = pdp->teic_gn; /* Copy peer GSN address */ pdp_old->gsnrc.l = pdp->gsnrc.l; memcpy(&pdp_old->gsnrc.v, &pdp->gsnrc.v, pdp->gsnrc.l); pdp_old->gsnru.l = pdp->gsnru.l; memcpy(&pdp_old->gsnru.v, &pdp->gsnru.v, pdp->gsnru.l); /* Copy request parameters */ pdp_old->seq = pdp->seq; pdp_old->sa_peer = pdp->sa_peer; pdp_old->fd = pdp->fd = fd; pdp_old->version = pdp->version = version; /* Switch to using the old pdp context */ pdp = pdp_old; /* Confirm to peer that things were "successful" */ return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_ACC_REQ); } else { /* This is not the same PDP context. Delete the old one. */ DEBUGP(DLGTP, "gtp_create_pdp_ind: Deleting old context\n"); if (gsn->cb_delete_context) gsn->cb_delete_context(pdp_old); pdp_freepdp(pdp_old); DEBUGP(DLGTP, "gtp_create_pdp_ind: Deleted...\n"); } } pdp_newpdp(&pdp, pdp->imsi, pdp->nsapi, pdp); /* Callback function to validata login */ if (gsn->cb_create_context_ind != 0) return gsn->cb_create_context_ind(pdp); else { GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "No create_context_ind callback defined\n"); return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_NOT_SUPPORTED); } } /* Handle Create PDP Context Response */ int gtp_create_pdp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len) { struct pdp_t *pdp; union gtpie_member *ie[GTPIE_SIZE]; uint8_t cause, recovery; void *cbp = NULL; uint8_t type = 0; int hlen = get_hlen(pack); /* Remove packet from queue */ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; /* Find the context in question */ if (pdp_getgtp1(&pdp, get_tei(pack))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context: %u\n", get_tei(pack)); if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp); return EOF; } /* Register that we have received a valid teic from GGSN */ pdp->teic_confirmed = 1; /* Decode information elements */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } /* Extract cause value (mandatory) */ if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } /* Extract recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); } /* Extract protocol configuration options (optional) */ if (!gtpie_gettlv(ie, GTPIE_PCO, 0, &pdp->pco_req.l, &pdp->pco_req.v, sizeof(pdp->pco_req.v))) { } /* Check all conditional information elements */ if (GTPCAUSE_ACC_REQ == cause) { if (version == 0) { if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, &pdp->qos_neg0, sizeof(pdp->qos_neg0))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } } if (gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } if (version == 0) { if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } } if (version == 1) { if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } } if (gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ } if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l, &pdp->eua.v, sizeof(pdp->eua.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l, &pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l, &pdp->gsnru.v, sizeof(pdp->gsnru.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } if (version == 1) { if (gtpie_gettlv (ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_neg.l, &pdp->qos_neg.v, sizeof(pdp->qos_neg.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } } } if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp); return 0; } /* API: Send Update PDP Context Request */ int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, struct in_addr *inetaddr) { union gtp_packet packet; unsigned int length = get_default_gtp(pdp->version, GTP_UPDATE_PDP_REQ, &packet); if (pdp->version == 0) gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, sizeof(pdp->qos_req0), pdp->qos_req0); /* Include IMSI if updating with unknown teic_gn */ if ((pdp->version == 1) && (!pdp->teic_gn)) gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_IMSI, sizeof(pdp->imsi), (uint8_t *) & pdp->imsi); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, gsn->restart_counter); if (pdp->version == 0) { gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI, pdp->fllu); gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C, pdp->fllc); } if (pdp->version == 1) { gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI, pdp->teid_own); if (!pdp->teic_confirmed) gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, pdp->teic_own); } gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); /* TODO gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF, pdp->traceref); gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE, pdp->tracetype); */ /* TODO if ggsn update message gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, pdp->eua.l, pdp->eua.v); */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlc.l, pdp->gsnlc.v); gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlu.l, pdp->gsnlu.v); if (pdp->version == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE, pdp->qos_req.l, pdp->qos_req.v); if ((pdp->version == 1) && pdp->tft.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TFT, pdp->tft.l, pdp->tft.v); if ((pdp->version == 1) && pdp->triggerid.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TRIGGER_ID, pdp->triggerid.l, pdp->triggerid.v); if ((pdp->version == 1) && pdp->omcid.l) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID, pdp->omcid.l, pdp->omcid.v); gtp_req(gsn, pdp->version, NULL, &packet, length, inetaddr, cbp); return 0; } /* Send Update PDP Context Response */ int gtp_update_pdp_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len, struct pdp_t *pdp, uint8_t cause) { union gtp_packet packet; unsigned int length = get_default_gtp(version, GTP_UPDATE_PDP_RSP, &packet); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause); if (cause == GTPCAUSE_ACC_REQ) { if (version == 0) gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, sizeof(pdp->qos_neg0), pdp->qos_neg0); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, gsn->restart_counter); if (version == 0) { gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI, pdp->fllu); gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C, pdp->fllc); } if (version == 1) { gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI, pdp->teid_own); if (!pdp->teic_confirmed) gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, pdp->teic_own); } /* TODO we use teid_own as charging ID address */ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID, pdp->teid_own); /* If ggsn gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, pdp->eua.l, pdp->eua.v); */ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlc.l, pdp->gsnlc.v); gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, pdp->gsnlu.l, pdp->gsnlu.v); if (version == 1) gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE, pdp->qos_neg.l, pdp->qos_neg.v); /* TODO: Charging gateway address */ } return gtp_resp(version, gsn, pdp, &packet, length, peer, fd, get_seq(pack), get_tid(pack)); } /* Handle Update PDP Context Request */ int gtp_update_pdp_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { struct pdp_t *pdp; struct pdp_t pdp_backup; union gtpie_member *ie[GTPIE_SIZE]; uint8_t recovery; uint16_t seq = get_seq(pack); int hlen = get_hlen(pack); uint64_t imsi; uint8_t nsapi; /* Is this a dublicate ? */ if (!gtp_dublicate(gsn, version, peer, seq)) { return 0; /* We allready send of response once */ } /* Decode information elements */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (0 == version) return EOF; else return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, NULL, GTPCAUSE_INVALID_MESSAGE); } /* Finding PDP: */ /* For GTP0 we use the tunnel identifier to provide imsi and nsapi. */ /* For GTP1 we must use imsi and nsapi if imsi is present. Otherwise */ /* we have to use the tunnel endpoint identifier */ if (version == 0) { uint64_t tid = be64toh(((union gtp_packet *)pack)->gtp0.h.tid); pdp_set_imsi_nsapi(pdp, tid); /* Find the context in question */ if (pdp_getimsi(&pdp, imsi, nsapi)) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, NULL, GTPCAUSE_NON_EXIST); } } else if (version == 1) { /* NSAPI (mandatory) */ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &nsapi)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, NULL, GTPCAUSE_MAN_IE_MISSING); } /* IMSI (conditional) */ if (gtpie_gettv0(ie, GTPIE_IMSI, 0, &imsi, sizeof(imsi))) { /* Find the context in question */ if (pdp_getgtp1(&pdp, get_tei(pack))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context: %u\n", get_tei(pack)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, NULL, GTPCAUSE_NON_EXIST); } } else { /* Find the context in question */ if (pdp_getimsi(&pdp, imsi, nsapi)) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, NULL, GTPCAUSE_NON_EXIST); } } } else { LOGP(DLGTP, LOGL_ERROR, "Unknown version: %d\n", version); return EOF; } /* Make a backup copy in case anything is wrong */ memcpy(&pdp_backup, pdp, sizeof(pdp_backup)); if (version == 0) { if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, pdp->qos_req0, sizeof(pdp->qos_req0))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } } /* Recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); } if (version == 0) { if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } } if (version == 1) { /* TEID (mandatory) */ if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } /* TEIC (conditional) */ /* If TEIC is not included it means that we have allready received it */ /* TODO: From 29.060 it is not clear if TEI_C MUST be included for */ /* all updated contexts, or only for one of the linked contexts */ gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn); /* NSAPI (mandatory) */ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &pdp->nsapi)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } } /* Trace reference (optional) */ /* Trace type (optional) */ /* End User Address (conditional) TODO: GGSN Initiated if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l, &pdp->eua.v, sizeof(pdp->eua.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING); } */ /* SGSN address for signalling (mandatory) */ /* It is weird that this is mandatory when TEIC is conditional */ if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l, &pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } /* SGSN address for user traffic (mandatory) */ if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l, &pdp->gsnru.v, sizeof(pdp->gsnru.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } if (version == 1) { /* QoS (mandatory) */ if (gtpie_gettlv(ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_req.l, &pdp->qos_req.v, sizeof(pdp->qos_req.v))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_MAN_IE_MISSING); } /* TFT (conditional) */ if (gtpie_gettlv(ie, GTPIE_TFT, 0, &pdp->tft.l, &pdp->tft.v, sizeof(pdp->tft.v))) { } /* OMC identity */ } /* Confirm to peer that things were "successful" */ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp, GTPCAUSE_ACC_REQ); } /* Handle Update PDP Context Response */ int gtp_update_pdp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len) { struct pdp_t *pdp; union gtpie_member *ie[GTPIE_SIZE]; uint8_t cause, recovery; void *cbp = NULL; uint8_t type = 0; /* Remove packet from queue */ if (gtp_conf(gsn, 0, peer, pack, len, &type, &cbp)) return EOF; /* TODO This function is called from gtp_decaps1c() (for GTP v1) but * uses gtp0.h.flow (GTP v0 data element) */ /* Find the context in question */ if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, cbp); return EOF; } /* Register that we have received a valid teic from GGSN */ pdp->teic_confirmed = 1; /* TODO This function is called from gtp_decaps1c() (for GTP v1) but * explicitly passes version 0 and GTP0_HEADER_SIZE to gtpie_decaps() */ /* Decode information elements */ if (gtpie_decaps (ie, 0, pack + GTP0_HEADER_SIZE, len - GTP0_HEADER_SIZE)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } /* Extract cause value (mandatory) */ if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } /* Extract recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); } /* Check all conditional information elements */ if (GTPCAUSE_ACC_REQ != cause) { if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return 0; } else { /* Check for missing conditionary information elements */ if (!(gtpie_exist(ie, GTPIE_QOS_PROFILE0, 0) && gtpie_exist(ie, GTPIE_REORDER, 0) && gtpie_exist(ie, GTPIE_FL_DI, 0) && gtpie_exist(ie, GTPIE_FL_C, 0) && gtpie_exist(ie, GTPIE_CHARGING_ID, 0) && gtpie_exist(ie, GTPIE_EUA, 0) && gtpie_exist(ie, GTPIE_GSN_ADDR, 0) && gtpie_exist(ie, GTPIE_GSN_ADDR, 1))) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing conditional information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp); /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); */ return EOF; } /* Update pdp with new values */ gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, pdp->qos_neg0, sizeof(pdp->qos_neg0)); gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder); gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru); gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc); gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid); gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l, &pdp->eua.v, sizeof(pdp->eua.v)); gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l, &pdp->gsnrc.v, sizeof(pdp->gsnrc.v)); gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l, &pdp->gsnru.v, sizeof(pdp->gsnru.v)); if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp); return 0; /* Succes */ } } /* API: Send Delete PDP Context Request */ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, int teardown) { union gtp_packet packet; unsigned int length = get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet); struct in_addr addr; struct pdp_t *linked_pdp; struct pdp_t *secondary_pdp; int n; int count = 0; if (gsna2in_addr(&addr, &pdp->gsnrc)) { gsn->err_address++; LOGP(DLGTP, LOGL_ERROR, "GSN address conversion failed\n"); return EOF; } if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { LOGP(DLGTP, LOGL_ERROR, "Unknown linked PDP context: %u\n", pdp->teic_own); return EOF; } if (!teardown) { for (n = 0; n < PDP_MAXNSAPI; n++) if (linked_pdp->secondary_tei[n]) count++; if (count <= 1) { LOGP(DLGTP, LOGL_ERROR, "Must use teardown for last context: %d\n", count); return EOF; } } if (pdp->version == 1) { if (teardown) gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_TEARDOWN, 0xff); gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); } gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp); if (teardown) { /* Remove all contexts */ for (n = 0; n < PDP_MAXNSAPI; n++) { if (linked_pdp->secondary_tei[n]) { if (pdp_getgtp1 (&secondary_pdp, linked_pdp->secondary_tei[n])) { LOGP(DLGTP, LOGL_ERROR, "Unknown secondary PDP context\n"); return EOF; } if (linked_pdp != secondary_pdp) { if (gsn->cb_delete_context) gsn->cb_delete_context (secondary_pdp); pdp_freepdp(secondary_pdp); } } } if (gsn->cb_delete_context) gsn->cb_delete_context(linked_pdp); pdp_freepdp(linked_pdp); } else { if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); if (pdp == linked_pdp) { linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0; linked_pdp->nodata = 1; } else pdp_freepdp(pdp); } return 0; } /* Send Delete PDP Context Response */ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len, struct pdp_t *pdp, struct pdp_t *linked_pdp, uint8_t cause, int teardown) { union gtp_packet packet; struct pdp_t *secondary_pdp; unsigned int length = get_default_gtp(version, GTP_DELETE_PDP_RSP, &packet); int n; gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause); gtp_resp(version, gsn, pdp, &packet, length, peer, fd, get_seq(pack), get_tid(pack)); if (cause == GTPCAUSE_ACC_REQ) { if ((teardown) || (version == 0)) { /* Remove all contexts */ for (n = 0; n < PDP_MAXNSAPI; n++) { if (linked_pdp->secondary_tei[n]) { if (pdp_getgtp1 (&secondary_pdp, linked_pdp->secondary_tei[n])) { LOGP(DLGTP, LOGL_ERROR, "Unknown secondary PDP context\n"); return EOF; } if (linked_pdp != secondary_pdp) { if (gsn->cb_delete_context) gsn->cb_delete_context (secondary_pdp); pdp_freepdp(secondary_pdp); } } } if (gsn->cb_delete_context) gsn->cb_delete_context(linked_pdp); pdp_freepdp(linked_pdp); } else { /* Remove only current context */ if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); if (pdp == linked_pdp) { linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0; linked_pdp->nodata = 1; } else pdp_freepdp(pdp); } } /* if (cause == GTPCAUSE_ACC_REQ) */ return 0; } /* Handle Delete PDP Context Request */ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { struct pdp_t *pdp = NULL; struct pdp_t *linked_pdp = NULL; union gtpie_member *ie[GTPIE_SIZE]; uint16_t seq = get_seq(pack); int hlen = get_hlen(pack); uint8_t nsapi; uint8_t teardown = 0; int n; int count = 0; /* Is this a dublicate ? */ if (!gtp_dublicate(gsn, version, peer, seq)) { return 0; /* We allready send off response once */ } /* Find the linked context in question */ if (pdp_getgtp1(&linked_pdp, get_tei(pack))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context: %u\n", get_tei(pack)); return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, NULL, NULL, GTPCAUSE_NON_EXIST, teardown); } /* If version 0 this is also the secondary context */ if (version == 0) pdp = linked_pdp; /* Decode information elements */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (0 == version) return EOF; else return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, NULL, NULL, GTPCAUSE_INVALID_MESSAGE, teardown); } if (version == 1) { /* NSAPI (mandatory) */ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &nsapi)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, NULL, NULL, GTPCAUSE_MAN_IE_MISSING, teardown); } /* Find the context in question */ if (pdp_getgtp1(&pdp, linked_pdp->secondary_tei[nsapi & 0x0f])) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, NULL, NULL, GTPCAUSE_NON_EXIST, teardown); } /* Teardown (conditional) */ gtpie_gettv1(ie, GTPIE_TEARDOWN, 0, &teardown); if (!teardown) { for (n = 0; n < PDP_MAXNSAPI; n++) if (linked_pdp->secondary_tei[n]) count++; if (count <= 1) { return 0; /* 29.060 7.3.5 Ignore message */ } } } return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, pdp, linked_pdp, GTPCAUSE_ACC_REQ, teardown); } /* Handle Delete PDP Context Response */ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len) { union gtpie_member *ie[GTPIE_SIZE]; uint8_t cause; void *cbp = NULL; uint8_t type = 0; int hlen = get_hlen(pack); /* Remove packet from queue */ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; /* Decode information elements */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp); return EOF; } /* Extract cause value (mandatory) */ if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) { gsn->missing++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp); return EOF; } /* Check the cause value (again) */ if ((GTPCAUSE_ACC_REQ != cause) && (GTPCAUSE_NON_EXIST != cause)) { gsn->err_cause++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unexpected cause value received: %d\n", cause); if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, cbp); return EOF; } /* Callback function to notify application */ if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, cbp); return 0; } /* Send Error Indication (response to a GPDU message */ int gtp_error_ind_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { union gtp_packet packet; unsigned int length = get_default_gtp(version, GTP_ERROR, &packet); return gtp_resp(version, gsn, NULL, &packet, length, peer, fd, get_seq(pack), get_tid(pack)); } /* Handle Error Indication */ int gtp_error_ind_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len) { struct pdp_t *pdp; /* Find the context in question */ if (pdp_tidget(&pdp, be64toh(((union gtp_packet *)pack)->gtp0.h.tid))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return EOF; } gsn->err_unknownpdp++; /* TODO: Change counter */ GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Received Error Indication\n"); if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); pdp_freepdp(pdp); return 0; } int gtp_gpdu_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { int hlen = GTP1_HEADER_SIZE_SHORT; /* Need to include code to verify packet src and dest addresses */ struct pdp_t *pdp; if (version == 0) { if (pdp_getgtp0 (&pdp, ntoh16(((union gtp_packet *)pack)->gtp0.h.flow))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return gtp_error_ind_resp(gsn, version, peer, fd, pack, len); } hlen = GTP0_HEADER_SIZE; } else if (version == 1) { if (pdp_getgtp1 (&pdp, ntoh32(((union gtp_packet *)pack)->gtp1l.h.tei))) { gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return gtp_error_ind_resp(gsn, version, peer, fd, pack, len); } /* Is this a long or a short header ? */ if (((union gtp_packet *)pack)->gtp1l.h.flags & 0x07) hlen = GTP1_HEADER_SIZE_LONG; else hlen = GTP1_HEADER_SIZE_SHORT; } else { GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown version: %d\n", version); } /* If the GPDU was not from the peer GSN tell him to delete context */ if (memcmp(&peer->sin_addr, pdp->gsnru.v, pdp->gsnru.l)) { /* TODO Range? */ gsn->err_unknownpdp++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); return gtp_error_ind_resp(gsn, version, peer, fd, pack, len); } /* Callback function */ if (gsn->cb_data_ind != 0) return gsn->cb_data_ind(pdp, pack + hlen, len - hlen); return 0; } /* Receives GTP packet and sends off for further processing * Function will check the validity of the header. If the header * is not valid the packet is either dropped or a version not * supported is returned to the peer. * TODO: Need to decide on return values! */ int gtp_decaps0(struct gsn_t *gsn) { unsigned char buffer[PACKET_MAX]; struct sockaddr_in peer; socklen_t peerlen; int status; struct gtp0_header *pheader; int version = 0; /* GTP version should be determined from header! */ int fd = gsn->fd0; /* TODO: Need strategy of userspace buffering and blocking */ /* Currently read is non-blocking and send is blocking. */ /* This means that the program have to wait for busy send calls... */ while (1) { /* Loop until no more to read */ if (fcntl(gsn->fd0, F_SETFL, O_NONBLOCK)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } peerlen = sizeof(peer); if ((status = recvfrom(gsn->fd0, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &peerlen)) < 0) { if (errno == EAGAIN) return 0; gsn->err_readfrom++; LOGP(DLGTP, LOGL_ERROR, "recvfrom(fd0=%d, buffer=%lx, len=%zu) failed: status = %d error = %s\n", gsn->fd0, (unsigned long)buffer, sizeof(buffer), status, status ? strerror(errno) : "No error"); return -1; } /* Need at least 1 byte in order to check version */ if (status < (1)) { gsn->empty++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Discarding packet - too small\n"); continue; } pheader = (struct gtp0_header *)(buffer); /* Version should be gtp0 (or earlier) */ /* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */ /* GTP 0 messages. If other version message is received we reply that we */ /* only support version 0, implying that this is the only version */ /* supported on this port */ if (((pheader->flags & 0xe0) > 0x00)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported GTP version\n"); gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */ continue; } /* Check length of gtp0 packet */ if (status < GTP0_HEADER_SIZE) { gsn->tooshort++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "GTP0 packet too short\n"); continue; /* Silently discard 29.60: 11.1.2 */ } /* Check packet length field versus length of packet */ if (status != (ntoh16(pheader->length) + GTP0_HEADER_SIZE)) { gsn->tooshort++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "GTP packet length field does not match actual length\n"); continue; /* Silently discard */ } if ((gsn->mode == GTP_MODE_GGSN) && ((pheader->type == GTP_CREATE_PDP_RSP) || (pheader->type == GTP_UPDATE_PDP_RSP) || (pheader->type == GTP_DELETE_PDP_RSP))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unexpected GTP Signalling Message\n"); continue; /* Silently discard 29.60: 11.1.4 */ } if ((gsn->mode == GTP_MODE_SGSN) && ((pheader->type == GTP_CREATE_PDP_REQ) || (pheader->type == GTP_UPDATE_PDP_REQ) || (pheader->type == GTP_DELETE_PDP_REQ))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unexpected GTP Signalling Message\n"); continue; /* Silently discard 29.60: 11.1.4 */ } switch (pheader->type) { case GTP_ECHO_REQ: gtp_echo_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_ECHO_RSP: gtp_echo_conf(gsn, version, &peer, buffer, status); break; case GTP_NOT_SUPPORTED: gtp_unsup_ind(gsn, &peer, buffer, status); break; case GTP_CREATE_PDP_REQ: gtp_create_pdp_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_CREATE_PDP_RSP: gtp_create_pdp_conf(gsn, version, &peer, buffer, status); break; case GTP_UPDATE_PDP_REQ: gtp_update_pdp_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_UPDATE_PDP_RSP: gtp_update_pdp_conf(gsn, version, &peer, buffer, status); break; case GTP_DELETE_PDP_REQ: gtp_delete_pdp_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_DELETE_PDP_RSP: gtp_delete_pdp_conf(gsn, version, &peer, buffer, status); break; case GTP_ERROR: gtp_error_ind_conf(gsn, version, &peer, buffer, status); break; case GTP_GPDU: gtp_gpdu_ind(gsn, version, &peer, fd, buffer, status); break; default: gsn->unknown++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unknown GTP message type received: %d\n", pheader->type); break; } } } int gtp_decaps1c(struct gsn_t *gsn) { unsigned char buffer[PACKET_MAX]; struct sockaddr_in peer; socklen_t peerlen; int status; struct gtp1_header_short *pheader; int version = 1; /* TODO GTP version should be determined from header! */ int fd = gsn->fd1c; /* TODO: Need strategy of userspace buffering and blocking */ /* Currently read is non-blocking and send is blocking. */ /* This means that the program have to wait for busy send calls... */ while (1) { /* Loop until no more to read */ if (fcntl(fd, F_SETFL, O_NONBLOCK)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } peerlen = sizeof(peer); if ((status = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &peerlen)) < 0) { if (errno == EAGAIN) return 0; gsn->err_readfrom++; LOGP(DLGTP, LOGL_ERROR, "recvfrom(fd=%d, buffer=%lx, len=%zu) failed: status = %d error = %s\n", fd, (unsigned long)buffer, sizeof(buffer), status, status ? strerror(errno) : "No error"); return -1; } /* Need at least 1 byte in order to check version */ if (status < (1)) { gsn->empty++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Discarding packet - too small\n"); continue; } pheader = (struct gtp1_header_short *)(buffer); /* Version must be no larger than GTP 1 */ if (((pheader->flags & 0xe0) > 0x20)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported GTP version\n"); gtp_unsup_req(gsn, version, &peer, fd, buffer, status); /*29.60: 11.1.1 */ continue; } /* Version must be at least GTP 1 */ /* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */ /* GTP 1 messages. If GTP 0 message is received we silently discard */ /* the message */ if (((pheader->flags & 0xe0) < 0x20)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported GTP version\n"); continue; } /* Check packet flag field */ if (((pheader->flags & 0xf7) != 0x32)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported packet flag\n"); continue; } /* Check length of packet */ if (status < GTP1_HEADER_SIZE_LONG) { gsn->tooshort++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "GTP packet too short\n"); continue; /* Silently discard 29.60: 11.1.2 */ } /* Check packet length field versus length of packet */ if (status != (ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT)) { gsn->tooshort++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "GTP packet length field does not match actual length\n"); continue; /* Silently discard */ } /* Check for extension headers */ /* TODO: We really should cycle through the headers and determine */ /* if any have the comprehension required flag set */ if (((pheader->flags & 0x04) != 0x00)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported extension header\n"); gtp_extheader_req(gsn, version, &peer, fd, buffer, status); continue; } if ((gsn->mode == GTP_MODE_GGSN) && ((pheader->type == GTP_CREATE_PDP_RSP) || (pheader->type == GTP_UPDATE_PDP_RSP) || (pheader->type == GTP_DELETE_PDP_RSP))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unexpected GTP Signalling Message\n"); continue; /* Silently discard 29.60: 11.1.4 */ } if ((gsn->mode == GTP_MODE_SGSN) && ((pheader->type == GTP_CREATE_PDP_REQ) || (pheader->type == GTP_UPDATE_PDP_REQ) || (pheader->type == GTP_DELETE_PDP_REQ))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unexpected GTP Signalling Message\n"); continue; /* Silently discard 29.60: 11.1.4 */ } switch (pheader->type) { case GTP_ECHO_REQ: gtp_echo_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_ECHO_RSP: gtp_echo_conf(gsn, version, &peer, buffer, status); break; case GTP_NOT_SUPPORTED: gtp_unsup_ind(gsn, &peer, buffer, status); break; case GTP_SUPP_EXT_HEADER: gtp_extheader_ind(gsn, &peer, buffer, status); break; case GTP_CREATE_PDP_REQ: gtp_create_pdp_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_CREATE_PDP_RSP: gtp_create_pdp_conf(gsn, version, &peer, buffer, status); break; case GTP_UPDATE_PDP_REQ: gtp_update_pdp_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_UPDATE_PDP_RSP: gtp_update_pdp_conf(gsn, version, &peer, buffer, status); break; case GTP_DELETE_PDP_REQ: gtp_delete_pdp_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_DELETE_PDP_RSP: gtp_delete_pdp_conf(gsn, version, &peer, buffer, status); break; case GTP_ERROR: gtp_error_ind_conf(gsn, version, &peer, buffer, status); break; default: gsn->unknown++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unknown GTP message type received: %u\n", pheader->type); break; } } } int gtp_decaps1u(struct gsn_t *gsn) { unsigned char buffer[PACKET_MAX]; struct sockaddr_in peer; socklen_t peerlen; int status; struct gtp1_header_short *pheader; int version = 1; /* GTP version should be determined from header! */ int fd = gsn->fd1u; /* TODO: Need strategy of userspace buffering and blocking */ /* Currently read is non-blocking and send is blocking. */ /* This means that the program have to wait for busy send calls... */ while (1) { /* Loop until no more to read */ if (fcntl(gsn->fd1u, F_SETFL, O_NONBLOCK)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } peerlen = sizeof(peer); if ((status = recvfrom(gsn->fd1u, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &peerlen)) < 0) { if (errno == EAGAIN) return 0; gsn->err_readfrom++; LOGP(DLGTP, LOGL_ERROR, "recvfrom(fd1u=%d, buffer=%lx, len=%zu) failed: status = %d error = %s\n", gsn->fd1u, (unsigned long)buffer, sizeof(buffer), status, status ? strerror(errno) : "No error"); return -1; } /* Need at least 1 byte in order to check version */ if (status < (1)) { gsn->empty++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Discarding packet - too small\n"); continue; } pheader = (struct gtp1_header_short *)(buffer); /* Version must be no larger than GTP 1 */ if (((pheader->flags & 0xe0) > 0x20)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported GTP version\n"); gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status); /*29.60: 11.1.1 */ continue; } /* Version must be at least GTP 1 */ /* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */ /* GTP 1 messages. If GTP 0 message is received we silently discard */ /* the message */ if (((pheader->flags & 0xe0) < 0x20)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported GTP version\n"); continue; } /* Check packet flag field (allow both with and without sequence number) */ if (((pheader->flags & 0xf5) != 0x30)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported packet flag\n"); continue; } /* Check length of packet */ if (status < GTP1_HEADER_SIZE_SHORT) { gsn->tooshort++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "GTP packet too short\n"); continue; /* Silently discard 29.60: 11.1.2 */ } /* Check packet length field versus length of packet */ if (status != (ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT)) { gsn->tooshort++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "GTP packet length field does not match actual length\n"); continue; /* Silently discard */ } /* Check for extension headers */ /* TODO: We really should cycle through the headers and determine */ /* if any have the comprehension required flag set */ if (((pheader->flags & 0x04) != 0x00)) { gsn->unsup++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unsupported extension header\n"); gtp_extheader_req(gsn, version, &peer, fd, buffer, status); continue; } switch (pheader->type) { case GTP_ECHO_REQ: gtp_echo_ind(gsn, version, &peer, fd, buffer, status); break; case GTP_ECHO_RSP: gtp_echo_conf(gsn, version, &peer, buffer, status); break; case GTP_SUPP_EXT_HEADER: gtp_extheader_ind(gsn, &peer, buffer, status); break; case GTP_ERROR: gtp_error_ind_conf(gsn, version, &peer, buffer, status); break; /* Supported header extensions */ case GTP_GPDU: gtp_gpdu_ind(gsn, version, &peer, fd, buffer, status); break; default: gsn->unknown++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, "Unknown GTP message type received: %u\n", pheader->type); break; } } } int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len) { union gtp_packet packet; struct sockaddr_in addr; int fd; int length; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; #if defined(__FreeBSD__) || defined(__APPLE__) addr.sin_len = sizeof(addr); #endif memcpy(&addr.sin_addr, pdp->gsnru.v, pdp->gsnru.l); /* TODO range check */ if (pdp->version == 0) { length = GTP0_HEADER_SIZE + len; addr.sin_port = htons(GTP0_PORT); fd = gsn->fd0; get_default_gtp(0, GTP_GPDU, &packet); packet.gtp0.h.length = hton16(len); packet.gtp0.h.seq = hton16(pdp->gtpsntx++); packet.gtp0.h.flow = hton16(pdp->flru); packet.gtp0.h.tid = htobe64(pdp_gettid(pdp->imsi, pdp->nsapi)); if (len > sizeof(union gtp_packet) - sizeof(struct gtp0_header)) { gsn->err_memcpy++; LOGP(DLGTP, LOGL_ERROR, "Memcpy failed: %u > %zu\n", len, sizeof(union gtp_packet) - sizeof(struct gtp0_header)); return EOF; } memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */ } else if (pdp->version == 1) { length = GTP1_HEADER_SIZE_LONG + len; addr.sin_port = htons(GTP1U_PORT); fd = gsn->fd1u; get_default_gtp(1, GTP_GPDU, &packet); packet.gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT + GTP1_HEADER_SIZE_LONG); packet.gtp1l.h.seq = hton16(pdp->gtpsntx++); packet.gtp1l.h.tei = hton32(pdp->teid_gn); if (len > sizeof(union gtp_packet) - sizeof(struct gtp1_header_long)) { gsn->err_memcpy++; LOGP(DLGTP, LOGL_ERROR, "Memcpy failed: %u > %zu\n", len, sizeof(union gtp_packet) - sizeof(struct gtp0_header)); return EOF; } memcpy(packet.gtp1l.p, pack, len); /* TODO Should be avoided! */ } else { LOGP(DLGTP, LOGL_ERROR, "Unknown version: %d\n", pdp->version); return EOF; } if (fcntl(fd, F_SETFL, 0)) { LOGP(DLGTP, LOGL_ERROR, "fnctl()\n"); return -1; } if (sendto(fd, &packet, length, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { gsn->err_sendto++; LOGP(DLGTP, LOGL_ERROR, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s\n", fd, (unsigned long)&packet, GTP0_HEADER_SIZE + len, strerror(errno)); return EOF; } return 0; } /* *********************************************************** * Conversion functions *************************************************************/ int char2ul_t(char *src, struct ul_t dst) { dst.l = strlen(src) + 1; dst.v = malloc(dst.l); dst.v[0] = dst.l - 1; memcpy(&dst.v[1], src, dst.v[0]); return 0; } /* *********************************************************** * IP address conversion functions * There exist several types of address representations: * - eua: End User Address. (29.060, 7.7.27, message type 128) * Used for signalling address to mobile station. Supports IPv4 * IPv6 x.25 etc. etc. * - gsna: GSN Address. (29.060, 7.7.32, message type 133): IP address * of GSN. If length is 4 it is IPv4. If length is 16 it is IPv6. * - in_addr: IPv4 address struct. * - sockaddr_in: Socket API representation of IP address and * port number. *************************************************************/ int ipv42eua(struct ul66_t *eua, struct in_addr *src) { eua->v[0] = 0xf1; /* IETF */ eua->v[1] = 0x21; /* IPv4 */ if (src) { eua->l = 6; memcpy(&eua->v[2], src, 4); } else { eua->l = 2; } return 0; } int eua2ipv4(struct in_addr *dst, struct ul66_t *eua) { if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] = 0x21)) return -1; /* Not IPv4 address */ memcpy(dst, &eua->v[2], 4); return 0; } int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna) { memset(dst, 0, sizeof(struct in_addr)); if (gsna->l != 4) return EOF; /* Return if not IPv4 */ memcpy(dst, gsna->v, gsna->l); return 0; } int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src) { memset(gsna, 0, sizeof(struct ul16_t)); gsna->l = 4; memcpy(gsna->v, src, gsna->l); return 0; } openggsn-0.92/gtp/gtp.h000066400000000000000000000427131262356443100150360ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #ifndef _GTP_H #define _GTP_H #define GTP_MODE_GGSN 1 #define GTP_MODE_SGSN 2 #define GTP0_PORT 3386 #define GTP1C_PORT 2123 #define GTP1U_PORT 2152 #define PACKET_MAX 8196 #define GTP_MAX 0xffff /* TODO: Choose right number */ #define GTP0_HEADER_SIZE 20 #define GTP1_HEADER_SIZE_SHORT 8 #define GTP1_HEADER_SIZE_LONG 12 #define SYSLOG_PRINTSIZE 255 #define ERRMSG_SIZE 255 #define RESTART_FILE "gsn_restart" #define NAMESIZE 1024 /* GTP version 1 extension header type definitions. */ #define GTP_EXT_PDCP_PDU 0xC0 /* PDCP PDU Number */ /* GTP version 1 message type definitions. Also covers version 0 except * * for anonymous PDP context which was superceded in version 1 */ /* 0 For future use. */ #define GTP_ECHO_REQ 1 /* Echo Request */ #define GTP_ECHO_RSP 2 /* Echo Response */ #define GTP_NOT_SUPPORTED 3 /* Version Not Supported */ #define GTP_ALIVE_REQ 4 /* Node Alive Request */ #define GTP_ALIVE_RSP 5 /* Node Alive Response */ #define GTP_REDIR_REQ 6 /* Redirection Request */ #define GTP_REDIR_RSP 7 /* Redirection Response */ /* 8-15 For future use. */ #define GTP_CREATE_PDP_REQ 16 /* Create PDP Context Request */ #define GTP_CREATE_PDP_RSP 17 /* Create PDP Context Response */ #define GTP_UPDATE_PDP_REQ 18 /* Update PDP Context Request */ #define GTP_UPDATE_PDP_RSP 19 /* Update PDP Context Response */ #define GTP_DELETE_PDP_REQ 20 /* Delete PDP Context Request */ #define GTP_DELETE_PDP_RSP 21 /* Delete PDP Context Response */ /* 22-25 For future use. *//* In version GTP 1 anonomous PDP context */ #define GTP_ERROR 26 /* Error Indication */ #define GTP_PDU_NOT_REQ 27 /* PDU Notification Request */ #define GTP_PDU_NOT_RSP 28 /* PDU Notification Response */ #define GTP_PDU_NOT_REJ_REQ 29 /* PDU Notification Reject Request */ #define GTP_PDU_NOT_REJ_RSP 30 /* PDU Notification Reject Response */ #define GTP_SUPP_EXT_HEADER 31 /* Supported Extension Headers Notification */ #define GTP_SND_ROUTE_REQ 32 /* Send Routeing Information for GPRS Request */ #define GTP_SND_ROUTE_RSP 33 /* Send Routeing Information for GPRS Response */ #define GTP_FAILURE_REQ 34 /* Failure Report Request */ #define GTP_FAILURE_RSP 35 /* Failure Report Response */ #define GTP_MS_PRESENT_REQ 36 /* Note MS GPRS Present Request */ #define GTP_MS_PRESENT_RSP 37 /* Note MS GPRS Present Response */ /* 38-47 For future use. */ #define GTP_IDEN_REQ 48 /* Identification Request */ #define GTP_IDEN_RSP 49 /* Identification Response */ #define GTP_SGSN_CONTEXT_REQ 50 /* SGSN Context Request */ #define GTP_SGSN_CONTEXT_RSP 51 /* SGSN Context Response */ #define GTP_SGSN_CONTEXT_ACK 52 /* SGSN Context Acknowledge */ #define GTP_FWD_RELOC_REQ 53 /* Forward Relocation Request */ #define GTP_FWD_RELOC_RSP 54 /* Forward Relocation Response */ #define GTP_FWD_RELOC_COMPL 55 /* Forward Relocation Complete */ #define GTP_RELOC_CANCEL_REQ 56 /* Relocation Cancel Request */ #define GTP_RELOC_CANCEL_RSP 57 /* Relocation Cancel Response */ #define GTP_FWD_SRNS 58 /* Forward SRNS Context */ #define GTP_FWD_RELOC_ACK 59 /* Forward Relocation Complete Acknowledge */ #define GTP_FWD_SRNS_ACK 60 /* Forward SRNS Context Acknowledge */ /* 61-239 For future use. */ #define GTP_DATA_TRAN_REQ 240 /* Data Record Transfer Request */ #define GTP_DATA_TRAN_RSP 241 /* Data Record Transfer Response */ /* 242-254 For future use. */ #define GTP_GPDU 255 /* G-PDU */ /* GTP information element cause codes from 29.060 v3.9.0 7.7 */ /* */ #define GTPCAUSE_REQ_IMSI 0 /* Request IMSI */ #define GTPCAUSE_REQ_IMEI 1 /* Request IMEI */ #define GTPCAUSE_REQ_IMSI_IMEI 2 /* Request IMSI and IMEI */ #define GTPCAUSE_NO_ID_NEEDED 3 /* No identity needed */ #define GTPCAUSE_MS_REFUSES_X 4 /* MS refuses */ #define GTPCAUSE_MS_NOT_RESP_X 5 /* MS is not GPRS responding */ #define GTPCAUSE_006 6 /* For future use 6-48 */ #define GTPCAUSE_049 49 /* Cause values reserved for GPRS charging protocol use (See GTP' in GSM 12.15) 49-63 */ #define GTPCAUSE_064 64 /* For future use 64-127 */ #define GTPCAUSE_ACC_REQ 128 /* Request accepted */ #define GTPCAUSE_129 129 /* For future use 129-176 */ #define GTPCAUSE_177 177 /* Cause values reserved for GPRS charging protocol use (See GTP' In GSM 12.15) 177-191 */ #define GTPCAUSE_NON_EXIST 192 /* Non-existent */ #define GTPCAUSE_INVALID_MESSAGE 193 /* Invalid message format */ #define GTPCAUSE_IMSI_NOT_KNOWN 194 /* IMSI not known */ #define GTPCAUSE_MS_DETACHED 195 /* MS is GPRS detached */ #define GTPCAUSE_MS_NOT_RESP 196 /* MS is not GPRS responding */ #define GTPCAUSE_MS_REFUSES 197 /* MS refuses */ #define GTPCAUSE_198 198 /* For future use */ #define GTPCAUSE_NO_RESOURCES 199 /* No resources available */ #define GTPCAUSE_NOT_SUPPORTED 200 /* Service not supported */ #define GTPCAUSE_MAN_IE_INCORRECT 201 /* Mandatory IE incorrect */ #define GTPCAUSE_MAN_IE_MISSING 202 /* Mandatory IE missing */ #define GTPCAUSE_OPT_IE_INCORRECT 203 /* Optional IE incorrect */ #define GTPCAUSE_SYS_FAIL 204 /* System failure */ #define GTPCAUSE_ROAMING_REST 205 /* Roaming Restriction */ #define GTPCAUSE_PTIMSI_MISMATCH 206 /* P-TMSI signature mismatch */ #define GTPCAUSE_CONN_SUSP 207 /* GPRS connection suspended */ #define GTPCAUSE_AUTH_FAIL 208 /* Authentication failure */ #define GTPCAUSE_USER_AUTH_FAIL 209 /* User authentication failed */ #define GTPCAUSE_CONTEXT_NOT_FOUND 210 /* Context not found */ #define GTPCAUSE_ADDR_OCCUPIED 211 /* All dynamic PDP addresses are occupied */ #define GTPCAUSE_NO_MEMORY 212 /* No memory is available */ #define GTPCAUSE_RELOC_FAIL 213 /* Relocation failure */ #define GTPCAUSE_UNKNOWN_MAN_EXTHEADER 214 /* Unknown mandatory extension header */ #define GTPCAUSE_SEM_ERR_TFT 215 /* Semantic error in the TFT operation */ #define GTPCAUSE_SYN_ERR_TFT 216 /* Syntactic error in the TFT operation */ #define GTPCAUSE_SEM_ERR_FILTER 217 /* Semantic errors in packet filter(s) */ #define GTPCAUSE_SYN_ERR_FILTER 218 /* Syntactic errors in packet filter(s) */ #define GTPCAUSE_MISSING_APN 219 /* Missing or unknown APN */ #define GTPCAUSE_UNKNOWN_PDP 220 /* Unknown PDP address or PDP type */ #define GTPCAUSE_221 221 /* For Future Use 221-240 */ #define GTPCAUSE_241 241 /* Cause Values Reserved For Gprs Charging Protocol Use (See Gtp' In Gsm 12.15) 241-255 */ struct ul66_t; struct ul16_t; /* GTP 0 header. * Explanation to some of the fields: * SNDCP NPDU Number flag = 0 except for inter SGSN handover situations * SNDCP N-PDU LCC Number 0 = 0xff except for inter SGSN handover situations * Sequence number. Used for reliable delivery of signalling messages, and * to discard "illegal" data messages. * Flow label. Is used to point a particular PDP context. Is used in data * messages as well as signalling messages related to a particular context. * Tunnel ID is IMSI+NSAPI. Unique identifier of PDP context. Is somewhat * redundant because the header also includes flow. */ struct gtp0_header { /* Descriptions from 3GPP 09.60 */ uint8_t flags; /* 01 bitfield, with typical values */ /* 000..... Version: 1 (0) */ /* ...1111. Spare (7) */ /* .......0 SNDCP N-PDU Number flag (0) */ uint8_t type; /* 02 Message type. T-PDU = 0xff */ uint16_t length; /* 03 Length (of G-PDU excluding header) */ uint16_t seq; /* 05 Sequence Number */ uint16_t flow; /* 07 Flow Label ( = 0 for signalling) */ uint8_t number; /* 09 SNDCP N-PDU LCC Number ( 0 = 0xff) */ uint8_t spare1; /* 10 Spare */ uint8_t spare2; /* 11 Spare */ uint8_t spare3; /* 12 Spare */ uint64_t tid; /* 13 Tunnel ID */ } __attribute__((packed)); /* 20 */ struct gtp1_header_short { /* Descriptions from 3GPP 29060 */ uint8_t flags; /* 01 bitfield, with typical values */ /* 001..... Version: 1 */ /* ...1.... Protocol Type: GTP=1, GTP'=0 */ /* ....0... Spare = 0 */ /* .....0.. Extension header flag: 0 */ /* ......0. Sequence number flag: 0 */ /* .......0 PN: N-PDU Number flag */ uint8_t type; /* 02 Message type. T-PDU = 0xff */ uint16_t length; /* 03 Length (of IP packet or signalling) */ uint32_t tei; /* 05 - 08 Tunnel Endpoint ID */ } __attribute__((packed)); struct gtp1_header_long { /* Descriptions from 3GPP 29060 */ uint8_t flags; /* 01 bitfield, with typical values */ /* 001..... Version: 1 */ /* ...1.... Protocol Type: GTP=1, GTP'=0 */ /* ....0... Spare = 0 */ /* .....0.. Extension header flag: 0 */ /* ......1. Sequence number flag: 1 */ /* .......0 PN: N-PDU Number flag */ uint8_t type; /* 02 Message type. T-PDU = 0xff */ uint16_t length; /* 03 Length (of IP packet or signalling) */ uint32_t tei; /* 05 Tunnel Endpoint ID */ uint16_t seq; /* 10 Sequence Number */ uint8_t npdu; /* 11 N-PDU Number */ uint8_t next; /* 12 Next extension header type. Empty = 0 */ } __attribute__((packed)); struct gtp0_packet { struct gtp0_header h; uint8_t p[GTP_MAX]; } __attribute__ ((packed)); struct gtp1_packet_short { struct gtp1_header_short h; uint8_t p[GTP_MAX]; } __attribute__ ((packed)); struct gtp1_packet_long { struct gtp1_header_long h; uint8_t p[GTP_MAX]; } __attribute__ ((packed)); union gtp_packet { uint8_t flags; struct gtp0_packet gtp0; struct gtp1_packet_short gtp1s; struct gtp1_packet_long gtp1l; } __attribute__ ((packed)); /* *********************************************************** * Information storage for each gsn instance * * Normally each instance of the application corresponds to * one instance of a gsn. * * In order to avoid global variables in the application, and * also in order to allow several instances of a gsn in the same * application this struct is provided in order to store all * relevant information related to the gsn. * * Note that this does not include information storage for ' * each pdp context. This is stored in another struct. *************************************************************/ struct gsn_t { /* Parameters related to the network interface */ int fd0; /* GTP0 file descriptor */ int fd1c; /* GTP1 control plane file descriptor */ int fd1u; /* GTP0 user plane file descriptor */ int mode; /* Mode of operation: GGSN or SGSN */ struct in_addr gsnc; /* IP address of this gsn for signalling */ struct in_addr gsnu; /* IP address of this gsn for user traffic */ /* Parameters related to signalling messages */ uint16_t seq_next; /* Next sequence number to use */ int seq_first; /* First packet in queue (oldest timeout) */ int seq_last; /* Last packet in queue (youngest timeout) */ unsigned char restart_counter; /* Increment on restart. Stored on disk */ char *statedir; /* Disk location for permanent storage */ struct queue_t *queue_req; /* Request queue */ struct queue_t *queue_resp; /* Response queue */ /* Call back functions */ int (*cb_delete_context) (struct pdp_t *); int (*cb_create_context_ind) (struct pdp_t *); int (*cb_unsup_ind) (struct sockaddr_in * peer); int (*cb_extheader_ind) (struct sockaddr_in * peer); int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp); int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len); int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery); /* Counters */ uint64_t err_socket; /* Number of socket errors */ uint64_t err_readfrom; /* Number of readfrom errors */ uint64_t err_sendto; /* Number of sendto errors */ uint64_t err_memcpy; /* Number of memcpy */ uint64_t err_queuefull; /* Number of times queue was full */ uint64_t err_seq; /* Number of seq out of range */ uint64_t err_address; /* GSN address conversion failed */ uint64_t err_unknownpdp; /* GSN address conversion failed */ uint64_t err_unknowntid; /* Application supplied unknown imsi+nsapi */ uint64_t err_cause; /* Unexpected cause value received */ uint64_t err_outofpdp; /* Out of storage for PDP contexts */ uint64_t empty; /* Number of empty packets */ uint64_t unsup; /* Number of unsupported version 29.60 11.1.1 */ uint64_t tooshort; /* Number of too short headers 29.60 11.1.2 */ uint64_t unknown; /* Number of unknown messages 29.60 11.1.3 */ uint64_t unexpect; /* Number of unexpected messages 29.60 11.1.4 */ uint64_t duplicate; /* Number of duplicate or unsolicited replies */ uint64_t missing; /* Number of missing information field messages */ uint64_t incorrect; /* Number of incorrect information field messages */ uint64_t invalid; /* Number of invalid message format messages */ }; /* External API functions */ extern const char *gtp_version(); extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, int mode); extern int gtp_free(struct gsn_t *gsn); extern int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi); extern int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp); extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp); extern int gtp_set_cb_create_context_ind(struct gsn_t *gsn, int (*cb_create_context_ind) (struct pdp_t * pdp)); extern int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause); extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, struct in_addr *inetaddr); extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, int teardown); extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len); extern int gtp_set_cb_data_ind(struct gsn_t *gsn, int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len)); extern int gtp_fd(struct gsn_t *gsn); extern int gtp_decaps0(struct gsn_t *gsn); extern int gtp_decaps1c(struct gsn_t *gsn); extern int gtp_decaps1u(struct gsn_t *gsn); extern int gtp_retrans(struct gsn_t *gsn); extern int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout); extern int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb_delete_context) (struct pdp_t * pdp)); /*extern int gtp_set_cb_create_context(struct gsn_t *gsn, int (*cb_create_context) (struct pdp_t* pdp)); */ extern int gtp_set_cb_unsup_ind(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer)); extern int gtp_set_cb_extheader_ind(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer)); extern int gtp_set_cb_conf(struct gsn_t *gsn, int (*cb) (int type, int cause, struct pdp_t * pdp, void *cbp)); int gtp_set_cb_recovery(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer, uint8_t recovery)); /* Internal functions (not part of the API */ extern int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp, struct in_addr *inetaddrs); extern int gtp_echo_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len); extern int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len); extern int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len); extern int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len); extern int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer, void *pack, unsigned len); extern int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp, uint8_t cause); extern int gtp_create_pdp_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len); extern int gtp_create_pdp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len); extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *cbp, struct in_addr *inetaddr, struct pdp_t *pdp); extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *cbp, struct pdp_t *pdp); extern int gtp_delete_pdp_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len, struct pdp_t *pdp, struct pdp_t *linked_pdp, uint8_t cause, int teardown); extern int gtp_delete_pdp_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len); extern int gtp_delete_pdp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len); extern int ipv42eua(struct ul66_t *eua, struct in_addr *src); extern int eua2ipv4(struct in_addr *dst, struct ul66_t *eua); extern int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna); extern int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src); #endif /* !_GTP_H */ openggsn-0.92/gtp/gtpie.c000066400000000000000000000364021262356443100153450ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * gtpie.c: Contains functions to encapsulate and decapsulate GTP * information elements * * * Encapsulation * - gtpie_tlv, gtpie_tv0, gtpie_tv1, gtpie_tv2 ... Adds information * elements to a buffer. * * Decapsulation * - gtpie_decaps: Returns array with pointers to information elements. * - getie_getie: Returns the pointer of a particular element. * - gtpie_gettlv: Copies tlv information element. Return 0 on success. * - gtpie_gettv: Copies tv information element. Return 0 on success. * */ #include <../config.h> #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include "gtpie.h" int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t, int l, void *v) { if ((*length + 3 + l) >= size) return 1; ((union gtpie_member *)(p + *length))->tlv.t = hton8(t); ((union gtpie_member *)(p + *length))->tlv.l = hton16(l); memcpy((void *)(p + *length + 3), v, l); *length += 3 + l; return 0; } int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t, int l, uint8_t * v) { if ((*length + 1 + l) >= size) return 1; ((union gtpie_member *)(p + *length))->tv0.t = hton8(t); memcpy((void *)(p + *length + 1), v, l); *length += 1 + l; return 0; } int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t, uint8_t v) { if ((*length + 2) >= size) return 1; ((union gtpie_member *)(p + *length))->tv1.t = hton8(t); ((union gtpie_member *)(p + *length))->tv1.v = hton8(v); *length += 2; return 0; } int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t, uint16_t v) { if ((*length + 3) >= size) return 1; ((union gtpie_member *)(p + *length))->tv2.t = hton8(t); ((union gtpie_member *)(p + *length))->tv2.v = hton16(v); *length += 3; return 0; } int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t, uint32_t v) { if ((*length + 5) >= size) return 1; ((union gtpie_member *)(p + *length))->tv4.t = hton8(t); ((union gtpie_member *)(p + *length))->tv4.v = hton32(v); *length += 5; return 0; } int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t, uint64_t v) { if ((*length + 9) >= size) return 1; ((union gtpie_member *)(p + *length))->tv8.t = hton8(t); ((union gtpie_member *)(p + *length))->tv8.v = hton64(v); *length += 9; return 0; } int gtpie_getie(union gtpie_member *ie[], int type, int instance) { int j; for (j = 0; j < GTPIE_SIZE; j++) { if ((ie[j] != 0) && (ie[j]->t == type)) { if (instance-- == 0) return j; } } return -1; } int gtpie_exist(union gtpie_member *ie[], int type, int instance) { int j; for (j = 0; j < GTPIE_SIZE; j++) { if ((ie[j] != 0) && (ie[j]->t == type)) { if (instance-- == 0) return 1; } } return 0; } int gtpie_gettlv(union gtpie_member *ie[], int type, int instance, unsigned int *length, void *dst, unsigned int size) { int ien; ien = gtpie_getie(ie, type, instance); if (ien >= 0) { *length = ntoh16(ie[ien]->tlv.l); if (*length <= size) memcpy(dst, ie[ien]->tlv.v, *length); else return EOF; } return 0; } int gtpie_gettv0(union gtpie_member *ie[], int type, int instance, void *dst, unsigned int size) { int ien; ien = gtpie_getie(ie, type, instance); if (ien >= 0) memcpy(dst, ie[ien]->tv0.v, size); else return EOF; return 0; } int gtpie_gettv1(union gtpie_member *ie[], int type, int instance, uint8_t * dst) { int ien; ien = gtpie_getie(ie, type, instance); if (ien >= 0) *dst = ntoh8(ie[ien]->tv1.v); else return EOF; return 0; } int gtpie_gettv2(union gtpie_member *ie[], int type, int instance, uint16_t * dst) { int ien; ien = gtpie_getie(ie, type, instance); if (ien >= 0) *dst = ntoh16(ie[ien]->tv2.v); else return EOF; return 0; } int gtpie_gettv4(union gtpie_member *ie[], int type, int instance, uint32_t * dst) { int ien; ien = gtpie_getie(ie, type, instance); if (ien >= 0) *dst = ntoh32(ie[ien]->tv4.v); else return EOF; return 0; } int gtpie_gettv8(union gtpie_member *ie[], int type, int instance, uint64_t * dst) { int ien; ien = gtpie_getie(ie, type, instance); if (ien >= 0) *dst = ntoh64(ie[ien]->tv8.v); else return EOF; return 0; } int gtpie_decaps(union gtpie_member *ie[], int version, void *pack, unsigned len) { int i; int j = 0; unsigned char *p; unsigned char *end; end = (unsigned char *)pack + len; p = pack; memset(ie, 0, sizeof(union gtpie_member *) * GTPIE_SIZE); while ((p < end) && (j < GTPIE_SIZE)) { if (GTPIE_DEBUG) { printf("The packet looks like this:\n"); for (i = 0; i < (end - p); i++) { printf("%02x ", (unsigned char)*(char *)(p + i)); if (!((i + 1) % 16)) printf("\n"); }; printf("\n"); } switch (*p) { case GTPIE_CAUSE: /* TV GTPIE types with value length 1 */ case GTPIE_REORDER: case GTPIE_MAP_CAUSE: case GTPIE_MS_VALIDATED: case GTPIE_RECOVERY: case GTPIE_SELECTION_MODE: case GTPIE_TEARDOWN: case GTPIE_NSAPI: case GTPIE_RANAP_CAUSE: case GTPIE_RP_SMS: case GTPIE_RP: case GTPIE_MS_NOT_REACH: if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE TV1 found. Type %d, value %d\n", ie[j]->tv1.t, ie[j]->tv1.v); p += 1 + 1; j++; } break; case GTPIE_FL_DI: /* TV GTPIE types with value length 2 or 4 */ case GTPIE_FL_C: if (version != 0) { if (j < GTPIE_SIZE) { /* GTPIE_TEI_DI & GTPIE_TEI_C with length 4 */ /* case GTPIE_TEI_DI: gtp1 */ /* case GTPIE_TEI_C: gtp1 */ ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE TV 4 found. Type %d, value %d\n", ie[j]->tv4.t, ie[j]->tv4.v); p += 1 + 4; j++; } break; } case GTPIE_PFI: /* TV GTPIE types with value length 2 */ case GTPIE_CHARGING_C: case GTPIE_TRACE_REF: case GTPIE_TRACE_TYPE: if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE TV2 found. Type %d, value %d\n", ie[j]->tv2.t, ie[j]->tv2.v); p += 1 + 2; j++; } break; case GTPIE_QOS_PROFILE0: /* TV GTPIE types with value length 3 */ case GTPIE_P_TMSI_S: if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE TV 3 found. Type %d, value %d, %d, %d\n", ie[j]->tv0.t, ie[j]->tv0.v[0], ie[j]->tv0.v[1], ie[j]->tv0.v[2]); p += 1 + 3; j++; } break; case GTPIE_TLLI: /* TV GTPIE types with value length 4 */ case GTPIE_P_TMSI: case GTPIE_CHARGING_ID: /* case GTPIE_TEI_DI: Handled by GTPIE_FL_DI */ /* case GTPIE_TEI_C: Handled by GTPIE_FL_DI */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE TV 4 found. Type %d, value %d\n", ie[j]->tv4.t, ie[j]->tv4.v); p += 1 + 4; j++; } break; case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf("GTPIE TV 5 found. Type %d\n", ie[j]->tv0.t); p += 1 + 5; j++; } break; case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf("GTPIE TV 7 found. Type %d\n", ie[j]->tv0.t); p += 1 + 7; j++; } break; case GTPIE_IMSI: /* TV GTPIE types with value length 8 */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE_IMSI - GTPIE TV 8 found. Type %d, value 0x%llx\n", ie[j]->tv0.t, ie[j]->tv8.v); p += 1 + 8; j++; } break; case GTPIE_RAI: /* TV GTPIE types with value length 6 */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE_RAI - GTPIE TV 6 found. Type %d, value 0x%llx\n", ie[j]->tv0.t, ie[j]->tv8.v); p += 1 + 6; j++; } break; case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf("GTPIE TV 28 found. Type %d\n", ie[j]->tv0.t); p += 1 + 28; j++; } break; case GTPIE_EXT_HEADER_T: /* GTP extension header */ if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf ("GTPIE GTP extension header found. Type %d\n", ie[j]->ext.t); p += 2 + ntoh8(ie[j]->ext.l); j++; } break; case GTPIE_EUA: /* TLV GTPIE types with variable length */ case GTPIE_MM_CONTEXT: case GTPIE_PDP_CONTEXT: case GTPIE_APN: case GTPIE_PCO: case GTPIE_GSN_ADDR: case GTPIE_MSISDN: case GTPIE_QOS_PROFILE: case GTPIE_AUTH_QUINTUP: case GTPIE_TFT: case GTPIE_TARGET_INF: case GTPIE_UTRAN_TRANS: case GTPIE_RAB_SETUP: case GTPIE_TRIGGER_ID: case GTPIE_OMC_ID: case GTPIE_CHARGING_ADDR: case GTPIE_RAT_TYPE: case GTPIE_USER_LOC: case GTPIE_MS_TZ: case GTPIE_IMEI_SV: case GTPIE_PRIVATE: if (j < GTPIE_SIZE) { ie[j] = (union gtpie_member *)p; if (GTPIE_DEBUG) printf("GTPIE TLV found. Type %d\n", ie[j]->tlv.t); p += 3 + ntoh16(ie[j]->tlv.l); j++; } break; default: if (GTPIE_DEBUG) printf("GTPIE something unknown. Type %d\n", *p); return EOF; /* We received something unknown */ } } if (p == end) { if (GTPIE_DEBUG) printf("GTPIE normal return. %lx %lx\n", (unsigned long)p, (unsigned long)end); return 0; /* We landed at the end of the packet: OK */ } else if (!(j < GTPIE_SIZE)) { if (GTPIE_DEBUG) printf("GTPIE too many elements.\n"); return EOF; /* We received too many information elements */ } else { if (GTPIE_DEBUG) printf("GTPIE exceeded end of packet. %lx %lx\n", (unsigned long)p, (unsigned long)end); return EOF; /* We exceeded the end of the packet: Error */ } } int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len) { int i; unsigned char *p; unsigned char *end; union gtpie_member *m; int iesize; p = pack; memset(pack, 0, GTPIE_MAX); end = p + GTPIE_MAX; for (i = 1; i < GTPIE_SIZE; i++) if (ie[i] != 0) { if (GTPIE_DEBUG) printf("gtpie_encaps. Type %d\n", i); m = (union gtpie_member *)p; switch (i) { case GTPIE_CAUSE: /* TV GTPIE types with value length 1 */ case GTPIE_REORDER: case GTPIE_MAP_CAUSE: case GTPIE_MS_VALIDATED: case GTPIE_RECOVERY: case GTPIE_SELECTION_MODE: case GTPIE_TEARDOWN: case GTPIE_NSAPI: case GTPIE_RANAP_CAUSE: case GTPIE_RP_SMS: case GTPIE_RP: case GTPIE_MS_NOT_REACH: iesize = 2; break; case GTPIE_FL_DI: /* TV GTPIE types with value length 2 */ case GTPIE_FL_C: case GTPIE_PFI: case GTPIE_CHARGING_C: case GTPIE_TRACE_REF: case GTPIE_TRACE_TYPE: iesize = 3; break; case GTPIE_QOS_PROFILE0: /* TV GTPIE types with value length 3 */ case GTPIE_P_TMSI_S: iesize = 4; break; case GTPIE_TLLI: /* TV GTPIE types with value length 4 */ case GTPIE_P_TMSI: /* case GTPIE_TEI_DI: only in gtp1 */ /* case GTPIE_TEI_C: only in gtp1 */ case GTPIE_CHARGING_ID: iesize = 5; break; case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */ iesize = 6; break; case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */ iesize = 8; break; case GTPIE_IMSI: /* TV GTPIE types with value length 8 */ case GTPIE_RAI: iesize = 9; break; case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */ iesize = 29; break; case GTPIE_EXT_HEADER_T: /* GTP extension header */ iesize = 2 + hton8(ie[i]->ext.l); break; case GTPIE_EUA: /* TLV GTPIE types with length length 2 */ case GTPIE_MM_CONTEXT: case GTPIE_PDP_CONTEXT: case GTPIE_APN: case GTPIE_PCO: case GTPIE_GSN_ADDR: case GTPIE_MSISDN: case GTPIE_QOS_PROFILE: case GTPIE_AUTH_QUINTUP: case GTPIE_TFT: case GTPIE_TARGET_INF: case GTPIE_UTRAN_TRANS: case GTPIE_RAB_SETUP: case GTPIE_TRIGGER_ID: case GTPIE_OMC_ID: case GTPIE_CHARGING_ADDR: case GTPIE_PRIVATE: iesize = 3 + hton16(ie[i]->tlv.l); break; default: return 2; /* We received something unknown */ } if (p + iesize < end) { memcpy(p, ie[i], iesize); p += iesize; *len += iesize; } else return 2; /* Out of space */ } return 0; } int gtpie_encaps2(union gtpie_member ie[], unsigned int size, void *pack, unsigned *len) { unsigned int i, j; unsigned char *p; unsigned char *end; union gtpie_member *m; int iesize; p = pack; memset(pack, 0, GTPIE_MAX); end = p + GTPIE_MAX; for (j = 0; j < GTPIE_SIZE; j++) for (i = 0; i < size; i++) if (ie[i].t == j) { if (GTPIE_DEBUG) printf ("gtpie_encaps. Number %d, Type %d\n", i, ie[i].t); m = (union gtpie_member *)p; switch (ie[i].t) { case GTPIE_CAUSE: /* TV GTPIE types with value length 1 */ case GTPIE_REORDER: case GTPIE_MAP_CAUSE: case GTPIE_MS_VALIDATED: case GTPIE_RECOVERY: case GTPIE_SELECTION_MODE: case GTPIE_TEARDOWN: case GTPIE_NSAPI: case GTPIE_RANAP_CAUSE: case GTPIE_RP_SMS: case GTPIE_RP: case GTPIE_MS_NOT_REACH: iesize = 2; break; case GTPIE_PFI: /* TV GTPIE types with value length 2 */ case GTPIE_CHARGING_C: case GTPIE_TRACE_REF: case GTPIE_TRACE_TYPE: iesize = 3; break; case GTPIE_QOS_PROFILE0: /* TV GTPIE types with value length 3 */ case GTPIE_P_TMSI_S: iesize = 4; break; case GTPIE_TLLI: /* TV GTPIE types with value length 4 */ case GTPIE_P_TMSI: case GTPIE_TEI_DI: case GTPIE_TEI_C: iesize = 5; break; case GTPIE_TEI_DII: /* TV GTPIE types with value length 5 */ iesize = 6; break; case GTPIE_RAB_CONTEXT: /* TV GTPIE types with value length 7 */ iesize = 8; break; case GTPIE_IMSI: /* TV GTPIE types with value length 8 */ case GTPIE_RAI: iesize = 9; break; case GTPIE_AUTH_TRIPLET: /* TV GTPIE types with value length 28 */ iesize = 29; break; case GTPIE_EXT_HEADER_T: /* GTP extension header */ iesize = 2 + hton8(ie[i].ext.l); break; case GTPIE_CHARGING_ID: /* TLV GTPIE types with length length 2 */ case GTPIE_EUA: case GTPIE_MM_CONTEXT: case GTPIE_PDP_CONTEXT: case GTPIE_APN: case GTPIE_PCO: case GTPIE_GSN_ADDR: case GTPIE_MSISDN: case GTPIE_QOS_PROFILE: case GTPIE_AUTH_QUINTUP: case GTPIE_TFT: case GTPIE_TARGET_INF: case GTPIE_UTRAN_TRANS: case GTPIE_RAB_SETUP: case GTPIE_TRIGGER_ID: case GTPIE_OMC_ID: case GTPIE_CHARGING_ADDR: case GTPIE_PRIVATE: iesize = 3 + hton16(ie[i].tlv.l); break; default: return 2; /* We received something unknown */ } if (p + iesize < end) { memcpy(p, &ie[i], iesize); p += iesize; *len += iesize; } else return 2; /* Out of space */ } return 0; } openggsn-0.92/gtp/gtpie.h000066400000000000000000000203761262356443100153550ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #ifndef _GTPIE_H #define _GTPIE_H /* Macroes for conversion between host and network byte order */ #define hton8(x) (x) #define ntoh8(x) (x) #define hton16(x) htons(x) #define ntoh16(x) ntohs(x) #define hton32(x) htonl(x) #define ntoh32(x) ntohl(x) #if BYTE_ORDER == LITTLE_ENDIAN static __inline uint64_t hton64(uint64_t q) { register uint32_t u, l; u = q >> 32; l = (uint32_t) q; return htonl(u) | ((uint64_t) htonl(l) << 32); } #define ntoh64(_x) hton64(_x) #elif BYTE_ORDER == BIG_ENDIAN #define hton64(_x) (_x) #define ntoh64(_x) hton64(_x) #else #error "Please fix " #endif #define GTPIE_SIZE 256 /* Max number of information elements */ #define GTPIE_MAX 0xffff /* Max length of information elements */ #define GTPIE_MAX_TV 28 /* Max length of type value pair */ #define GTPIE_MAX_TLV 0xffff-3 /* Max length of TLV (GTP length is 16 bit) */ #define GTPIE_DEBUG 0 /* Print debug information */ /* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */ /* Also covers version 0. Note that version 0 6: QOS Profile was superceded * * by 135: QOS Profile in version 1 */ #define GTPIE_CAUSE 1 /* Cause 1 */ #define GTPIE_IMSI 2 /* International Mobile Subscriber Identity 8 */ #define GTPIE_RAI 3 /* Routing Area Identity (RAI) 8 */ #define GTPIE_TLLI 4 /* Temporary Logical Link Identity (TLLI) 4 */ #define GTPIE_P_TMSI 5 /* Packet TMSI (P-TMSI) 4 */ #define GTPIE_QOS_PROFILE0 6 /* Quality of Service Profile GTP version 0 3 */ /* 6-7 SPARE *//* 6 is QoS Profile vers 0 */ #define GTPIE_REORDER 8 /* Reordering Required 1 */ #define GTPIE_AUTH_TRIPLET 9 /* Authentication Triplet 28 */ /* 10 SPARE */ #define GTPIE_MAP_CAUSE 11 /* MAP Cause 1 */ #define GTPIE_P_TMSI_S 12 /* P-TMSI Signature 3 */ #define GTPIE_MS_VALIDATED 13 /* MS Validated 1 */ #define GTPIE_RECOVERY 14 /* Recovery 1 */ #define GTPIE_SELECTION_MODE 15 /* Selection Mode 1 */ #define GTPIE_FL_DI 16 /* Flow Label Data I 2 */ #define GTPIE_TEI_DI 16 /* Tunnel Endpoint Identifier Data I 4 */ #define GTPIE_TEI_C 17 /* Tunnel Endpoint Identifier Control Plane 4 */ #define GTPIE_FL_C 17 /* Flow Label Signalling 2 */ #define GTPIE_TEI_DII 18 /* Tunnel Endpoint Identifier Data II 5 */ #define GTPIE_TEARDOWN 19 /* Teardown Ind 1 */ #define GTPIE_NSAPI 20 /* NSAPI 1 */ #define GTPIE_RANAP_CAUSE 21 /* RANAP Cause 1 */ #define GTPIE_RAB_CONTEXT 22 /* RAB Context 7 */ #define GTPIE_RP_SMS 23 /* Radio Priority SMS 1 */ #define GTPIE_RP 24 /* Radio Priority 1 */ #define GTPIE_PFI 25 /* Packet Flow Id 2 */ #define GTPIE_CHARGING_C 26 /* Charging Characteristics 2 */ #define GTPIE_TRACE_REF 27 /* Trace Reference 2 */ #define GTPIE_TRACE_TYPE 28 /* Trace Type 2 */ #define GTPIE_MS_NOT_REACH 29 /* MS Not Reachable Reason 1 */ /* 30-116 UNUSED */ /* 117-126 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */ #define GTPIE_CHARGING_ID 127 /* Charging ID 4 */ #define GTPIE_EUA 128 /* End User Address */ #define GTPIE_MM_CONTEXT 129 /* MM Context */ #define GTPIE_PDP_CONTEXT 130 /* PDP Context */ #define GTPIE_APN 131 /* Access Point Name */ #define GTPIE_PCO 132 /* Protocol Configuration Options */ #define GTPIE_GSN_ADDR 133 /* GSN Address */ #define GTPIE_MSISDN 134 /* MS International PSTN/ISDN Number */ #define GTPIE_QOS_PROFILE 135 /* Quality of Service Profile */ #define GTPIE_AUTH_QUINTUP 136 /* Authentication Quintuplet */ #define GTPIE_TFT 137 /* Traffic Flow Template */ #define GTPIE_TARGET_INF 138 /* Target Identification */ #define GTPIE_UTRAN_TRANS 139 /* UTRAN Transparent Container */ #define GTPIE_RAB_SETUP 140 /* RAB Setup Information */ #define GTPIE_EXT_HEADER_T 141 /* Extension Header Type List */ #define GTPIE_TRIGGER_ID 142 /* Trigger Id */ #define GTPIE_OMC_ID 143 /* OMC Identity */ #define GTPIE_RAT_TYPE 151 /* Radio Access Technology Type */ #define GTPIE_USER_LOC 152 /* User Location Information */ #define GTPIE_MS_TZ 153 /* MS Time Zone */ #define GTPIE_IMEI_SV 154 /* IMEI Software Version */ /* 239-250 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */ #define GTPIE_CHARGING_ADDR 251 /* Charging Gateway Address */ /* 252-254 Reserved for the GPRS charging protocol (see GTP' in GSM 12.15) */ #define GTPIE_PRIVATE 255 /* Private Extension */ /* GTP information element structs in network order */ struct gtpie_ext { /* Extension header */ uint8_t t; /* Type */ uint8_t l; /* Length */ uint8_t *p; /* Value */ } __attribute__ ((packed)); struct gtpie_tlv { /* Type length value pair */ uint8_t t; /* Type */ uint16_t l; /* Length */ uint8_t v[GTPIE_MAX_TLV]; /* Value */ } __attribute__ ((packed)); struct gtpie_tv0 { /* 1 byte type value pair */ uint8_t t; /* Type */ uint8_t v[GTPIE_MAX_TV]; /* Pointer to value */ } __attribute__ ((packed)); struct gtpie_tv1 { /* 1 byte type value pair */ uint8_t t; /* Type */ uint8_t v; /* Value */ } __attribute__ ((packed)); struct gtpie_tv2 { /* 2 byte type value pair */ uint8_t t; /* Type */ uint16_t v; /* Value */ } __attribute__ ((packed)); struct gtpie_tv4 { /* 4 byte type value pair */ uint8_t t; /* Type */ uint32_t v; /* Value */ } __attribute__ ((packed)); struct gtpie_tv8 { /* 8 byte type value pair */ uint8_t t; /* Type */ uint64_t v; /* Value */ } __attribute__ ((packed)); union gtpie_member { uint8_t t; struct gtpie_ext ext; struct gtpie_tlv tlv; struct gtpie_tv0 tv0; struct gtpie_tv1 tv1; struct gtpie_tv2 tv2; struct gtpie_tv4 tv4; struct gtpie_tv8 tv8; } __attribute__ ((packed)); /* cause imsi rai tlli p_tmsi qos_profile0 reorder auth map_cause p_tmsi_s ms_validated recovery selection_mode tei_di tei_c tei_dii teardown nsapi ranap_cause rab_context rp_sms rp pfi charging_c trace_ref trace_type ms_not_reach charging_id eua mm_context pdp_context apn pco gsn_addr msisdn qos_profile auth tft target_inf utran_trans rab_setup ext_header_t trigger_id omc_id charging_addr private */ struct tlv1 { uint8_t type; uint8_t length; } __attribute__ ((packed)); struct tlv2 { uint8_t type; uint16_t length; } __attribute__ ((packed)); extern int gtpie_tlv(void *p, unsigned int *length, unsigned int size, uint8_t t, int l, void *v); extern int gtpie_tv0(void *p, unsigned int *length, unsigned int size, uint8_t t, int l, uint8_t * v); extern int gtpie_tv1(void *p, unsigned int *length, unsigned int size, uint8_t t, uint8_t v); extern int gtpie_tv2(void *p, unsigned int *length, unsigned int size, uint8_t t, uint16_t v); extern int gtpie_tv4(void *p, unsigned int *length, unsigned int size, uint8_t t, uint32_t v); extern int gtpie_tv8(void *p, unsigned int *length, unsigned int size, uint8_t t, uint64_t v); extern int gtpie_getie(union gtpie_member *ie[], int type, int instance); extern int gtpie_exist(union gtpie_member *ie[], int type, int instance); extern int gtpie_gettlv(union gtpie_member *ie[], int type, int instance, unsigned int *length, void *dst, unsigned int size); extern int gtpie_gettv0(union gtpie_member *ie[], int type, int instance, void *dst, unsigned int size); extern int gtpie_gettv1(union gtpie_member *ie[], int type, int instance, uint8_t * dst); extern int gtpie_gettv2(union gtpie_member *ie[], int type, int instance, uint16_t * dst); extern int gtpie_gettv4(union gtpie_member *ie[], int type, int instance, uint32_t * dst); extern int gtpie_gettv8(union gtpie_member *ie[], int type, int instance, uint64_t * dst); extern int gtpie_decaps(union gtpie_member *ie[], int version, void *pack, unsigned len); extern int gtpie_encaps(union gtpie_member *ie[], void *pack, unsigned *len); extern int gtpie_encaps2(union gtpie_member ie[], unsigned int size, void *pack, unsigned *len); #endif /* !_GTPIE_H */ openggsn-0.92/gtp/lookupa.c000066400000000000000000000210161262356443100157020ustar00rootroot00000000000000/* -------------------------------------------------------------------- lookupa.c, by Bob Jenkins, December 1996. Same as lookup2.c Use this code however you wish. Public Domain. No warranty. Source is http://burtleburtle.net/bob/c/lookupa.c -------------------------------------------------------------------- */ #ifndef STANDARD /* #include "standard.h" */ #endif #ifndef LOOKUPA #include "lookupa.h" #endif /* -------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. For every delta with one or two bit set, and the deltas of all three high bits or all three low bits, whether the original value of a,b,c is almost all zero or is uniformly distributed, * If mix() is run forward or backward, at least 32 bits in a,b,c have at least 1/4 probability of changing. * If mix() is run forward, every bit of c will change between 1/3 and 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) mix() was built out of 36 single-cycle latency instructions in a structure that could supported 2x parallelism, like so: a -= b; a -= c; x = (c>>13); b -= c; a ^= x; b -= a; x = (a<<8); c -= a; b ^= x; c -= b; x = (b>>13); ... Unfortunately, superscalar Pentiums and Sparcs can't take advantage of that parallelism. They've also turned some of those single-cycle latency instructions into multi-cycle latency instructions. Still, this is the fastest good hash I could find. There were about 2^^68 to choose from. I only looked at a billion or so. -------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } /* -------------------------------------------------------------------- lookup() -- hash a variable-length key into a 32-bit value k : the key (the unaligned variable-length array of bytes) len : the length of the key, counting by bytes level : can be any 4-byte value Returns a 32-bit value. Every bit of the key affects every bit of the return value. Every 1-bit and 2-bit delta achieves avalanche. About 6len+35 instructions. The best hash table sizes are powers of 2. There is no need to do mod a prime (mod is sooo slow!). If you need less than 32 bits, use a bitmask. For example, if you need only 10 bits, do h = (h & hashmask(10)); In which case, the hash table should have hashsize(10) elements. If you are hashing n strings (ub1 **)k, do it like this: for (i=0, h=0; i= 12) { a += (k[0] + ((ub4) k[1] << 8) + ((ub4) k[2] << 16) + ((ub4) k[3] << 24)); b += (k[4] + ((ub4) k[5] << 8) + ((ub4) k[6] << 16) + ((ub4) k[7] << 24)); c += (k[8] + ((ub4) k[9] << 8) + ((ub4) k[10] << 16) + ((ub4) k[11] << 24)); mix(a, b, c); k += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch (len) { /* all the case statements fall through */ case 11: c += ((ub4) k[10] << 24); case 10: c += ((ub4) k[9] << 16); case 9: c += ((ub4) k[8] << 8); /* the first byte of c is reserved for the length */ case 8: b += ((ub4) k[7] << 24); case 7: b += ((ub4) k[6] << 16); case 6: b += ((ub4) k[5] << 8); case 5: b += k[4]; case 4: a += ((ub4) k[3] << 24); case 3: a += ((ub4) k[2] << 16); case 2: a += ((ub4) k[1] << 8); case 1: a += k[0]; /* case 0: nothing left to add */ } mix(a, b, c); /*-------------------------------------------- report the result */ return c; } /* -------------------------------------------------------------------- mixc -- mixc 8 4-bit values as quickly and thoroughly as possible. Repeating mix() three times achieves avalanche. Repeating mix() four times eliminates all funnels and all characteristics stronger than 2^{-11}. -------------------------------------------------------------------- */ #define mixc(a,b,c,d,e,f,g,h) \ { \ a^=b<<11; d+=a; b+=c; \ b^=c>>2; e+=b; c+=d; \ c^=d<<8; f+=c; d+=e; \ d^=e>>16; g+=d; e+=f; \ e^=f<<10; h+=e; f+=g; \ f^=g>>4; a+=f; g+=h; \ g^=h<<8; b+=g; h+=a; \ h^=a>>9; c+=h; a+=b; \ } /* -------------------------------------------------------------------- checksum() -- hash a variable-length key into a 256-bit value k : the key (the unaligned variable-length array of bytes) len : the length of the key, counting by bytes state : an array of CHECKSTATE 4-byte values (256 bits) The state is the checksum. Every bit of the key affects every bit of the state. There are no funnels. About 112+6.875len instructions. If you are hashing n strings (ub1 **)k, do it like this: for (i=0; i<8; ++i) state[i] = 0x9e3779b9; for (i=0, h=0; i= 32) { a += (k[0] + (k[1] << 8) + (k[2] << 16) + (k[3] << 24)); b += (k[4] + (k[5] << 8) + (k[6] << 16) + (k[7] << 24)); c += (k[8] + (k[9] << 8) + (k[10] << 16) + (k[11] << 24)); d += (k[12] + (k[13] << 8) + (k[14] << 16) + (k[15] << 24)); e += (k[16] + (k[17] << 8) + (k[18] << 16) + (k[19] << 24)); f += (k[20] + (k[21] << 8) + (k[22] << 16) + (k[23] << 24)); g += (k[24] + (k[25] << 8) + (k[26] << 16) + (k[27] << 24)); h += (k[28] + (k[29] << 8) + (k[30] << 16) + (k[31] << 24)); mixc(a, b, c, d, e, f, g, h); mixc(a, b, c, d, e, f, g, h); mixc(a, b, c, d, e, f, g, h); mixc(a, b, c, d, e, f, g, h); k += 32; len -= 32; } /*------------------------------------- handle the last 31 bytes */ h += length; switch (len) { case 31: h += (k[30] << 24); case 30: h += (k[29] << 16); case 29: h += (k[28] << 8); case 28: g += (k[27] << 24); case 27: g += (k[26] << 16); case 26: g += (k[25] << 8); case 25: g += k[24]; case 24: f += (k[23] << 24); case 23: f += (k[22] << 16); case 22: f += (k[21] << 8); case 21: f += k[20]; case 20: e += (k[19] << 24); case 19: e += (k[18] << 16); case 18: e += (k[17] << 8); case 17: e += k[16]; case 16: d += (k[15] << 24); case 15: d += (k[14] << 16); case 14: d += (k[13] << 8); case 13: d += k[12]; case 12: c += (k[11] << 24); case 11: c += (k[10] << 16); case 10: c += (k[9] << 8); case 9: c += k[8]; case 8: b += (k[7] << 24); case 7: b += (k[6] << 16); case 6: b += (k[5] << 8); case 5: b += k[4]; case 4: a += (k[3] << 24); case 3: a += (k[2] << 16); case 2: a += (k[1] << 8); case 1: a += k[0]; } mixc(a, b, c, d, e, f, g, h); mixc(a, b, c, d, e, f, g, h); mixc(a, b, c, d, e, f, g, h); mixc(a, b, c, d, e, f, g, h); /*-------------------------------------------- report the result */ state[0] = a; state[1] = b; state[2] = c; state[3] = d; state[4] = e; state[5] = f; state[6] = g; state[7] = h; } openggsn-0.92/gtp/lookupa.h000066400000000000000000000015001262356443100157030ustar00rootroot00000000000000/* ------------------------------------------------------------------------------ By Bob Jenkins, September 1996. lookupa.h, a hash function for table lookup, same function as lookup.c. Use this code in any way you wish. Public Domain. It has no warranty. Source is http://burtleburtle.net/bob/c/lookupa.h ------------------------------------------------------------------------------ */ /* Uncommented by Jens Jakobsen 20020717 #ifndef STANDARD #include "standard.h" #endif */ #ifndef LOOKUPA #define LOOKUPA typedef unsigned long int ub4; /* unsigned 4-byte quantities */ typedef unsigned char ub1; #define CHECKSTATE 8 #define hashsize(n) ((ub4)1<<(n)) #define hashmask(n) (hashsize(n)-1) ub4 lookup( /*_ ub1 *k, ub4 length, ub4 level _*/ ); void checksum( /*_ ub1 *k, ub4 length, ub4 *state _*/ ); #endif /* LOOKUPA */ openggsn-0.92/gtp/pdp.c000066400000000000000000000254271262356443100150250ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * pdp.c: * */ #include <../config.h> #include #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include "pdp.h" #include "lookupa.h" /* *********************************************************** * Global variables TODO: most should be moved to gsn_t *************************************************************/ static struct pdp_t pdpa[PDP_MAX]; /* PDP storage */ static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */ /* struct pdp_t* haship[PDP_MAX]; Hash table for IP and network interface */ /* *********************************************************** * Functions related to PDP storage * * Lifecycle * For a GGSN pdp context life begins with the reception of a * create pdp context request. It normally ends with the reception * of a delete pdp context request, but will also end with the * reception of an error indication message. * Provisions should probably be made for terminating pdp contexts * based on either idle timeout, or by sending downlink probe * messages (ping?) to see if the MS is still responding. * * For an SGSN pdp context life begins with the application just * before sending off a create pdp context request. It normally * ends when a delete pdp context response message is received * from the GGSN, but should also end when with the reception of * an error indication message. * * * HASH Tables * * Downlink packets received in the GGSN are identified only by their * network interface together with their destination IP address (Two * network interfaces can use the same private IP address). Each IMSI * (mobile station) can have several PDP contexts using the same IP * address. In this case the traffic flow template (TFT) is used to * determine the correct PDP context for a particular IMSI. Also it * should be possible for each PDP context to use several IP adresses * For fixed wireless access a mobile station might need a full class * C network. Even in the case of several IP adresses the PDP context * should be determined on the basis of the network IP address. * Thus we need a hash table based on network interface + IP address. * * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which * is collectively called the tunnel identifier. There is also a 16 bit * flow label that can be used for identification of uplink packets. This * however is quite useless as it limits the number of contexts to 65536. * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier * (32 bit), or in some cases by the combination of IMSI and NSAPI. * For GTP1 delete context requests there is a need to find the PDP * contexts with the same IP address. This however can be done by using * the IP hash table. * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will * be used for directly addressing the PDP context. * pdp_newpdp * Gives you a pdp context with no hash references In some way * this should have a limited lifetime. * * pdp_freepdp * Frees a context that was previously allocated with * pdp_newpdp * * * pdp_getpdpIP * An incoming IP packet is uniquely identified by a pointer * to a network connection (void *) and an IP address * (struct in_addr) * * pdp_getpdpGTP * An incoming GTP packet is uniquely identified by a the * TID (imsi + nsapi (8 octets)) in or by the Flow Label * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier * (4 octets) in gtp1. * * This leads to an architecture where the receiving GSN * chooses a Flow Label or a Tunnel Endpoint Identifier * when the connection is setup. * Thus no hash table is needed for GTP lookups. * *************************************************************/ int pdp_init() { memset(&pdpa, 0, sizeof(pdpa)); memset(&hashtid, 0, sizeof(hashtid)); /* memset(&haship, 0, sizeof(haship)); */ return 0; } int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi, struct pdp_t *pdp_old) { int n; for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */ if (pdpa[n].inuse == 0) { *pdp = &pdpa[n]; if (NULL != pdp_old) memcpy(*pdp, pdp_old, sizeof(struct pdp_t)); else memset(*pdp, 0, sizeof(struct pdp_t)); (*pdp)->inuse = 1; (*pdp)->imsi = imsi; (*pdp)->nsapi = nsapi; (*pdp)->fllc = (uint16_t) n + 1; (*pdp)->fllu = (uint16_t) n + 1; (*pdp)->teid_own = (uint32_t) n + 1; if (!(*pdp)->secondary) (*pdp)->teic_own = (uint32_t) n + 1; pdp_tidset(*pdp, pdp_gettid(imsi, nsapi)); /* Insert reference in primary context */ if (((*pdp)->teic_own > 0) && ((*pdp)->teic_own <= PDP_MAX)) { pdpa[(*pdp)->teic_own - 1].secondary_tei[(*pdp)->nsapi & 0x0f] = (*pdp)->teid_own; } return 0; } } return EOF; /* No more available */ } int pdp_freepdp(struct pdp_t *pdp) { pdp_tiddel(pdp); /* Remove any references in primary context */ if ((pdp->secondary) && (pdp->teic_own > 0) && (pdp->teic_own <= PDP_MAX)) { pdpa[pdp->teic_own - 1].secondary_tei[pdp->nsapi & 0x0f] = 0; } memset(pdp, 0, sizeof(struct pdp_t)); return 0; } int pdp_getpdp(struct pdp_t **pdp) { *pdp = &pdpa[0]; return 0; } int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl) { if ((fl > PDP_MAX) || (fl < 1)) { return EOF; /* Not found */ } else { *pdp = &pdpa[fl - 1]; if ((*pdp)->inuse) return 0; else return EOF; /* Context exists. We do no further validity checking. */ } } int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei) { if ((tei > PDP_MAX) || (tei < 1)) { return EOF; /* Not found */ } else { *pdp = &pdpa[tei - 1]; if ((*pdp)->inuse) return 0; else return EOF; /* Context exists. We do no further validity checking. */ } } int pdp_tidhash(uint64_t tid) { return (lookup(&tid, sizeof(tid), 0) % PDP_MAX); } int pdp_tidset(struct pdp_t *pdp, uint64_t tid) { int hash = pdp_tidhash(tid); struct pdp_t *pdp2; struct pdp_t *pdp_prev = NULL; DEBUGP(DLGTP, "Begin pdp_tidset tid = %"PRIx64"\n", tid); pdp->tidnext = NULL; pdp->tid = tid; for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) pdp_prev = pdp2; if (!pdp_prev) hashtid[hash] = pdp; else pdp_prev->tidnext = pdp; DEBUGP(DLGTP, "End pdp_tidset\n"); return 0; } int pdp_tiddel(struct pdp_t *pdp) { int hash = pdp_tidhash(pdp->tid); struct pdp_t *pdp2; struct pdp_t *pdp_prev = NULL; DEBUGP(DLGTP, "Begin pdp_tiddel tid = %"PRIx64"\n", pdp->tid); for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) { if (pdp2 == pdp) { if (!pdp_prev) hashtid[hash] = pdp2->tidnext; else pdp_prev->tidnext = pdp2->tidnext; DEBUGP(DLGTP, "End pdp_tiddel: PDP found\n"); return 0; } pdp_prev = pdp2; } DEBUGP(DLGTP, "End pdp_tiddel: PDP not found\n"); return EOF; /* End of linked list and not found */ } int pdp_tidget(struct pdp_t **pdp, uint64_t tid) { int hash = pdp_tidhash(tid); struct pdp_t *pdp2; DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid); for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) { if (pdp2->tid == tid) { *pdp = pdp2; DEBUGP(DLGTP, "Begin pdp_tidget. Found\n"); return 0; } } DEBUGP(DLGTP, "Begin pdp_tidget. Not found\n"); return EOF; /* End of linked list and not found */ } int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi) { return pdp_tidget(pdp, (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60)); } /* int pdp_iphash(void* ipif, struct ul66_t *eua) { /#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/ return (lookup(eua->v, eua->l, ipif) % PDP_MAX); } int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) { int hash; struct pdp_t *pdp2; struct pdp_t *pdp_prev = NULL; if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n", (unsigned) ipif, eua->l, eua->v[2], eua->v[3], eua->v[4], eua->v[5]); pdp->ipnext = NULL; pdp->ipif = ipif; pdp->eua.l = eua->l; memcpy(pdp->eua.v, eua->v, eua->l); hash = pdp_iphash(pdp->ipif, &pdp->eua); for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) pdp_prev = pdp2; if (!pdp_prev) haship[hash] = pdp; else pdp_prev->ipnext = pdp; if (PDP_DEBUG) printf("End pdp_ipset\n"); return 0; } int pdp_ipdel(struct pdp_t *pdp) { int hash = pdp_iphash(pdp->ipif, &pdp->eua); struct pdp_t *pdp2; struct pdp_t *pdp_prev = NULL; if (PDP_DEBUG) printf("Begin pdp_ipdel\n"); for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) { if (pdp2 == pdp) { if (!pdp_prev) haship[hash] = pdp2->ipnext; else pdp_prev->ipnext = pdp2->ipnext; if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n"); return 0; } pdp_prev = pdp2; } if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n"); return EOF; /# End of linked list and not found #/ } int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) { int hash = pdp_iphash(ipif, eua); struct pdp_t *pdp2; /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/ for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) { if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) && (memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) { *pdp = pdp2; /#printf("End pdp_ipget. Found\n");#/ return 0; } } if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]); return EOF; /# End of linked list and not found #/ } */ /* Various conversion functions */ int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua) { eua->l = 6; eua->v[0] = 0xf1; /* IETF */ eua->v[1] = 0x21; /* IPv4 */ memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */ return 0; } int pdp_euaton(struct ul66_t *eua, struct in_addr *dst) { if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] != 0x21)) { return EOF; } memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */ return 0; } uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi) { return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60); } void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid) { pdp->imsi = teid & 0x0fffffffffffffffull; pdp->nsapi = (teid & 0xf000000000000000ull) >> 60; } int ulcpy(void *dst, void *src, size_t size) { if (((struct ul255_t *)src)->l <= size) { ((struct ul255_t *)dst)->l = ((struct ul255_t *)src)->l; memcpy(((struct ul255_t *)dst)->v, ((struct ul255_t *)src)->v, ((struct ul255_t *)dst)->l); return 0; } else return EOF; } openggsn-0.92/gtp/pdp.h000066400000000000000000000275601262356443100150320ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #ifndef _PDP_H #define _PDP_H #define PDP_MAX 1024 /* Max number of PDP contexts */ #define PDP_MAXNSAPI 16 /* Max number of NSAPI */ /* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */ /* Also covers version 0. Note that version 0 6: QOS Profile was superceded * * by 135: QOS Profile in version 1 */ struct sl_t { unsigned int l; char *v; }; struct ul_t { unsigned int l; unsigned char *v; }; struct ul16_t { unsigned int l; unsigned char v[16]; }; struct ul66_t { unsigned int l; unsigned char v[66]; }; struct ul255_t { unsigned int l; unsigned char v[255]; }; /* ***************************************************************** * Information storage for each PDP context * * Information storage for each PDP context is defined in 23.060 * section 13.3 and 03.60. Includes IMSI, MSISDN, APN, PDP-type, * PDP-address (IP address), sequence numbers, charging ID. For the * SGSN it also includes radio related mobility information. * * The following structure is a combination of the storage * requirements for each PDP context for the GGSN and SGSN. It * contains both 23.060 as well as 03.60 parameters. Information is * stored in the format for information elements described in 29.060 * and 09.60. * 31 * 4 + 15 structs + = 120 + 15 structs ~ 2k / context * Structs: IP address 16+4 bytes (6), APN 255 bytes (2) * QOS: 255 bytes (3), msisdn 16 bytes (1), * * TODO: We need to consider who manages the pdp_t hash tables * Is it gtp_lib, or is it the application? * I suppose that it will be gtp_lib. * SGSN will ask gtplib for new pdp_t. Fill out the fields, * and pass it on to gtp_create_pdp_req. * GGSN will receive gtp_create_pdp_ind, create new pdp_t and * send responce to SGSN. * SGSN will receive response and gtplib will find the * original pdp_t corresponding to the request. This will be * passed on to the application. * Eventually the SGSN will close the connection, and the * pdp_t will be freed by gtplib in SGSN and GGSN * This means that gtplib need to have functions to * allocate, free, sort and find pdp_t * (newpdp, freepdp, getpdp) * Hash tables: TID, IMSI, IP etc.) * * * Secondary PDP Context Activation Procedure * * With GTP version 1 it is possible to establish multiple PDP * contexts with the same IP address. With this scheme the first * context is established as normal. Following contexts are * established by referencing one of the allready existing ones. Each * context is uniquely identified by IMSI and NSAPI. Each context is * allocated an tei_di, but they all share the same tei_c. * * For Delete PDP Context the context is referenced by tei_c and * nsapi. As an option it is possible to include a teardown indicater, * in which case all PDP contexts with the same tei_c (IP address) are * deleted. * * For Update PDP Context the context is normally referenced by tei_c * and nsapi. If moving from GTP0 to GTP1 during an update the context * is referenced instead by IMSI and NSAPI. *****************************************************************/ struct pdp_t { /* Parameter determining if this PDP is in use. */ uint8_t inuse; /* 0=free. 1=used by somebody */ /* Pointers related to hash tables */ struct pdp_t *tidnext; struct pdp_t *ipnext; /* Parameters shared by all PDP context belonging to the same MS */ void *ipif; /* IP network interface */ void *peer; /* Pointer to peer protocol */ void *asap; /* Application specific service access point */ uint64_t imsi; /* International Mobile Subscriber Identity. */ struct ul16_t msisdn; /* The basic MSISDN of the MS. */ uint8_t mnrg; /* Indicates whether the MS is marked as not reachable for PS at the HLR. (1 bit, not transmitted) */ uint8_t cch_sub; /* The charging characteristics for the MS, e.g. normal, prepaid, flat-rate, and/or hot billing subscription. (not transmitted) */ uint16_t traceref; /* Identifies a record or a collection of records for a particular trace. */ uint16_t tracetype; /* Indicates the type of trace. */ struct ul_t triggerid; /* Identifies the entity that initiated the trace. */ struct ul_t omcid; /* Identifies the OMC that shall receive the trace record(s). */ uint8_t rec_hlr; /* Indicates if HLR or VLR is performing database recovery. (1 bit, not transmitted) */ /* Parameters specific to each individual PDP context */ uint8_t pdp_id; /* Index of the PDP context. (PDP context identifier) */ uint8_t pdp_state; /* PDP State Packet data protocol state, INACTIVE or ACTIVE. (1 bit, not transmitted) */ /* struct ul_t pdp_type; * PDP type; e.g. PPP or IP. */ /* struct ul_t pdp_addr; * PDP address; e.g. an IP address. */ struct ul66_t eua; /* End user address. PDP type and address combined */ uint8_t pdp_dyn; /* Indicates whether PDP Address is static or dynamic. (1 bit, not transmitted) */ struct ul255_t apn_req; /* The APN requested. */ struct ul255_t apn_sub; /* The APN received from the HLR. */ struct ul255_t apn_use; /* The APN Network Identifier currently used. */ uint8_t nsapi; /* Network layer Service Access Point Identifier. (4 bit) */ uint16_t ti; /* Transaction Identifier. (4 or 12 bit) */ uint32_t teic_own; /* (Own Tunnel Endpoint Identifier Control) */ uint32_t teid_own; /* (Own Tunnel Endpoint Identifier Data I) */ uint32_t teic_gn; /* Tunnel Endpoint Identifier for the Gn and Gp interfaces. (Control plane) */ uint32_t teid_gn; /* Tunnel Endpoint Identifier for the Gn and Gp interfaces. (Data I) */ uint32_t tei_iu; /* Tunnel Endpoint Identifier for the Iu interface. */ uint16_t fllc; /* (Local Flow Label Control, gtp0) */ uint16_t fllu; /* (Local Flow Label Data I, gtp0) */ uint16_t flrc; /* (Remote gn/gp Flow Label Control, gtp0) */ uint16_t flru; /* (Remote gn/gp Flow Label Data I, gtp0) */ struct ul255_t tft; /* Traffic flow template. */ /*struct ul16_t sgsnc; * The IP address of the SGSN currently serving this MS. (Control plane) */ /*struct ul16_t sgsnu; * The IP address of the SGSN currently serving this MS. (User plane) */ /*struct ul16_t ggsnc; * The IP address of the GGSN currently used. (Control plane) */ /*struct ul16_t ggsnu; * The IP address of the GGSN currently used. (User plane) */ struct ul16_t gsnlc; /* The IP address of the local GSN. (Control plane) */ struct ul16_t gsnlu; /* The IP address of the local GSN. (User plane) */ struct ul16_t gsnrc; /* The IP address of the remote GSN. (Control plane) */ struct ul16_t gsnru; /* The IP address of the remote GSN. (User plane) */ uint8_t vplmn_allow; /* Specifies whether the MS is allowed to use the APN in the domain of the HPLMN only, or additionally the APN in the domain of the VPLMN. (1 bit) */ uint8_t qos_sub0[3]; /* The quality of service profile subscribed. */ uint8_t qos_req0[3]; /* The quality of service profile requested. */ uint8_t qos_neg0[3]; /* The quality of service profile negotiated. */ struct ul255_t qos_sub; /* The quality of service profile subscribed. */ struct ul255_t qos_req; /* The quality of service profile requested. */ struct ul255_t qos_neg; /* The quality of service profile negotiated. */ uint8_t radio_pri; /* The RLC/MAC radio priority level for uplink user data transmission. (4 bit) */ uint16_t flow_id; /* Packet flow identifier. */ /* struct ul_t bssqos_neg; * The aggregate BSS quality of service profile negotiated for the packet flow that this PDP context belongs to. (NOT GTP) */ uint8_t sndcpd; /* SNDCP sequence number of the next downlink N-PDU to be sent to the MS. */ uint8_t sndcpu; /* SNDCP sequence number of the next uplink N-PDU expected from the MS. */ uint8_t rec_sgsn; /* Indicates if the SGSN is performing database recovery. (1 bit, not transmitted) */ /* uint16_t gtpsnd; GTP-U sequence number of the next downlink N-PDU to be sent to the SGSN / received from the GGSN. */ /* uint16_t gtpsnu; GTP-U sequence number of the next uplink N-PDU to be received from the SGSN / sent to the GGSN */ uint16_t gtpsntx; /* GTP-U sequence number of the next downlink N-PDU to be sent (09.60 section 8.1.1.1) */ uint16_t gtpsnrx; /* GTP-U sequence number of the next uplink N-PDU to be received (09.60 section 8.1.1.1) */ uint8_t pdcpsndd; /* Sequence number of the next downlink in-sequence PDCP-PDU to be sent to the MS. */ uint8_t pdcpsndu; /* Sequence number of the next uplink in-sequence PDCP-PDU expected from the MS. */ uint32_t cid; /* Charging identifier, identifies charging records generated by SGSN and GGSN. */ uint16_t cch_pdp; /* The charging characteristics for this PDP context, e.g. normal, prepaid, flat-rate, and/or hot billing. */ struct ul16_t rnc_addr; /* The IP address of the RNC currently used. */ uint8_t reorder; /* Specifies whether the GGSN shall reorder N-PDUs received from the SGSN / Specifies whether the SGSN shall reorder N-PDUs before delivering the N-PSUs to the MS. (1 bit) */ struct ul255_t pco_req; /* Requested packet control options. */ struct ul255_t pco_neg; /* Negotiated packet control options. */ uint32_t selmode; /* Selection mode. */ struct ul255_t rattype; /* Radio Access Technology Type */ int rattype_given; /* Radio Access Technology Type given */ struct ul255_t userloc; /* User Location Information */ int userloc_given; /* User Location Information given */ struct ul255_t rai; /* Routing Area Information */ int rai_given; /* Routing Area Information given */ struct ul255_t mstz; /* MS Time Zone */ int mstz_given; /* MS Time Zone given */ struct ul255_t imeisv; /* IMEI Software Version */ int imeisv_given; /* IMEI Software Version given */ int norecovery_given; /* norecovery given */ /* Additional parameters used by library */ int version; /* Protocol version currently in use. 0 or 1 */ uint64_t tid; /* Combination of imsi and nsapi */ uint16_t seq; /* Sequence number of last request */ struct sockaddr_in sa_peer; /* Address of last request */ int fd; /* File descriptor request was received on */ uint8_t teic_confirmed; /* 0: Not confirmed. 1: Confirmed */ /* Parameters used for secondary activation procedure (tei data) */ /* If (secondary == 1) then teic_own indicates linked PDP context */ uint8_t secondary; /* 0: Primary (control). 1: Secondary (data only) */ uint8_t nodata; /* 0: User plane PDP context. 1: No user plane */ /* Secondary contexts of this primary context */ uint32_t secondary_tei[PDP_MAXNSAPI]; /* IP address used for Create and Update PDP Context Requests */ struct in_addr hisaddr0; /* Server address */ struct in_addr hisaddr1; /* Server address */ /* to be used by libgtp callers/users (to attach their own private state) */ void *priv; }; /* functions related to pdp_t management */ int pdp_init(); int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi, struct pdp_t *pdp_old); int pdp_freepdp(struct pdp_t *pdp); int pdp_getpdp(struct pdp_t **pdp); int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl); int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei); int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi); int pdp_tidhash(uint64_t tid); int pdp_tidset(struct pdp_t *pdp, uint64_t tid); int pdp_tiddel(struct pdp_t *pdp); int pdp_tidget(struct pdp_t **pdp, uint64_t tid); void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid); /* int pdp_iphash(void* ipif, struct ul66_t *eua); int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua); int pdp_ipdel(struct pdp_t *pdp); int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua); */ int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua); int pdp_euaton(struct ul66_t *eua, struct in_addr *dst); uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi); int ulcpy(void *dst, void *src, size_t size); #endif /* !_PDP_H */ openggsn-0.92/gtp/queue.c000066400000000000000000000172301262356443100153570ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * Copyright (C) 2011 Harald Welte * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * Queue.c * Reliable delivery of signalling messages */ #include <../config.h> #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include "pdp.h" #include "gtp.h" #include "queue.h" /*! \brief dump a queue_t to stdout */ static int queue_print(struct queue_t *queue) { int n; printf("Queue: %p Next: %d First: %d Last: %d\n", queue, queue->next, queue->first, queue->last); printf("# State seq next prev timeout retrans\n"); for (n = 0; n < QUEUE_SIZE; n++) { printf("%d %d %d %d %d %d %d\n", n, queue->qmsga[n].state, queue->qmsga[n].seq, queue->qmsga[n].next, queue->qmsga[n].prev, (int)queue->qmsga[n].timeout, queue->qmsga[n].retrans); } return 0; } /*! \brief compute the hash function */ static int queue_seqhash(struct sockaddr_in *peer, uint16_t seq) { /* With QUEUE_HASH_SIZE = 2^16 this describes all possible seq values. Thus we have perfect hash for the request queue. For the response queue we might have collisions, but not very often. For performance optimisation we should remove the modulus operator, but this is only valid for QUEUE_HASH_SIZE = 2^16 */ return seq % QUEUE_HASH_SIZE; } /*! \brief Insert a message with given sequence number into the hash * * This function sets the peer and the seq of the qmsg and then inserts * the qmsg into the queue hash. To do so, it does a hashtable lookup * and appends the new entry as the last into the double-linked list of * entries for this sequence number. */ static int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg, struct sockaddr_in *peer, uint16_t seq) { int hash = queue_seqhash(peer, seq); struct qmsg_t *qmsg2; struct qmsg_t *qmsg_prev = NULL; if (QUEUE_DEBUG) printf("Begin queue_seqset seq = %d\n", (int)seq); if (QUEUE_DEBUG) printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer), sizeof(*peer)); qmsg->seq = seq; memcpy(&qmsg->peer, peer, sizeof(*peer)); for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) qmsg_prev = qmsg2; if (!qmsg_prev) queue->hashseq[hash] = qmsg; else qmsg_prev->seqnext = qmsg; if (QUEUE_DEBUG) printf("End queue_seqset\n"); return 0; } /*! \brief Remove a given qmsg_t from the queue hash */ static int queue_seqdel(struct queue_t *queue, struct qmsg_t *qmsg) { int hash = queue_seqhash(&qmsg->peer, qmsg->seq); struct qmsg_t *qmsg2; struct qmsg_t *qmsg_prev = NULL; if (QUEUE_DEBUG) printf("Begin queue_seqdel seq = %d\n", (int)qmsg->seq); for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) { /* FIXME: this is always true !?! */ if (qmsg == qmsg) { if (!qmsg_prev) queue->hashseq[hash] = qmsg2->seqnext; else qmsg_prev->seqnext = qmsg2->seqnext; if (QUEUE_DEBUG) printf("End queue_seqdel: SEQ found\n"); return 0; } qmsg_prev = qmsg2; } printf("End queue_seqdel: SEQ not found\n"); return EOF; /* End of linked list and not found */ } /*! \brief Allocates and initialises new queue structure */ int queue_new(struct queue_t **queue) { if (QUEUE_DEBUG) printf("queue_new\n"); *queue = calloc(1, sizeof(struct queue_t)); (*queue)->next = 0; (*queue)->first = -1; (*queue)->last = -1; if (QUEUE_DEBUG) queue_print(*queue); if (*queue) return 0; else return EOF; } /*! \brief Deallocates queue structure */ int queue_free(struct queue_t *queue) { if (QUEUE_DEBUG) printf("queue_free\n"); if (QUEUE_DEBUG) queue_print(queue); free(queue); return 0; } /*! \brief Add a new message to the queue */ int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg, struct sockaddr_in *peer, uint16_t seq) { if (QUEUE_DEBUG) printf("queue_newmsg %d\n", (int)seq); if (queue->qmsga[queue->next].state == 1) { return EOF; /* Queue is full */ } else { *qmsg = &queue->qmsga[queue->next]; queue_seqset(queue, *qmsg, peer, seq); (*qmsg)->state = 1; /* Space taken */ (*qmsg)->this = queue->next; (*qmsg)->next = -1; /* End of the queue */ (*qmsg)->prev = queue->last; /* Link to the previous */ if (queue->last != -1) queue->qmsga[queue->last].next = queue->next; /* Link previous to us */ queue->last = queue->next; /* End of queue */ if (queue->first == -1) queue->first = queue->next; queue->next = (queue->next + 1) % QUEUE_SIZE; /* Increment */ if (QUEUE_DEBUG) queue_print(queue); return 0; } } /*! \brief Simply remoev a given qmsg_t from the queue * * Internally, we first delete the entry from the queue, and then update * up our global queue->first / queue->last pointers. Finally, * the qmsg_t is re-initialized with zero bytes. No memory is released. */ int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg) { if (QUEUE_DEBUG) printf("queue_freemsg\n"); if (qmsg->state != 1) { return EOF; /* Not in queue */ } queue_seqdel(queue, qmsg); if (qmsg->next == -1) /* Are we the last in queue? */ queue->last = qmsg->prev; else queue->qmsga[qmsg->next].prev = qmsg->prev; if (qmsg->prev == -1) /* Are we the first in queue? */ queue->first = qmsg->next; else queue->qmsga[qmsg->prev].next = qmsg->next; memset(qmsg, 0, sizeof(struct qmsg_t)); /* Just to be safe */ if (QUEUE_DEBUG) queue_print(queue); return 0; } /*! \brief Move a given qmsg_t to the end of the queue ?!? */ int queue_back(struct queue_t *queue, struct qmsg_t *qmsg) { if (QUEUE_DEBUG) printf("queue_back\n"); if (qmsg->state != 1) { return EOF; /* Not in queue */ } /* Insert stuff to maintain hash table */ if (qmsg->next != -1) { /* Only swop if there are others */ queue->qmsga[qmsg->next].prev = qmsg->prev; queue->first = qmsg->next; qmsg->next = -1; qmsg->prev = queue->last; if (queue->last != -1) queue->qmsga[queue->last].next = qmsg->this; queue->last = qmsg->this; } if (QUEUE_DEBUG) queue_print(queue); return 0; } /*! \brief Get the first element in the entire queue */ int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg) { /*printf("queue_getfirst\n"); */ if (queue->first == -1) { *qmsg = NULL; return EOF; /* End of queue = queue is empty. */ } *qmsg = &queue->qmsga[queue->first]; if (QUEUE_DEBUG) queue_print(queue); return 0; } /*! \brief Get a queue entry for a given peer + seq */ int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg, struct sockaddr_in *peer, uint16_t seq) { int hash = queue_seqhash(peer, seq); struct qmsg_t *qmsg2; if (QUEUE_DEBUG) printf("Begin queue_seqget seq = %d\n", (int)seq); for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) { if ((qmsg2->seq == seq) && (!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) { *qmsg = qmsg2; if (QUEUE_DEBUG) printf("End queue_seqget. Found\n"); return 0; } } if (QUEUE_DEBUG) printf("End queue_seqget. Not found\n"); return EOF; /* End of linked list and not found */ } /*! \brief look-up a given seq/peer, return cbp + type and free entry */ int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer, uint16_t seq, uint8_t * type, void **cbp) { struct qmsg_t *qmsg; if (queue_seqget(queue, &qmsg, peer, seq)) { *cbp = NULL; *type = 0; return EOF; } *cbp = qmsg->cbp; *type = qmsg->type; if (queue_freemsg(queue, qmsg)) { return EOF; } return 0; } openggsn-0.92/gtp/queue.h000066400000000000000000000052321262356443100153630ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * Queue.c * Reliable delivery of signalling messages */ #ifndef _QUEUE_H #define _QUEUE_H #define QUEUE_DEBUG 0 /* Print debug information */ #define QUEUE_SIZE 1024 /* Size of retransmission queue */ #define QUEUE_HASH_SIZE 65536 /* Size of hash table (2^16) */ struct qmsg_t { /* Holder for queued packets */ int state; /* 0=empty, 1=full */ uint16_t seq; /* The sequence number */ uint8_t type; /* The type of packet */ void *cbp; /* Application specific pointer */ union gtp_packet p; /* The packet stored */ int l; /* Length of the packet */ int fd; /* Socket packet was sent to / received from */ struct sockaddr_in peer; /* Address packet was sent to / received from */ struct qmsg_t *seqnext; /* Pointer to next in sequence hash list */ int next; /* Pointer to the next in queue. -1: Last */ int prev; /* Pointer to the previous in queue. -1: First */ int this; /* Pointer to myself */ time_t timeout; /* When do we retransmit this packet? */ int retrans; /* How many times did we retransmit this? */ }; struct queue_t { struct qmsg_t qmsga[QUEUE_SIZE]; /* Array holding signalling messages */ void *hashseq[QUEUE_HASH_SIZE]; /* Hash array */ int next; /* Next location in queue to use */ int first; /* First packet in queue (oldest timeout) */ int last; /* Last packet in queue (youngest timeout) */ }; /* Allocates and initialises new queue structure */ int queue_new(struct queue_t **queue); /* Deallocates queue structure */ int queue_free(struct queue_t *queue); /* Find a new queue element. Return EOF if allready full */ int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg, struct sockaddr_in *peer, uint16_t seq); /* Remove an element from the queue. */ int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg); /* Move an element to the back of the queue */ int queue_back(struct queue_t *queue, struct qmsg_t *qmsg); /* Get the first element in the queue (oldest) */ int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg); /* Get the element with a particular sequence number */ int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg, struct sockaddr_in *peer, uint16_t seq); /* Free message based on sequence number */ int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer, uint16_t seq, uint8_t * type, void **cbp); #endif /* !_QUEUE_H */ openggsn-0.92/intl/000077500000000000000000000000001262356443100142405ustar00rootroot00000000000000openggsn-0.92/intl/Makefile.in000066400000000000000000000000001262356443100162730ustar00rootroot00000000000000openggsn-0.92/lib/000077500000000000000000000000001262356443100140405ustar00rootroot00000000000000openggsn-0.92/lib/Makefile.am000066400000000000000000000003751262356443100161010ustar00rootroot00000000000000noinst_LIBRARIES = libmisc.a noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c openggsn-0.92/lib/debug.c000066400000000000000000000013161262356443100152730ustar00rootroot00000000000000/* * (C) 2014 by Holger Hans Peter Freyther */ #include "syserr.h" #include static const struct log_info_cat default_categories[] = { [DIP] = { .name = "DIP", .description = "IP Pool and other groups", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DTUN] = { .name = "DTUN", .description = "Tunnel interface", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DGGSN] = { .name = "DGGSN", .description = "GGSN", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSGSN] = { .name = "DSGSN", .description = "SGSN Emulator", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; const struct log_info log_info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; openggsn-0.92/lib/getopt.c000066400000000000000000000721171262356443100155160ustar00rootroot00000000000000/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #ifdef HAVE_CONFIG_H #include #endif #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #include #endif /* GNU C library. */ #ifdef VMS #include #if HAVE_STRING_H - 0 #include #endif #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. */ #if defined HAVE_LIBINTL_H || defined _LIBC #include #ifndef _ #define _(msgid) gettext (msgid) #endif #else #define _(msgid) (msgid) #endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #else #if HAVE_STRING_H #include #else #include #endif /* Avoid depending on library functions or files whose names are inconsistent. */ #ifndef getenv extern char *getenv(); #endif static char *my_index(str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *)str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ #if (!defined __STDC__ || !__STDC__) && !defined strlen /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen(const char *); #endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Stored original parameters. XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ extern int __libc_argc; extern char **__libc_argv; /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ #ifdef USE_NONOPTION_FLAGS /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; #endif #ifdef USE_NONOPTION_FLAGS #define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } #else #define SWAP_FLAGS(ch1, ch2) #endif #else /* !_LIBC */ #define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined __STDC__ && __STDC__ static void exchange(char **); #endif static void exchange(argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc(top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memset(__mempcpy(new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS(bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined __STDC__ && __STDC__ static const char *_getopt_initialize(int, char *const *, const char *); #endif static const char *_getopt_initialize(argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #if defined _LIBC && defined USE_NONOPTION_FLAGS if (posixly_correct == NULL && argc == __libc_argc && argv == __libc_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen(orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *)malloc(nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else memset(__mempcpy (__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal(argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int print_errors = opterr; if (optstring[0] == ':') print_errors = 0; if (argc < 1) return -1; optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize(argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else #define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **)argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp(argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **)argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp(p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == (unsigned int)strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf(stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen(nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) { if (argv[optind - 1][1] == '-') /* --option */ fprintf(stderr, _ ("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf(stderr, _ ("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen(nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf(stderr, _ ("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen(nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index(optstring, *nextchar) == NULL) { if (print_errors) { if (argv[optind][1] == '-') /* --option */ fprintf(stderr, _ ("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf(stderr, _ ("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *)""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index(optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (print_errors) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf(stderr, _ ("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf(stderr, _ ("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen(nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) fprintf(stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen(nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf(stderr, _ ("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen(nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf(stderr, _ ("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt(argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal(argc, argv, optstring, (const struct option *)0, (int *)0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main(argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt(argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf("option %c\n", c); break; case 'a': printf("option a\n"); break; case 'b': printf("option b\n"); break; case 'c': printf("option c with value `%s'\n", optarg); break; case '?': break; default: printf("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } exit(0); } #endif /* TEST */ openggsn-0.92/lib/getopt1.c000066400000000000000000000105071262356443100155720ustar00rootroot00000000000000/* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "getopt.h" #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long(argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal(argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only(argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal(argc, argv, options, long_options, opt_index, 1); } #endif /* Not ELIDE_CODE. */ #ifdef TEST #include int main(argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf("option %c\n", c); break; case 'a': printf("option a\n"); break; case 'b': printf("option b\n"); break; case 'c': printf("option c with value `%s'\n", optarg); break; case 'd': printf("option d with value `%s'\n", optarg); break; case '?': break; default: printf("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } exit(0); } #endif /* TEST */ openggsn-0.92/lib/gnugetopt.h000066400000000000000000000144761262356443100162410ustar00rootroot00000000000000/* Declarations for getopt. Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _GETOPT_H #ifndef __need_getopt #define _GETOPT_H 1 #endif /* If __GNU_LIBRARY__ is not already defined, either we are being used standalone, or this is the first header included in the source file. If we are being used with glibc, we need to include , but that does not exist if we are standalone. So: if __GNU_LIBRARY__ is not defined, include , which will pull in for us if it's from glibc. (Why ctype.h? It's guaranteed to exist and it doesn't flood the namespace with stuff the way some other headers do.) */ #if !defined __GNU_LIBRARY__ #include #endif #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; #ifndef __need_getopt /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if (defined __STDC__ && __STDC__) || defined __cplusplus const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #endif /* need getopt */ /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ #if (defined __STDC__ && __STDC__) || defined __cplusplus #ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt(int __argc, char *const *__argv, const char *__shortopts); #else /* not __GNU_LIBRARY__ */ extern int getopt(); #endif /* __GNU_LIBRARY__ */ #ifndef __need_getopt extern int getopt_long(int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); extern int getopt_long_only(int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal(int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only); #endif #else /* not __STDC__ */ extern int getopt(); #ifndef __need_getopt extern int getopt_long(); extern int getopt_long_only(); extern int _getopt_internal(); #endif #endif /* __STDC__ */ #ifdef __cplusplus } #endif /* Make sure we later can get all the definitions and declarations. */ #undef __need_getopt #endif /* getopt.h */ openggsn-0.92/lib/ippool.c000066400000000000000000000323351262356443100155140ustar00rootroot00000000000000/* * IP address pool functions. * Copyright (C) 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #include #include /* in_addr */ #include /* calloc */ #include /* sscanf */ #include #include #include #include "syserr.h" #include "ippool.h" #include "lookup.h" int ippool_printaddr(struct ippool_t *this) { unsigned int n; printf("ippool_printaddr\n"); printf("Firstdyn %d\n", this->firstdyn - this->member); printf("Lastdyn %d\n", this->lastdyn - this->member); printf("Firststat %d\n", this->firststat - this->member); printf("Laststat %d\n", this->laststat - this->member); printf("Listsize %d\n", this->listsize); for (n = 0; n < this->listsize; n++) { printf("Unit %d inuse %d prev %d next %d addr %s %x\n", n, this->member[n].inuse, this->member[n].prev - this->member, this->member[n].next - this->member, inet_ntoa(this->member[n].addr), this->member[n].addr.s_addr); } return 0; } int ippool_hashadd(struct ippool_t *this, struct ippoolm_t *member) { uint32_t hash; struct ippoolm_t *p; struct ippoolm_t *p_prev = NULL; /* Insert into hash table */ hash = ippool_hash4(&member->addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) p_prev = p; if (!p_prev) this->hash[hash] = member; else p_prev->nexthash = member; return 0; /* Always OK to insert */ } int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member) { uint32_t hash; struct ippoolm_t *p; struct ippoolm_t *p_prev = NULL; /* Find in hash table */ hash = ippool_hash4(&member->addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { if (p == member) { break; } p_prev = p; } if (p != member) { SYS_ERR(DIP, LOGL_ERROR, 0, "ippool_hashdel: Tried to delete member not in hash table"); return -1; } if (!p_prev) this->hash[hash] = p->nexthash; else p_prev->nexthash = p->nexthash; return 0; } unsigned long int ippool_hash4(struct in_addr *addr) { return lookup((unsigned char *)&addr->s_addr, sizeof(addr->s_addr), 0); } #ifndef IPPOOL_NOIP6 unsigned long int ippool_hash6(struct in6_addr *addr) { return lookup((unsigned char *)addr->u6_addr8, sizeof(addr->u6_addr8), 0); } #endif /* Get IP address and mask */ int ippool_aton(struct in_addr *addr, struct in_addr *mask, char *pool, int number) { /* Parse only first instance of network for now */ /* Eventually "number" will indicate the token which we want to parse */ unsigned int a1, a2, a3, a4; unsigned int m1, m2, m3, m4; int c; int m; int masklog; c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u", &a1, &a2, &a3, &a4, &m1, &m2, &m3, &m4); switch (c) { case 4: mask->s_addr = 0xffffffff; break; case 5: if (m1 > 32) { SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Invalid mask */ } mask->s_addr = htonl(0xffffffff << (32 - m1)); break; case 8: if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256) { SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Wrong mask format */ } m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4; for (masklog = 0; ((1 << masklog) < ((~m) + 1)); masklog++) ; if (((~m) + 1) != (1 << masklog)) { SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Wrong mask format (not all ones followed by all zeros) */ } mask->s_addr = htonl(m); break; default: SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Invalid mask */ } if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) { SYS_ERR(DIP, LOGL_ERROR, 0, "Wrong IP address format"); return -1; } else addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4); return 0; } /* Create new address pool */ int ippool_new(struct ippool_t **this, char *dyn, char *stat, int allowdyn, int allowstat, int flags) { /* Parse only first instance of pool for now */ int i; struct in_addr addr; struct in_addr mask; struct in_addr stataddr; struct in_addr statmask; unsigned int m; int listsize; int dynsize; unsigned int statsize; if (!allowdyn) { dynsize = 0; } else { if (ippool_aton(&addr, &mask, dyn, 0)) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to parse dynamic pool"); return -1; } /* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */ if (flags & IPPOOL_NOGATEWAY) { flags |= IPPOOL_NONETWORK; } m = ntohl(mask.s_addr); dynsize = ((~m) + 1); if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */ dynsize--; if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */ dynsize--; if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */ dynsize--; } if (!allowstat) { statsize = 0; stataddr.s_addr = 0; statmask.s_addr = 0; } else { if (ippool_aton(&stataddr, &statmask, stat, 0)) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to parse static range"); return -1; } m = ntohl(statmask.s_addr); statsize = ((~m) + 1); if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE; } listsize = dynsize + statsize; /* Allocate space for static IP addresses */ if (!(*this = calloc(sizeof(struct ippool_t), 1))) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to allocate memory for ippool"); return -1; } (*this)->allowdyn = allowdyn; (*this)->allowstat = allowstat; (*this)->stataddr = stataddr; (*this)->statmask = statmask; (*this)->listsize += listsize; if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to allocate memory for members in ippool"); return -1; } for ((*this)->hashlog = 0; ((1 << (*this)->hashlog) < listsize); (*this)->hashlog++) ; /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */ /* Determine hashsize */ (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet */ (*this)->hashmask = (*this)->hashsize - 1; /* Allocate hash table */ if (! ((*this)->hash = calloc(sizeof(struct ippoolm_t), (*this)->hashsize))) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to allocate memory for hash members in ippool"); return -1; } (*this)->firstdyn = NULL; (*this)->lastdyn = NULL; for (i = 0; i < dynsize; i++) { if (flags & IPPOOL_NOGATEWAY) (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 2); else if (flags & IPPOOL_NONETWORK) (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 1); else (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i); (*this)->member[i].inuse = 0; /* Insert into list of unused */ (*this)->member[i].prev = (*this)->lastdyn; if ((*this)->lastdyn) { (*this)->lastdyn->next = &((*this)->member[i]); } else { (*this)->firstdyn = &((*this)->member[i]); } (*this)->lastdyn = &((*this)->member[i]); (*this)->member[i].next = NULL; /* Redundant */ (void)ippool_hashadd(*this, &(*this)->member[i]); } (*this)->firststat = NULL; (*this)->laststat = NULL; for (i = dynsize; i < listsize; i++) { (*this)->member[i].addr.s_addr = 0; (*this)->member[i].inuse = 0; /* Insert into list of unused */ (*this)->member[i].prev = (*this)->laststat; if ((*this)->laststat) { (*this)->laststat->next = &((*this)->member[i]); } else { (*this)->firststat = &((*this)->member[i]); } (*this)->laststat = &((*this)->member[i]); (*this)->member[i].next = NULL; /* Redundant */ } if (0) (void)ippool_printaddr(*this); return 0; } /* Delete existing address pool */ int ippool_free(struct ippool_t *this) { free(this->hash); free(this->member); free(this); return 0; /* Always OK */ } /* Find an IP address in the pool */ int ippool_getip(struct ippool_t *this, struct ippoolm_t **member, struct in_addr *addr) { struct ippoolm_t *p; uint32_t hash; /* Find in hash table */ hash = ippool_hash4(addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) { if (member) *member = p; return 0; } } if (member) *member = NULL; /*SYS_ERR(DIP, LOGL_ERROR, 0, "Address could not be found"); */ return -1; } /** * ippool_newip * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise * check to see if the given address is available. If available within * dynamic address space allocate it there, otherwise allocate within static * address space. **/ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member, struct in_addr *addr, int statip) { struct ippoolm_t *p; struct ippoolm_t *p2 = NULL; uint32_t hash; /* If static: * Look in dynaddr. * If found remove from firstdyn/lastdyn linked list. * Else allocate from stataddr. * Remove from firststat/laststat linked list. * Insert into hash table. * * If dynamic * Remove from firstdyn/lastdyn linked list. * */ if (0) (void)ippool_printaddr(this); /* First check to see if this type of address is allowed */ if ((addr) && (addr->s_addr) && statip) { /* IP address given */ if (!this->allowstat) { SYS_ERR(DIP, LOGL_ERROR, 0, "Static IP address not allowed"); return -1; } if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) { SYS_ERR(DIP, LOGL_ERROR, 0, "Static out of range"); return -1; } } else { if (!this->allowdyn) { SYS_ERR(DIP, LOGL_ERROR, 0, "Dynamic IP address not allowed"); return -1; } } /* If IP address given try to find it in dynamic address pool */ if ((addr) && (addr->s_addr)) { /* IP address given */ /* Find in hash table */ hash = ippool_hash4(addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { if ((p->addr.s_addr == addr->s_addr)) { p2 = p; break; } } } /* If IP was already allocated we can not use it */ if ((!statip) && (p2) && (p2->inuse)) { p2 = NULL; } /* If not found yet and dynamic IP then allocate dynamic IP */ if ((!p2) && (!statip)) { if (!this->firstdyn) { SYS_ERR(DIP, LOGL_ERROR, 0, "No more IP addresses available"); return -1; } else p2 = this->firstdyn; } if (p2) { /* Was allocated from dynamic address pool */ if (p2->inuse) { SYS_ERR(DIP, LOGL_ERROR, 0, "IP address allready in use"); return -1; /* Allready in use / Should not happen */ } /* Remove from linked list of free dynamic addresses */ if (p2->prev) p2->prev->next = p2->next; else this->firstdyn = p2->next; if (p2->next) p2->next->prev = p2->prev; else this->lastdyn = p2->prev; p2->next = NULL; p2->prev = NULL; p2->inuse = 1; /* Dynamic address in use */ *member = p2; if (0) (void)ippool_printaddr(this); return 0; /* Success */ } /* It was not possible to allocate from dynamic address pool */ /* Try to allocate from static address space */ if ((addr) && (addr->s_addr) && (statip)) { /* IP address given */ if (!this->firststat) { SYS_ERR(DIP, LOGL_ERROR, 0, "No more IP addresses available"); return -1; /* No more available */ } else p2 = this->firststat; /* Remove from linked list of free static addresses */ if (p2->prev) p2->prev->next = p2->next; else this->firststat = p2->next; if (p2->next) p2->next->prev = p2->prev; else this->laststat = p2->prev; p2->next = NULL; p2->prev = NULL; p2->inuse = 2; /* Static address in use */ memcpy(&p2->addr, addr, sizeof(addr)); *member = p2; (void)ippool_hashadd(this, *member); if (0) (void)ippool_printaddr(this); return 0; /* Success */ } SYS_ERR(DIP, LOGL_ERROR, 0, "Could not allocate IP address"); return -1; /* Should never get here. TODO: Bad code */ } int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member) { if (0) (void)ippool_printaddr(this); if (!member->inuse) { SYS_ERR(DIP, LOGL_ERROR, 0, "Address not in use"); return -1; /* Not in use: Should not happen */ } switch (member->inuse) { case 0: /* Not in use: Should not happen */ SYS_ERR(DIP, LOGL_ERROR, 0, "Address not in use"); return -1; case 1: /* Allocated from dynamic address space */ /* Insert into list of unused */ member->prev = this->lastdyn; if (this->lastdyn) { this->lastdyn->next = member; } else { this->firstdyn = member; } this->lastdyn = member; member->inuse = 0; member->peer = NULL; if (0) (void)ippool_printaddr(this); return 0; case 2: /* Allocated from static address space */ if (ippool_hashdel(this, member)) return -1; /* Insert into list of unused */ member->prev = this->laststat; if (this->laststat) { this->laststat->next = member; } else { this->firststat = member; } this->laststat = member; member->inuse = 0; member->addr.s_addr = 0; member->peer = NULL; member->nexthash = NULL; if (0) (void)ippool_printaddr(this); return 0; default: /* Should not happen */ SYS_ERR(DIP, LOGL_ERROR, 0, "Could not free IP address"); return -1; } } #ifndef IPPOOL_NOIP6 extern unsigned long int ippool_hash6(struct in6_addr *addr); extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr); extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr); #endif openggsn-0.92/lib/ippool.h000066400000000000000000000073401262356443100155170ustar00rootroot00000000000000/* * IP address pool functions. * Copyright (C) 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #ifndef _IPPOOL_H #define _IPPOOL_H /* Assuming that the address space is fragmented we need a hash table in order to return the addresses. The list pool should provide for both IPv4 and IPv6 addresses. When initialising a new address pool it should be possible to pass a string of CIDR format networks: "10.0.0.0/24 10.15.0.0/20" would translate to 256 addresses starting at 10.0.0.0 and 1024 addresses starting at 10.15.0.0. The above also applies to IPv6 which can be specified as described in RFC2373. */ #define IPPOOL_NOIP6 #define IPPOOL_NONETWORK 0x01 #define IPPOOL_NOBROADCAST 0x02 #define IPPOOL_NOGATEWAY 0x04 #define IPPOOL_STATSIZE 0x10000 struct ippoolm_t; /* Forward declaration */ struct ippool_t { unsigned int listsize; /* Total number of addresses */ int allowdyn; /* Allow dynamic IP address allocation */ int allowstat; /* Allow static IP address allocation */ struct in_addr stataddr; /* Static address range network address */ struct in_addr statmask; /* Static address range network mask */ struct ippoolm_t *member; /* Listsize array of members */ unsigned int hashsize; /* Size of hash table */ int hashlog; /* Log2 size of hash table */ int hashmask; /* Bitmask for calculating hash */ struct ippoolm_t **hash; /* Hashsize array of pointer to member */ struct ippoolm_t *firstdyn; /* Pointer to first free dynamic member */ struct ippoolm_t *lastdyn; /* Pointer to last free dynamic member */ struct ippoolm_t *firststat; /* Pointer to first free static member */ struct ippoolm_t *laststat; /* Pointer to last free static member */ }; struct ippoolm_t { #ifndef IPPOOL_NOIP6 struct in6_addr addr; /* IP address of this member */ #else struct in_addr addr; /* IP address of this member */ #endif int inuse; /* 0=available; 1= dynamic; 2 = static */ struct ippoolm_t *nexthash; /* Linked list part of hash table */ struct ippoolm_t *prev, *next; /* Linked list of free dynamic or static */ void *peer; /* Pointer to peer protocol handler */ }; /* The above structures require approximately 20+4 = 24 bytes for each address (IPv4). For IPv6 the corresponding value is 32+4 = 36 bytes for each address. */ /* Hash an IP address using code based on Bob Jenkins lookupa */ extern unsigned long int ippool_hash4(struct in_addr *addr); /* Create new address pool */ extern int ippool_new(struct ippool_t **this, char *dyn, char *stat, int allowdyn, int allowstat, int flags); /* Delete existing address pool */ extern int ippool_free(struct ippool_t *this); /* Find an IP address in the pool */ extern int ippool_getip(struct ippool_t *this, struct ippoolm_t **member, struct in_addr *addr); /* Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise check to see if the given address is available */ extern int ippool_newip(struct ippool_t *this, struct ippoolm_t **member, struct in_addr *addr, int statip); /* Return a previously allocated IP address */ extern int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member); /* Get net and mask based on ascii string */ extern int ippool_aton(struct in_addr *addr, struct in_addr *mask, char *pool, int number); #ifndef IPPOOL_NOIP6 extern unsigned long int ippool_hash6(struct in6_addr *addr); extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr); extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr); #endif #endif /* !_IPPOOL_H */ openggsn-0.92/lib/lookup.c000077500000000000000000000050111262356443100155150ustar00rootroot00000000000000/* * Hash lookup function. * Copyright (C) 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /** * lookup() * Generates a 32 bit hash. * Based on public domain code by Bob Jenkins * It should be one of the best hash functions around in terms of both * statistical properties and speed. It is NOT recommended for cryptographic * purposes. **/ unsigned long int lookup(k, length, level) register unsigned char *k; /* the key */ register unsigned long int length; /* the length of the key */ register unsigned long int level; /* the previous hash, or an arbitrary value */ { #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } typedef unsigned long int ub4; /* unsigned 4-byte quantities */ typedef unsigned char ub1; /* unsigned 1-byte quantities */ register unsigned long int a, b, c, len; /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = level; /* the previous hash value */ /*---------------------------------------- handle most of the key */ while (len >= 12) { a += (k[0] + ((ub4) k[1] << 8) + ((ub4) k[2] << 16) + ((ub4) k[3] << 24)); b += (k[4] + ((ub4) k[5] << 8) + ((ub4) k[6] << 16) + ((ub4) k[7] << 24)); c += (k[8] + ((ub4) k[9] << 8) + ((ub4) k[10] << 16) + ((ub4) k[11] << 24)); mix(a, b, c); k += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch (len) { /* all the case statements fall through */ case 11: c += ((ub4) k[10] << 24); case 10: c += ((ub4) k[9] << 16); case 9: c += ((ub4) k[8] << 8); /* the first byte of c is reserved for the length */ case 8: b += ((ub4) k[7] << 24); case 7: b += ((ub4) k[6] << 16); case 6: b += ((ub4) k[5] << 8); case 5: b += k[4]; case 4: a += ((ub4) k[3] << 24); case 3: a += ((ub4) k[2] << 16); case 2: a += ((ub4) k[1] << 8); case 1: a += k[0]; /* case 0: nothing left to add */ } mix(a, b, c); /*-------------------------------------------- report the result */ return c; } openggsn-0.92/lib/lookup.h000077500000000000000000000013501262356443100155240ustar00rootroot00000000000000/* * Hash lookup function. * Copyright (C) 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /** * lookup() * Generates a 32 bit hash. * Based on public domain code by Bob Jenkins * It should be one of the best hash functions around in terms of both * statistical properties and speed. It is NOT recommended for cryptographic * purposes. **/ #ifndef _LOOKUP_H #define _LOOKUP_H unsigned long int lookup(unsigned char *k, unsigned long int length, unsigned long int level); #endif /* !_LOOKUP_H */ openggsn-0.92/lib/syserr.h000066400000000000000000000014041262356443100155370ustar00rootroot00000000000000/* * Syslog functions. * Copyright (C) 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #ifndef _SYSERR_H #define _SYSERR_H #include enum { DIP, DTUN, DGGSN, DSGSN, }; #define SYS_ERR(sub, pri, en, fmt, args...) \ if (en) { \ logp2(sub, pri, __FILE__, __LINE__, 0, \ "errno=%d/%s " fmt "\n", en, strerror(en), \ ##args); \ } else { \ logp2(sub, pri, __FILE__, __LINE__, 0, \ fmt "\n", ##args); \ } extern const struct log_info log_info; #endif /* !_SYSERR_H */ openggsn-0.92/lib/tun.c000066400000000000000000000515431262356443100150220ustar00rootroot00000000000000/* * TUN interface functions. * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * tun.c: Contains all TUN functionality. Is able to handle multiple * tunnels in the same program. Each tunnel is identified by the struct, * which is passed to functions. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) #include #include #include #include #elif defined (__FreeBSD__) #include #include #elif defined (__APPLE__) #include #elif defined (__sun__) #include #include #include #include /*#include "sun_if_tun.h"*/ #else #error "Unknown platform!" #endif #include "tun.h" #include "syserr.h" #if defined(__linux__) int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen) { int len = RTA_LENGTH(dlen); int alen = NLMSG_ALIGN(n->nlmsg_len); struct rtattr *rta = (struct rtattr *)(((void *)n) + alen); if (alen + len > nsize) return -1; rta->rta_len = len; rta->rta_type = type; memcpy(RTA_DATA(rta), d, dlen); n->nlmsg_len = alen + len; return 0; } int tun_gifindex(struct tun_t *this, __u32 * index) { struct ifreq ifr; int fd; memset(&ifr, '\0', sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_dstaddr.sa_family = AF_INET; ifr.ifr_netmask.sa_family = AF_INET; strncpy(ifr.ifr_name, this->devname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); } if (ioctl(fd, SIOCGIFINDEX, &ifr)) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed"); close(fd); return -1; } close(fd); *index = ifr.ifr_ifindex; return 0; } #endif int tun_sifflags(struct tun_t *this, int flags) { struct ifreq ifr; int fd; memset(&ifr, '\0', sizeof(ifr)); ifr.ifr_flags = flags; strncpy(ifr.ifr_name, this->devname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } if (ioctl(fd, SIOCSIFFLAGS, &ifr)) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCSIFFLAGS) failed"); close(fd); return -1; } close(fd); return 0; } /* Currently unused int tun_addroute2(struct tun_t *this, struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask) { struct { struct nlmsghdr n; struct rtmsg r; char buf[TUN_NLBUFSIZE]; } req; struct sockaddr_nl local; int addr_len; int fd; int status; struct sockaddr_nl nladdr; struct iovec iov; struct msghdr msg; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; req.n.nlmsg_type = RTM_NEWROUTE; req.r.rtm_family = AF_INET; req.r.rtm_table = RT_TABLE_MAIN; req.r.rtm_protocol = RTPROT_BOOT; req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_type = RTN_UNICAST; tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4); tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4); if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = 0; if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed"); close(fd); return -1; } addr_len = sizeof(local); if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "getsockname() failed"); close(fd); return -1; } if (addr_len != sizeof(local)) { SYS_ERR(DTUN, LOGL_ERROR, 0, "Wrong address length %d", addr_len); close(fd); return -1; } if (local.nl_family != AF_NETLINK) { SYS_ERR(DTUN, LOGL_ERROR, 0, "Wrong address family %d", local.nl_family); close(fd); return -1; } iov.iov_base = (void*)&req.n; iov.iov_len = req.n.nlmsg_len; msg.msg_name = (void*)&nladdr; msg.msg_namelen = sizeof(nladdr), msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; req.n.nlmsg_seq = 0; req.n.nlmsg_flags |= NLM_F_ACK; status = sendmsg(fd, &msg, 0); * TODO: Error check * close(fd); return 0; } */ int tun_addaddr(struct tun_t *this, struct in_addr *addr, struct in_addr *dstaddr, struct in_addr *netmask) { #if defined(__linux__) struct { struct nlmsghdr n; struct ifaddrmsg i; char buf[TUN_NLBUFSIZE]; } req; struct sockaddr_nl local; socklen_t addr_len; int fd; int status; struct sockaddr_nl nladdr; struct iovec iov; struct msghdr msg; if (!this->addrs) /* Use ioctl for first addr to make ping work */ return tun_setaddr(this, addr, dstaddr, netmask); memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; req.n.nlmsg_type = RTM_NEWADDR; req.i.ifa_family = AF_INET; req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */ req.i.ifa_flags = 0; req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */ if (tun_gifindex(this, &req.i.ifa_index)) { return -1; } tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr)); tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr)); if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; local.nl_groups = 0; if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "bind() failed"); close(fd); return -1; } addr_len = sizeof(local); if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "getsockname() failed"); close(fd); return -1; } if (addr_len != sizeof(local)) { SYS_ERR(DTUN, LOGL_ERROR, 0, "Wrong address length %d", addr_len); close(fd); return -1; } if (local.nl_family != AF_NETLINK) { SYS_ERR(DTUN, LOGL_ERROR, 0, "Wrong address family %d", local.nl_family); close(fd); return -1; } iov.iov_base = (void *)&req.n; iov.iov_len = req.n.nlmsg_len; msg.msg_name = (void *)&nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; req.n.nlmsg_seq = 0; req.n.nlmsg_flags |= NLM_F_ACK; status = sendmsg(fd, &msg, 0); if (status != req.n.nlmsg_len) { SYS_ERR(DTUN, LOGL_ERROR, errno, "sendmsg() failed, returned %d", status); close(fd); return -1; } status = tun_sifflags(this, IFF_UP | IFF_RUNNING); if (status == -1) { close(fd); return -1; } close(fd); this->addrs++; return 0; #elif defined (__FreeBSD__) || defined (__APPLE__) int fd; struct ifaliasreq areq; /* TODO: Is this needed on FreeBSD? */ if (!this->addrs) /* Use ioctl for first addr to make ping work */ return tun_setaddr(this, addr, dstaddr, netmask); /* TODO dstaddr */ memset(&areq, 0, sizeof(areq)); /* Set up interface name */ strncpy(areq.ifra_name, this->devname, IFNAMSIZ); areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */ ((struct sockaddr_in *)&areq.ifra_addr)->sin_family = AF_INET; ((struct sockaddr_in *)&areq.ifra_addr)->sin_len = sizeof(areq.ifra_addr); ((struct sockaddr_in *)&areq.ifra_addr)->sin_addr.s_addr = addr->s_addr; ((struct sockaddr_in *)&areq.ifra_mask)->sin_family = AF_INET; ((struct sockaddr_in *)&areq.ifra_mask)->sin_len = sizeof(areq.ifra_mask); ((struct sockaddr_in *)&areq.ifra_mask)->sin_addr.s_addr = netmask->s_addr; /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */ ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_family = AF_INET; ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_len = sizeof(areq.ifra_broadaddr); ((struct sockaddr_in *)&areq.ifra_broadaddr)->sin_addr.s_addr = dstaddr->s_addr; /* Create a channel to the NET kernel. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } if (ioctl(fd, SIOCAIFADDR, (void *)&areq) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCAIFADDR) failed"); close(fd); return -1; } close(fd); this->addrs++; return 0; #elif defined (__sun__) if (!this->addrs) /* Use ioctl for first addr to make ping work */ return tun_setaddr(this, addr, dstaddr, netmask); SYS_ERR(DTUN, LOGL_ERROR, errno, "Setting multiple addresses not possible on Solaris"); return -1; #else #error "Unknown platform!" #endif } int tun_setaddr(struct tun_t *this, struct in_addr *addr, struct in_addr *dstaddr, struct in_addr *netmask) { struct ifreq ifr; int fd; memset(&ifr, '\0', sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; ifr.ifr_dstaddr.sa_family = AF_INET; #if defined(__linux__) ifr.ifr_netmask.sa_family = AF_INET; #elif defined(__FreeBSD__) || defined (__APPLE__) ((struct sockaddr_in *)&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in); ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_len = sizeof(struct sockaddr_in); #endif strncpy(ifr.ifr_name, this->devname, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */ /* Create a channel to the NET kernel. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } if (addr) { /* Set the interface address */ this->addr.s_addr = addr->s_addr; memcpy(&((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, addr, sizeof(*addr)); if (ioctl(fd, SIOCSIFADDR, (void *)&ifr) < 0) { if (errno != EEXIST) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCSIFADDR) failed"); } else { SYS_ERR(DTUN, LOGL_NOTICE, errno, "ioctl(SIOCSIFADDR): Address already exists"); } close(fd); return -1; } } if (dstaddr) { /* Set the destination address */ this->dstaddr.s_addr = dstaddr->s_addr; memcpy(&((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr, dstaddr, sizeof(*dstaddr)); if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) & ifr) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCSIFDSTADDR) failed"); close(fd); return -1; } } if (netmask) { /* Set the netmask */ this->netmask.s_addr = netmask->s_addr; #if defined(__linux__) memcpy(&((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr, netmask, sizeof(*netmask)); #elif defined(__FreeBSD__) || defined (__APPLE__) ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = netmask->s_addr; #elif defined(__sun__) ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = netmask->s_addr; #else #error "Unknown platform!" #endif if (ioctl(fd, SIOCSIFNETMASK, (void *)&ifr) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCSIFNETMASK) failed"); close(fd); return -1; } } close(fd); this->addrs++; /* On linux the route to the interface is set automatically on FreeBSD we have to do this manually */ /* TODO: How does it work on Solaris? */ tun_sifflags(this, IFF_UP | IFF_RUNNING); #if defined(__FreeBSD__) || defined (__APPLE__) tun_addroute(this, dstaddr, addr, netmask); this->routes = 1; #endif return 0; } int tun_route(struct tun_t *this, struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask, int delete) { /* TODO: Learn how to set routing table on sun */ #if defined(__linux__) struct rtentry r; int fd; memset(&r, '\0', sizeof(r)); r.rt_flags = RTF_UP | RTF_GATEWAY; /* RTF_HOST not set */ /* Create a channel to the NET kernel. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } r.rt_dst.sa_family = AF_INET; r.rt_gateway.sa_family = AF_INET; r.rt_genmask.sa_family = AF_INET; memcpy(&((struct sockaddr_in *)&r.rt_dst)->sin_addr, dst, sizeof(*dst)); memcpy(&((struct sockaddr_in *)&r.rt_gateway)->sin_addr, gateway, sizeof(*gateway)); memcpy(&((struct sockaddr_in *)&r.rt_genmask)->sin_addr, mask, sizeof(*mask)); if (delete) { if (ioctl(fd, SIOCDELRT, (void *)&r) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCDELRT) failed"); close(fd); return -1; } } else { if (ioctl(fd, SIOCADDRT, (void *)&r) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl(SIOCADDRT) failed"); close(fd); return -1; } } close(fd); return 0; #elif defined(__FreeBSD__) || defined (__APPLE__) struct { struct rt_msghdr rt; struct sockaddr_in dst; struct sockaddr_in gate; struct sockaddr_in mask; } req; int fd; struct rt_msghdr *rtm; if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } memset(&req, 0x00, sizeof(req)); rtm = &req.rt; rtm->rtm_msglen = sizeof(req); rtm->rtm_version = RTM_VERSION; if (delete) { rtm->rtm_type = RTM_DELETE; } else { rtm->rtm_type = RTM_ADD; } rtm->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; /* TODO */ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; rtm->rtm_pid = getpid(); rtm->rtm_seq = 0044; /* TODO */ req.dst.sin_family = AF_INET; req.dst.sin_len = sizeof(req.dst); req.mask.sin_family = AF_INET; req.mask.sin_len = sizeof(req.mask); req.gate.sin_family = AF_INET; req.gate.sin_len = sizeof(req.gate); req.dst.sin_addr.s_addr = dst->s_addr; req.mask.sin_addr.s_addr = mask->s_addr; req.gate.sin_addr.s_addr = gateway->s_addr; if (write(fd, rtm, rtm->rtm_msglen) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "write() failed"); close(fd); return -1; } close(fd); return 0; #elif defined(__sun__) SYS_ERR(DTUN, LOGL_NOTICE, errno, "Could not set up routing on Solaris. Please add route manually."); return 0; #else #error "Unknown platform!" #endif } int tun_addroute(struct tun_t *this, struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask) { return tun_route(this, dst, gateway, mask, 0); } int tun_delroute(struct tun_t *this, struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask) { return tun_route(this, dst, gateway, mask, 1); } int tun_new(struct tun_t **tun) { #if defined(__linux__) struct ifreq ifr; #elif defined(__FreeBSD__) || defined (__APPLE__) char devname[IFNAMSIZ + 5]; /* "/dev/" + ifname */ int devnum; struct ifaliasreq areq; int fd; #elif defined(__sun__) int if_fd, ppa = -1; static int ip_fd = 0; int muxid; struct ifreq ifr; #else #error "Unknown platform!" #endif if (!(*tun = calloc(1, sizeof(struct tun_t)))) { SYS_ERR(DTUN, LOGL_ERROR, errno, "calloc() failed"); return EOF; } (*tun)->cb_ind = NULL; (*tun)->addrs = 0; (*tun)->routes = 0; #if defined(__linux__) /* Open the actual tun device */ if (((*tun)->fd = open("/dev/net/tun", O_RDWR)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "open() failed"); return -1; } /* Set device flags. For some weird reason this is also the method used to obtain the network interface name */ memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */ if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed"); close((*tun)->fd); return -1; } strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ); (*tun)->devname[IFNAMSIZ - 1] = 0; ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */ return 0; #elif defined(__FreeBSD__) || defined (__APPLE__) /* Find suitable device */ for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ snprintf(devname, sizeof(devname), "/dev/tun%d", devnum); if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break; if (errno != EBUSY) break; } if ((*tun)->fd < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't find tunnel device"); return -1; } snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum); (*tun)->devname[sizeof((*tun)->devname)] = 0; /* The tun device we found might have "old" IP addresses allocated */ /* We need to delete those. This problem is not present on Linux */ memset(&areq, 0, sizeof(areq)); /* Set up interface name */ strncpy(areq.ifra_name, (*tun)->devname, IFNAMSIZ); areq.ifra_name[IFNAMSIZ - 1] = 0; /* Make sure to terminate */ /* Create a channel to the NET kernel. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "socket() failed"); return -1; } /* Delete any IP addresses until SIOCDIFADDR fails */ while (ioctl(fd, SIOCDIFADDR, (void *)&areq) != -1) ; close(fd); return 0; #elif defined(__sun__) if ((ip_fd = open("/dev/udp", O_RDWR, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't open /dev/udp"); return -1; } if (((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't open /dev/tun"); return -1; } /* Assign a new PPA and get its unit number. */ if ((ppa = ioctl((*tun)->fd, TUNNEWPPA, -1)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't assign new interface"); return -1; } if ((if_fd = open("/dev/tun", O_RDWR, 0)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't open /dev/tun (2)"); return -1; } if (ioctl(if_fd, I_PUSH, "ip") < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't push IP module"); return -1; } /* Assign ppa according to the unit number returned by tun device */ if (ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't set PPA %d", ppa); return -1; } /* Link the two streams */ if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't link TUN device to IP"); return -1; } close(if_fd); snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa); (*tun)->devname[sizeof((*tun)->devname)] = 0; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, (*tun)->devname); ifr.ifr_ip_muxid = muxid; if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) { ioctl(ip_fd, I_PUNLINK, muxid); SYS_ERR(DTUN, LOGL_ERROR, errno, "Can't set multiplexor id"); return -1; } /* if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0) msg (M_ERR, "Set file descriptor to non-blocking failed"); */ return 0; #else #error "Unknown platform!" #endif } int tun_free(struct tun_t *tun) { if (tun->routes) { tun_delroute(tun, &tun->dstaddr, &tun->addr, &tun->netmask); } if (close(tun->fd)) { SYS_ERR(DTUN, LOGL_ERROR, errno, "close() failed"); } /* TODO: For solaris we need to unlink streams */ free(tun); return 0; } int tun_set_cb_ind(struct tun_t *this, int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len)) { this->cb_ind = cb_ind; return 0; } int tun_decaps(struct tun_t *this) { #if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__) unsigned char buffer[PACKET_MAX]; int status; if ((status = read(this->fd, buffer, sizeof(buffer))) <= 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "read() failed"); return -1; } if (this->cb_ind) return this->cb_ind(this, buffer, status); return 0; #elif defined (__sun__) unsigned char buffer[PACKET_MAX]; struct strbuf sbuf; int f = 0; sbuf.maxlen = PACKET_MAX; sbuf.buf = buffer; if (getmsg(this->fd, NULL, &sbuf, &f) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "getmsg() failed"); return -1; } if (this->cb_ind) return this->cb_ind(this, buffer, sbuf.len); return 0; #endif } int tun_encaps(struct tun_t *tun, void *pack, unsigned len) { #if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__) return write(tun->fd, pack, len); #elif defined (__sun__) struct strbuf sbuf; sbuf.len = len; sbuf.buf = pack; return putmsg(tun->fd, NULL, &sbuf, 0); #endif } int tun_runscript(struct tun_t *tun, char *script) { char buf[TUN_SCRIPTSIZE]; char snet[TUN_ADDRSIZE]; char smask[TUN_ADDRSIZE]; int rc; strncpy(snet, inet_ntoa(tun->addr), sizeof(snet)); snet[sizeof(snet) - 1] = 0; strncpy(smask, inet_ntoa(tun->netmask), sizeof(smask)); smask[sizeof(smask) - 1] = 0; /* system("ipup /dev/tun0 192.168.0.10 255.255.255.0"); */ snprintf(buf, sizeof(buf), "%s %s %s %s", script, tun->devname, snet, smask); buf[sizeof(buf) - 1] = 0; rc = system(buf); if (rc == -1) { SYS_ERR(DTUN, LOGL_ERROR, errno, "Error executing command %s", buf); return -1; } return 0; } openggsn-0.92/lib/tun.h000066400000000000000000000041331262356443100150200ustar00rootroot00000000000000/* * TUN interface functions. * Copyright (C) 2002, 2003 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ #ifndef _TUN_H #define _TUN_H #define PACKET_MAX 8196 /* Maximum packet size we receive */ #define TUN_SCRIPTSIZE 256 #define TUN_ADDRSIZE 128 #define TUN_NLBUFSIZE 1024 struct tun_packet_t { unsigned int ver:4; unsigned int ihl:4; unsigned int dscp:6; unsigned int ecn:2; unsigned int length:16; unsigned int id:16; unsigned int flags:3; unsigned int fragment:13; unsigned int ttl:8; unsigned int protocol:8; unsigned int check:16; unsigned int src:32; unsigned int dst:32; }; /* *********************************************************** * Information storage for each tun instance *************************************************************/ struct tun_t { int fd; /* File descriptor to tun interface */ struct in_addr addr; struct in_addr dstaddr; struct in_addr netmask; int addrs; /* Number of allocated IP addresses */ int routes; /* One if we allocated an automatic route */ char devname[IFNAMSIZ]; /* Name of the tun device */ int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len); }; extern int tun_new(struct tun_t **tun); extern int tun_free(struct tun_t *tun); extern int tun_decaps(struct tun_t *this); extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len); extern int tun_addaddr(struct tun_t *this, struct in_addr *addr, struct in_addr *dstaddr, struct in_addr *netmask); extern int tun_setaddr(struct tun_t *this, struct in_addr *our_adr, struct in_addr *his_adr, struct in_addr *net_mask); int tun_addroute(struct tun_t *this, struct in_addr *dst, struct in_addr *gateway, struct in_addr *mask); extern int tun_set_cb_ind(struct tun_t *this, int (*cb_ind) (struct tun_t * tun, void *pack, unsigned len)); extern int tun_runscript(struct tun_t *tun, char *script); #endif /* !_TUN_H */ openggsn-0.92/libgtp.pc.in000066400000000000000000000003221262356443100155010ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: OpenGGSN STP Library Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -lgtp Cflags: -I${includedir}/ openggsn-0.92/openggsn.spec.in000066400000000000000000000035301262356443100163740ustar00rootroot00000000000000Summary: Open Source Gateway GPRS Support Node (GGSN) Name: @PACKAGE@ Version: @VERSION@ Release: 1 URL: http://sourceforge.net/projects/ggsn/ Source0: http://prdownloads.sourceforge.net/ggsn/%{name}-%{version}.tar.gz License: GPL Group: System Environment/Daemons BuildRoot: %{_tmppath}/%{name}-root %description OpenGGSN is a Gateway GPRS Support Node (GGSN). It is used by mobile operators as the interface between the Internet and the rest of the mobile network infrastructure. The project also provides an SGSN emulator suitable for GPRS core network testing. %prep %setup -q %build ./configure --prefix=/usr --enable-static-exec make %install make install prefix=$RPM_BUILD_ROOT/usr strip $RPM_BUILD_ROOT/usr/bin/ggsn strip $RPM_BUILD_ROOT/usr/bin/sgsnemu #Copy ggsn init script in place mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d install -m755 examples/ggsn.init \ $RPM_BUILD_ROOT/etc/rc.d/init.d/ggsn #Copy ggsn.conf in place install -m755 examples/ggsn.conf \ $RPM_BUILD_ROOT/etc/ggsn.conf #Copy gsn_restart file in place mkdir -p $RPM_BUILD_ROOT/var/lib/ggsn echo "0" > $RPM_BUILD_ROOT/var/lib/ggsn/gsn_restart #Clean up unwanted library files rm -rf $RPM_BUILD_ROOT/usr/include/* rm -rf $RPM_BUILD_ROOT/usr/lib/* %clean rm -rf $RPM_BUILD_ROOT make clean %post /sbin/chkconfig --add ggsn %files %defattr(-,root,root) /usr/bin/ggsn /usr/bin/sgsnemu /etc/rc.d/init.d/ggsn %dir /var/lib/ggsn /var/lib/ggsn/gsn_restart %doc AUTHORS ChangeLog COPYING INSTALL NEWS README %doc examples/ggsn.conf %doc examples/sgsnemu.conf %doc examples/ggsn.init %doc examples/firewall %doc /usr/man/man8/ggsn.8.gz %doc /usr/man/man8/sgsnemu.8.gz %config /etc/ggsn.conf #/usr/lib/libgtp.a #/usr/lib/libgtp.la #/usr/lib/libgtp.so #/usr/lib/libgtp.so.0 #/usr/lib/libgtp.so.0.0.0 %changelog * Mon Jun 30 2003 - Initial build. openggsn-0.92/po/000077500000000000000000000000001262356443100137105ustar00rootroot00000000000000openggsn-0.92/po/Makefile.in000066400000000000000000000000001262356443100157430ustar00rootroot00000000000000openggsn-0.92/sgsnemu/000077500000000000000000000000001262356443100147535ustar00rootroot00000000000000openggsn-0.92/sgsnemu/Makefile.am000066400000000000000000000005261262356443100170120ustar00rootroot00000000000000bin_PROGRAMS = sgsnemu AM_LDFLAGS = @EXEC_LDFLAGS@ AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) sgsnemu_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) sgsnemu_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a sgsnemu_SOURCES = sgsnemu.c cmdline.c cmdline.h openggsn-0.92/sgsnemu/cmdline.c000066400000000000000000001562451262356443100165470ustar00rootroot00000000000000/* File autogenerated by gengetopt version 2.17 generated with the following command: gengetopt --conf-parser The developers of gengetopt consider the fixed text that goes in all gengetopt output files to be in the public domain: we make no copyright claims on it. */ /* If we use autoconf. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "getopt.h" #include "cmdline.h" const char *gengetopt_args_info_purpose = ""; const char *gengetopt_args_info_usage = "Usage: " CMDLINE_PARSER_PACKAGE " [OPTIONS]..."; const char *gengetopt_args_info_help[] = { " -h, --help Print help and exit", " -V, --version Print version and exit", " -d, --debug Run in debug mode (default=off)", " -c, --conf=STRING Read configuration file", " --pidfile=STRING Filename of process id file (default=`./sgsnemu.pid')", " --statedir=STRING Directory of nonvolatile data (default=`./')", " --dns=STRING DNS Server to use", " -l, --listen=STRING Local interface", " -r, --remote=STRING Remote host", " --contexts=INT Number of contexts (default=`1')", " --timelimit=INT Exit after timelimit seconds (default=`0')", " --gtpversion=INT GTP version to use (default=`1')", " -a, --apn=STRING Access point name (default=`internet')", " --selmode=INT Selection mode (default=`0x01')", " --rattype=INT Radio Access Technology Type (optional-1to5)", " --userloc=STRING User Location Information (optional-type.MCC.MNC.LAC.CIorSACorRAC)", " --rai=STRING Routing Area Information (optional-MCC.MNC.LAC.RAC)", " --mstz=STRING MS Time Zone (optional- sign.NbQuartersOfAnHour.DSTAdjustment)", " --imeisv=STRING IMEI(SV) International Mobile Equipment Identity (and Software Version) (optional,16 digits)", " -i, --imsi=STRING IMSI (default=`240010123456789')", " --nsapi=INT NSAPI (default=`0')", " -m, --msisdn=STRING Mobile Station ISDN number (default=`46702123456')", " -q, --qos=INT Requested quality of service (default=`0x000b921f')", " --qose1=INT Requested quality of service Extension 1 (example=`0x9396404074f9ffff')", " --qose2=INT Requested quality of service Extension 2 (example=`0x11')", " --qose3=INT Requested quality of service Extension 3 (example=`0x0101')", " --qose4=INT Requested quality of service Extension 4 (example=`0x4040')", " --charging=INT Charging characteristics (default=`0x0800')", " -u, --uid=STRING Login user ID (default=`mig')", " -p, --pwd=STRING Login password (default=`hemmelig')", " --createif Create local network interface (default=off)", " -n, --net=STRING Network address for local interface", " --defaultroute Create default route (default=off)", " --ipup=STRING Script to run after link-up", " --ipdown=STRING Script to run after link-down", " --pinghost=STRING Ping remote host", " --pingrate=INT Number of ping req per second (default=`1')", " --pingsize=INT Number of ping data bytes (default=`56')", " --pingcount=INT Number of ping req to send (default=`0')", " --pingquiet Do not print ping packet info (default=off)", " --norecovery Do not send recovery (default=off)", 0 }; static void clear_given(struct gengetopt_args_info *args_info); static void clear_args(struct gengetopt_args_info *args_info); static int cmdline_parser_internal(int argc, char *const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required, const char *additional_error); struct line_list { char *string_arg; struct line_list *next; }; static struct line_list *cmd_line_list = 0; static struct line_list *cmd_line_list_tmp = 0; static void free_cmd_list(void) { /* free the list of a previous call */ if (cmd_line_list) { while (cmd_line_list) { cmd_line_list_tmp = cmd_line_list; cmd_line_list = cmd_line_list->next; free(cmd_line_list_tmp->string_arg); free(cmd_line_list_tmp); } } } static char *gengetopt_strdup(const char *s); static void clear_given(struct gengetopt_args_info *args_info) { args_info->help_given = 0; args_info->version_given = 0; args_info->debug_given = 0; args_info->conf_given = 0; args_info->pidfile_given = 0; args_info->statedir_given = 0; args_info->dns_given = 0; args_info->listen_given = 0; args_info->remote_given = 0; args_info->contexts_given = 0; args_info->timelimit_given = 0; args_info->gtpversion_given = 0; args_info->apn_given = 0; args_info->selmode_given = 0; args_info->rattype_given = 0; args_info->userloc_given = 0; args_info->rai_given = 0; args_info->mstz_given = 0; args_info->imeisv_given = 0; args_info->imsi_given = 0; args_info->nsapi_given = 0; args_info->msisdn_given = 0; args_info->qos_given = 0; args_info->qose1_given = 0; args_info->qose2_given = 0; args_info->qose3_given = 0; args_info->qose4_given = 0; args_info->charging_given = 0; args_info->uid_given = 0; args_info->pwd_given = 0; args_info->createif_given = 0; args_info->net_given = 0; args_info->defaultroute_given = 0; args_info->ipup_given = 0; args_info->ipdown_given = 0; args_info->pinghost_given = 0; args_info->pingrate_given = 0; args_info->pingsize_given = 0; args_info->pingcount_given = 0; args_info->pingquiet_given = 0; args_info->norecovery_given = 0; } static void clear_args(struct gengetopt_args_info *args_info) { args_info->debug_flag = 0; args_info->conf_arg = NULL; args_info->conf_orig = NULL; args_info->pidfile_arg = gengetopt_strdup("./sgsnemu.pid"); args_info->pidfile_orig = NULL; args_info->statedir_arg = gengetopt_strdup("./"); args_info->statedir_orig = NULL; args_info->dns_arg = NULL; args_info->dns_orig = NULL; args_info->listen_arg = NULL; args_info->listen_orig = NULL; args_info->remote_arg = NULL; args_info->remote_orig = NULL; args_info->contexts_arg = 1; args_info->contexts_orig = NULL; args_info->timelimit_arg = 0; args_info->timelimit_orig = NULL; args_info->gtpversion_arg = 1; args_info->gtpversion_orig = NULL; args_info->apn_arg = gengetopt_strdup("internet"); args_info->apn_orig = NULL; args_info->selmode_arg = 0x01; args_info->selmode_orig = NULL; args_info->rattype_arg = "1"; args_info->rattype_orig = NULL; args_info->userloc_arg = strdup("02509946241207"); args_info->userloc_orig = NULL; args_info->rai_arg = strdup("02509946241207"); args_info->rai_orig = NULL; args_info->mstz_arg = strdup("0"); args_info->mstz_orig = NULL; args_info->imeisv_arg = strdup("2143658709214365"); args_info->imeisv_orig = NULL; args_info->imsi_arg = gengetopt_strdup("240010123456789"); args_info->imsi_orig = NULL; args_info->nsapi_arg = 0; args_info->nsapi_orig = NULL; args_info->msisdn_arg = gengetopt_strdup("46702123456"); args_info->msisdn_orig = NULL; args_info->qos_arg = 0x000b921f; args_info->qos_orig = NULL; args_info->qose1_arg = 0x9396404074f9ffff; args_info->qose1_orig = NULL; args_info->qose2_arg = 0x11; args_info->qose2_orig = NULL; args_info->qose3_arg = 0x0101; args_info->qose3_orig = NULL; args_info->qose4_arg = 0x4040; args_info->qose4_orig = NULL; args_info->charging_arg = 0x0800; args_info->charging_orig = NULL; args_info->uid_arg = gengetopt_strdup("mig"); args_info->uid_orig = NULL; args_info->pwd_arg = gengetopt_strdup("hemmelig"); args_info->pwd_orig = NULL; args_info->createif_flag = 0; args_info->net_arg = NULL; args_info->net_orig = NULL; args_info->defaultroute_flag = 0; args_info->ipup_arg = NULL; args_info->ipup_orig = NULL; args_info->ipdown_arg = NULL; args_info->ipdown_orig = NULL; args_info->pinghost_arg = NULL; args_info->pinghost_orig = NULL; args_info->pingrate_arg = 1; args_info->pingrate_orig = NULL; args_info->pingsize_arg = 56; args_info->pingsize_orig = NULL; args_info->pingcount_arg = 0; args_info->pingcount_orig = NULL; args_info->pingquiet_flag = 0; args_info->norecovery_flag = 0; } static void init_args_info(struct gengetopt_args_info *args_info) { args_info->help_help = gengetopt_args_info_help[0]; args_info->version_help = gengetopt_args_info_help[1]; args_info->debug_help = gengetopt_args_info_help[2]; args_info->conf_help = gengetopt_args_info_help[3]; args_info->pidfile_help = gengetopt_args_info_help[4]; args_info->statedir_help = gengetopt_args_info_help[5]; args_info->dns_help = gengetopt_args_info_help[6]; args_info->listen_help = gengetopt_args_info_help[7]; args_info->remote_help = gengetopt_args_info_help[8]; args_info->contexts_help = gengetopt_args_info_help[9]; args_info->timelimit_help = gengetopt_args_info_help[10]; args_info->gtpversion_help = gengetopt_args_info_help[11]; args_info->apn_help = gengetopt_args_info_help[12]; args_info->selmode_help = gengetopt_args_info_help[13]; args_info->imsi_help = gengetopt_args_info_help[14]; args_info->nsapi_help = gengetopt_args_info_help[15]; args_info->msisdn_help = gengetopt_args_info_help[16]; args_info->qos_help = gengetopt_args_info_help[17]; args_info->charging_help = gengetopt_args_info_help[18]; args_info->uid_help = gengetopt_args_info_help[19]; args_info->pwd_help = gengetopt_args_info_help[20]; args_info->createif_help = gengetopt_args_info_help[21]; args_info->net_help = gengetopt_args_info_help[22]; args_info->defaultroute_help = gengetopt_args_info_help[23]; args_info->ipup_help = gengetopt_args_info_help[24]; args_info->ipdown_help = gengetopt_args_info_help[25]; args_info->pinghost_help = gengetopt_args_info_help[26]; args_info->pingrate_help = gengetopt_args_info_help[27]; args_info->pingsize_help = gengetopt_args_info_help[28]; args_info->pingcount_help = gengetopt_args_info_help[29]; args_info->pingquiet_help = gengetopt_args_info_help[30]; args_info->norecovery_help = gengetopt_args_info_help[31]; } void cmdline_parser_print_version(void) { printf("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION); } void cmdline_parser_print_help(void) { int i = 0; cmdline_parser_print_version(); if (strlen(gengetopt_args_info_purpose) > 0) printf("\n%s\n", gengetopt_args_info_purpose); printf("\n%s\n\n", gengetopt_args_info_usage); while (gengetopt_args_info_help[i]) printf("%s\n", gengetopt_args_info_help[i++]); } void cmdline_parser_init(struct gengetopt_args_info *args_info) { clear_given(args_info); clear_args(args_info); init_args_info(args_info); } static void cmdline_parser_release(struct gengetopt_args_info *args_info) { if (args_info->conf_arg) { free(args_info->conf_arg); /* free previous argument */ args_info->conf_arg = 0; } if (args_info->conf_orig) { free(args_info->conf_orig); /* free previous argument */ args_info->conf_orig = 0; } if (args_info->pidfile_arg) { free(args_info->pidfile_arg); /* free previous argument */ args_info->pidfile_arg = 0; } if (args_info->pidfile_orig) { free(args_info->pidfile_orig); /* free previous argument */ args_info->pidfile_orig = 0; } if (args_info->statedir_arg) { free(args_info->statedir_arg); /* free previous argument */ args_info->statedir_arg = 0; } if (args_info->statedir_orig) { free(args_info->statedir_orig); /* free previous argument */ args_info->statedir_orig = 0; } if (args_info->dns_arg) { free(args_info->dns_arg); /* free previous argument */ args_info->dns_arg = 0; } if (args_info->dns_orig) { free(args_info->dns_orig); /* free previous argument */ args_info->dns_orig = 0; } if (args_info->listen_arg) { free(args_info->listen_arg); /* free previous argument */ args_info->listen_arg = 0; } if (args_info->listen_orig) { free(args_info->listen_orig); /* free previous argument */ args_info->listen_orig = 0; } if (args_info->remote_arg) { free(args_info->remote_arg); /* free previous argument */ args_info->remote_arg = 0; } if (args_info->remote_orig) { free(args_info->remote_orig); /* free previous argument */ args_info->remote_orig = 0; } if (args_info->contexts_orig) { free(args_info->contexts_orig); /* free previous argument */ args_info->contexts_orig = 0; } if (args_info->timelimit_orig) { free(args_info->timelimit_orig); /* free previous argument */ args_info->timelimit_orig = 0; } if (args_info->gtpversion_orig) { free(args_info->gtpversion_orig); /* free previous argument */ args_info->gtpversion_orig = 0; } if (args_info->apn_arg) { free(args_info->apn_arg); /* free previous argument */ args_info->apn_arg = 0; } if (args_info->apn_orig) { free(args_info->apn_orig); /* free previous argument */ args_info->apn_orig = 0; } if (args_info->selmode_orig) { free(args_info->selmode_orig); /* free previous argument */ args_info->selmode_orig = 0; } if (args_info->imsi_arg) { free(args_info->imsi_arg); /* free previous argument */ args_info->imsi_arg = 0; } if (args_info->imsi_orig) { free(args_info->imsi_orig); /* free previous argument */ args_info->imsi_orig = 0; } if (args_info->nsapi_orig) { free(args_info->nsapi_orig); /* free previous argument */ args_info->nsapi_orig = 0; } if (args_info->msisdn_arg) { free(args_info->msisdn_arg); /* free previous argument */ args_info->msisdn_arg = 0; } if (args_info->msisdn_orig) { free(args_info->msisdn_orig); /* free previous argument */ args_info->msisdn_orig = 0; } if (args_info->qos_orig) { free(args_info->qos_orig); /* free previous argument */ args_info->qos_orig = 0; } if (args_info->charging_orig) { free(args_info->charging_orig); /* free previous argument */ args_info->charging_orig = 0; } if (args_info->uid_arg) { free(args_info->uid_arg); /* free previous argument */ args_info->uid_arg = 0; } if (args_info->uid_orig) { free(args_info->uid_orig); /* free previous argument */ args_info->uid_orig = 0; } if (args_info->pwd_arg) { free(args_info->pwd_arg); /* free previous argument */ args_info->pwd_arg = 0; } if (args_info->pwd_orig) { free(args_info->pwd_orig); /* free previous argument */ args_info->pwd_orig = 0; } if (args_info->net_arg) { free(args_info->net_arg); /* free previous argument */ args_info->net_arg = 0; } if (args_info->net_orig) { free(args_info->net_orig); /* free previous argument */ args_info->net_orig = 0; } if (args_info->ipup_arg) { free(args_info->ipup_arg); /* free previous argument */ args_info->ipup_arg = 0; } if (args_info->ipup_orig) { free(args_info->ipup_orig); /* free previous argument */ args_info->ipup_orig = 0; } if (args_info->ipdown_arg) { free(args_info->ipdown_arg); /* free previous argument */ args_info->ipdown_arg = 0; } if (args_info->ipdown_orig) { free(args_info->ipdown_orig); /* free previous argument */ args_info->ipdown_orig = 0; } if (args_info->pinghost_arg) { free(args_info->pinghost_arg); /* free previous argument */ args_info->pinghost_arg = 0; } if (args_info->pinghost_orig) { free(args_info->pinghost_orig); /* free previous argument */ args_info->pinghost_orig = 0; } if (args_info->pingrate_orig) { free(args_info->pingrate_orig); /* free previous argument */ args_info->pingrate_orig = 0; } if (args_info->pingsize_orig) { free(args_info->pingsize_orig); /* free previous argument */ args_info->pingsize_orig = 0; } if (args_info->pingcount_orig) { free(args_info->pingcount_orig); /* free previous argument */ args_info->pingcount_orig = 0; } clear_given(args_info); } int cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) { FILE *outfile; int i = 0; outfile = fopen(filename, "w"); if (!outfile) { fprintf(stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); return EXIT_FAILURE; } if (args_info->help_given) { fprintf(outfile, "%s\n", "help"); } if (args_info->version_given) { fprintf(outfile, "%s\n", "version"); } if (args_info->debug_given) { fprintf(outfile, "%s\n", "debug"); } if (args_info->conf_given) { if (args_info->conf_orig) { fprintf(outfile, "%s=\"%s\"\n", "conf", args_info->conf_orig); } else { fprintf(outfile, "%s\n", "conf"); } } if (args_info->pidfile_given) { if (args_info->pidfile_orig) { fprintf(outfile, "%s=\"%s\"\n", "pidfile", args_info->pidfile_orig); } else { fprintf(outfile, "%s\n", "pidfile"); } } if (args_info->statedir_given) { if (args_info->statedir_orig) { fprintf(outfile, "%s=\"%s\"\n", "statedir", args_info->statedir_orig); } else { fprintf(outfile, "%s\n", "statedir"); } } if (args_info->dns_given) { if (args_info->dns_orig) { fprintf(outfile, "%s=\"%s\"\n", "dns", args_info->dns_orig); } else { fprintf(outfile, "%s\n", "dns"); } } if (args_info->listen_given) { if (args_info->listen_orig) { fprintf(outfile, "%s=\"%s\"\n", "listen", args_info->listen_orig); } else { fprintf(outfile, "%s\n", "listen"); } } if (args_info->remote_given) { if (args_info->remote_orig) { fprintf(outfile, "%s=\"%s\"\n", "remote", args_info->remote_orig); } else { fprintf(outfile, "%s\n", "remote"); } } if (args_info->contexts_given) { if (args_info->contexts_orig) { fprintf(outfile, "%s=\"%s\"\n", "contexts", args_info->contexts_orig); } else { fprintf(outfile, "%s\n", "contexts"); } } if (args_info->timelimit_given) { if (args_info->timelimit_orig) { fprintf(outfile, "%s=\"%s\"\n", "timelimit", args_info->timelimit_orig); } else { fprintf(outfile, "%s\n", "timelimit"); } } if (args_info->gtpversion_given) { if (args_info->gtpversion_orig) { fprintf(outfile, "%s=\"%s\"\n", "gtpversion", args_info->gtpversion_orig); } else { fprintf(outfile, "%s\n", "gtpversion"); } } if (args_info->apn_given) { if (args_info->apn_orig) { fprintf(outfile, "%s=\"%s\"\n", "apn", args_info->apn_orig); } else { fprintf(outfile, "%s\n", "apn"); } } if (args_info->selmode_given) { if (args_info->selmode_orig) { fprintf(outfile, "%s=\"%s\"\n", "selmode", args_info->selmode_orig); } else { fprintf(outfile, "%s\n", "selmode"); } } if (args_info->imsi_given) { if (args_info->imsi_orig) { fprintf(outfile, "%s=\"%s\"\n", "imsi", args_info->imsi_orig); } else { fprintf(outfile, "%s\n", "imsi"); } } if (args_info->nsapi_given) { if (args_info->nsapi_orig) { fprintf(outfile, "%s=\"%s\"\n", "nsapi", args_info->nsapi_orig); } else { fprintf(outfile, "%s\n", "nsapi"); } } if (args_info->msisdn_given) { if (args_info->msisdn_orig) { fprintf(outfile, "%s=\"%s\"\n", "msisdn", args_info->msisdn_orig); } else { fprintf(outfile, "%s\n", "msisdn"); } } if (args_info->qos_given) { if (args_info->qos_orig) { fprintf(outfile, "%s=\"%s\"\n", "qos", args_info->qos_orig); } else { fprintf(outfile, "%s\n", "qos"); } } if (args_info->charging_given) { if (args_info->charging_orig) { fprintf(outfile, "%s=\"%s\"\n", "charging", args_info->charging_orig); } else { fprintf(outfile, "%s\n", "charging"); } } if (args_info->uid_given) { if (args_info->uid_orig) { fprintf(outfile, "%s=\"%s\"\n", "uid", args_info->uid_orig); } else { fprintf(outfile, "%s\n", "uid"); } } if (args_info->pwd_given) { if (args_info->pwd_orig) { fprintf(outfile, "%s=\"%s\"\n", "pwd", args_info->pwd_orig); } else { fprintf(outfile, "%s\n", "pwd"); } } if (args_info->createif_given) { fprintf(outfile, "%s\n", "createif"); } if (args_info->net_given) { if (args_info->net_orig) { fprintf(outfile, "%s=\"%s\"\n", "net", args_info->net_orig); } else { fprintf(outfile, "%s\n", "net"); } } if (args_info->defaultroute_given) { fprintf(outfile, "%s\n", "defaultroute"); } if (args_info->ipup_given) { if (args_info->ipup_orig) { fprintf(outfile, "%s=\"%s\"\n", "ipup", args_info->ipup_orig); } else { fprintf(outfile, "%s\n", "ipup"); } } if (args_info->ipdown_given) { if (args_info->ipdown_orig) { fprintf(outfile, "%s=\"%s\"\n", "ipdown", args_info->ipdown_orig); } else { fprintf(outfile, "%s\n", "ipdown"); } } if (args_info->pinghost_given) { if (args_info->pinghost_orig) { fprintf(outfile, "%s=\"%s\"\n", "pinghost", args_info->pinghost_orig); } else { fprintf(outfile, "%s\n", "pinghost"); } } if (args_info->pingrate_given) { if (args_info->pingrate_orig) { fprintf(outfile, "%s=\"%s\"\n", "pingrate", args_info->pingrate_orig); } else { fprintf(outfile, "%s\n", "pingrate"); } } if (args_info->pingsize_given) { if (args_info->pingsize_orig) { fprintf(outfile, "%s=\"%s\"\n", "pingsize", args_info->pingsize_orig); } else { fprintf(outfile, "%s\n", "pingsize"); } } if (args_info->pingcount_given) { if (args_info->pingcount_orig) { fprintf(outfile, "%s=\"%s\"\n", "pingcount", args_info->pingcount_orig); } else { fprintf(outfile, "%s\n", "pingcount"); } } if (args_info->pingquiet_given) { fprintf(outfile, "%s\n", "pingquiet"); } if (args_info->norecovery_given) { fprintf(outfile, "%s\n", "norecovery"); } fclose(outfile); i = EXIT_SUCCESS; return i; } void cmdline_parser_free(struct gengetopt_args_info *args_info) { cmdline_parser_release(args_info); } /* gengetopt_strdup() */ /* strdup.c replacement of strdup, which is not standard */ char *gengetopt_strdup(const char *s) { char *result = NULL; if (!s) return result; result = (char *)malloc(strlen(s) + 1); if (result == (char *)0) return (char *)0; strcpy(result, s); return result; } int cmdline_parser(int argc, char *const *argv, struct gengetopt_args_info *args_info) { return cmdline_parser2(argc, argv, args_info, 0, 1, 1); } int cmdline_parser2(int argc, char *const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) { int result; result = cmdline_parser_internal(argc, argv, args_info, override, initialize, check_required, NULL); if (result == EXIT_FAILURE) { cmdline_parser_free(args_info); exit(EXIT_FAILURE); } return result; } int cmdline_parser_required(struct gengetopt_args_info *args_info, const char *prog_name) { return EXIT_SUCCESS; } int cmdline_parser_internal(int argc, char *const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required, const char *additional_error) { int c; /* Character of the parsed option. */ int error = 0; struct gengetopt_args_info local_args_info; if (initialize) cmdline_parser_init(args_info); cmdline_parser_init(&local_args_info); optarg = 0; optind = 0; opterr = 1; optopt = '?'; while (1) { int option_index = 0; char *stop_char; static struct option long_options[] = { {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'V'}, {"debug", 0, NULL, 'd'}, {"conf", 1, NULL, 'c'}, {"pidfile", 1, NULL, 0}, {"statedir", 1, NULL, 0}, {"dns", 1, NULL, 0}, {"listen", 1, NULL, 'l'}, {"remote", 1, NULL, 'r'}, {"contexts", 1, NULL, 0}, {"timelimit", 1, NULL, 0}, {"gtpversion", 1, NULL, 0}, {"apn", 1, NULL, 'a'}, {"selmode", 1, NULL, 0}, {"rattype", 1, NULL, 0}, {"userloc", 1, NULL, 0}, {"rai", 1, NULL, 0}, {"mstz", 1, NULL, 0}, {"imeisv", 1, NULL, 0}, {"imsi", 1, NULL, 'i'}, {"nsapi", 1, NULL, 0}, {"msisdn", 1, NULL, 'm'}, {"qos", 1, NULL, 'q'}, {"qose1", 1, NULL, 0}, {"qose2", 1, NULL, 0}, {"qose3", 1, NULL, 0}, {"qose4", 1, NULL, 0}, {"charging", 1, NULL, 0}, {"uid", 1, NULL, 'u'}, {"pwd", 1, NULL, 'p'}, {"createif", 0, NULL, 0}, {"net", 1, NULL, 'n'}, {"defaultroute", 0, NULL, 0}, {"ipup", 1, NULL, 0}, {"ipdown", 1, NULL, 0}, {"pinghost", 1, NULL, 0}, {"pingrate", 1, NULL, 0}, {"pingsize", 1, NULL, 0}, {"pingcount", 1, NULL, 0}, {"pingquiet", 0, NULL, 0}, {"norecovery", 0, NULL, 0}, {NULL, 0, NULL, 0} }; stop_char = 0; c = getopt_long(argc, argv, "hVdc:l:r:a:i:m:q:u:p:n:", long_options, &option_index); if (c == -1) break; /* Exit from `while (1)' loop. */ switch (c) { case 'h': /* Print help and exit. */ cmdline_parser_print_help(); cmdline_parser_free(&local_args_info); exit(EXIT_SUCCESS); case 'V': /* Print version and exit. */ cmdline_parser_print_version(); cmdline_parser_free(&local_args_info); exit(EXIT_SUCCESS); case 'd': /* Run in debug mode. */ if (local_args_info.debug_given) { fprintf(stderr, "%s: `--debug' (`-d') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->debug_given && !override) continue; local_args_info.debug_given = 1; args_info->debug_given = 1; args_info->debug_flag = !(args_info->debug_flag); break; case 'c': /* Read configuration file. */ if (local_args_info.conf_given) { fprintf(stderr, "%s: `--conf' (`-c') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->conf_given && !override) continue; local_args_info.conf_given = 1; args_info->conf_given = 1; if (args_info->conf_arg) free(args_info->conf_arg); /* free previous string */ args_info->conf_arg = gengetopt_strdup(optarg); if (args_info->conf_orig) free(args_info->conf_orig); /* free previous string */ args_info->conf_orig = gengetopt_strdup(optarg); break; case 'l': /* Local interface. */ if (local_args_info.listen_given) { fprintf(stderr, "%s: `--listen' (`-l') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->listen_given && !override) continue; local_args_info.listen_given = 1; args_info->listen_given = 1; if (args_info->listen_arg) free(args_info->listen_arg); /* free previous string */ args_info->listen_arg = gengetopt_strdup(optarg); if (args_info->listen_orig) free(args_info->listen_orig); /* free previous string */ args_info->listen_orig = gengetopt_strdup(optarg); break; case 'r': /* Remote host. */ if (local_args_info.remote_given) { fprintf(stderr, "%s: `--remote' (`-r') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->remote_given && !override) continue; local_args_info.remote_given = 1; args_info->remote_given = 1; if (args_info->remote_arg) free(args_info->remote_arg); /* free previous string */ args_info->remote_arg = gengetopt_strdup(optarg); if (args_info->remote_orig) free(args_info->remote_orig); /* free previous string */ args_info->remote_orig = gengetopt_strdup(optarg); break; case 'a': /* Access point name. */ if (local_args_info.apn_given) { fprintf(stderr, "%s: `--apn' (`-a') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->apn_given && !override) continue; local_args_info.apn_given = 1; args_info->apn_given = 1; if (args_info->apn_arg) free(args_info->apn_arg); /* free previous string */ args_info->apn_arg = gengetopt_strdup(optarg); if (args_info->apn_orig) free(args_info->apn_orig); /* free previous string */ args_info->apn_orig = gengetopt_strdup(optarg); break; case 'i': /* IMSI. */ if (local_args_info.imsi_given) { fprintf(stderr, "%s: `--imsi' (`-i') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->imsi_given && !override) continue; local_args_info.imsi_given = 1; args_info->imsi_given = 1; if (args_info->imsi_arg) free(args_info->imsi_arg); /* free previous string */ args_info->imsi_arg = gengetopt_strdup(optarg); if (args_info->imsi_orig) free(args_info->imsi_orig); /* free previous string */ args_info->imsi_orig = gengetopt_strdup(optarg); break; case 'm': /* Mobile Station ISDN number. */ if (local_args_info.msisdn_given) { fprintf(stderr, "%s: `--msisdn' (`-m') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->msisdn_given && !override) continue; local_args_info.msisdn_given = 1; args_info->msisdn_given = 1; if (args_info->msisdn_arg) free(args_info->msisdn_arg); /* free previous string */ args_info->msisdn_arg = gengetopt_strdup(optarg); if (args_info->msisdn_orig) free(args_info->msisdn_orig); /* free previous string */ args_info->msisdn_orig = gengetopt_strdup(optarg); break; case 'q': /* Requested quality of service. */ if (local_args_info.qos_given) { fprintf(stderr, "%s: `--qos' (`-q') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->qos_given && !override) continue; local_args_info.qos_given = 1; args_info->qos_given = 1; args_info->qos_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->qos_orig) free(args_info->qos_orig); /* free previous string */ args_info->qos_orig = gengetopt_strdup(optarg); break; case 'u': /* Login user ID. */ if (local_args_info.uid_given) { fprintf(stderr, "%s: `--uid' (`-u') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->uid_given && !override) continue; local_args_info.uid_given = 1; args_info->uid_given = 1; if (args_info->uid_arg) free(args_info->uid_arg); /* free previous string */ args_info->uid_arg = gengetopt_strdup(optarg); if (args_info->uid_orig) free(args_info->uid_orig); /* free previous string */ args_info->uid_orig = gengetopt_strdup(optarg); break; case 'p': /* Login password. */ if (local_args_info.pwd_given) { fprintf(stderr, "%s: `--pwd' (`-p') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pwd_given && !override) continue; local_args_info.pwd_given = 1; args_info->pwd_given = 1; if (args_info->pwd_arg) free(args_info->pwd_arg); /* free previous string */ args_info->pwd_arg = gengetopt_strdup(optarg); if (args_info->pwd_orig) free(args_info->pwd_orig); /* free previous string */ args_info->pwd_orig = gengetopt_strdup(optarg); break; case 'n': /* Network address for local interface. */ if (local_args_info.net_given) { fprintf(stderr, "%s: `--net' (`-n') option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->net_given && !override) continue; local_args_info.net_given = 1; args_info->net_given = 1; if (args_info->net_arg) free(args_info->net_arg); /* free previous string */ args_info->net_arg = gengetopt_strdup(optarg); if (args_info->net_orig) free(args_info->net_orig); /* free previous string */ args_info->net_orig = gengetopt_strdup(optarg); break; case 0: /* Long option with no short option */ /* Filename of process id file. */ if (strcmp(long_options[option_index].name, "pidfile") == 0) { if (local_args_info.pidfile_given) { fprintf(stderr, "%s: `--pidfile' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pidfile_given && !override) continue; local_args_info.pidfile_given = 1; args_info->pidfile_given = 1; if (args_info->pidfile_arg) free(args_info->pidfile_arg); /* free previous string */ args_info->pidfile_arg = gengetopt_strdup(optarg); if (args_info->pidfile_orig) free(args_info->pidfile_orig); /* free previous string */ args_info->pidfile_orig = gengetopt_strdup(optarg); } /* Directory of nonvolatile data. */ else if (strcmp (long_options[option_index].name, "statedir") == 0) { if (local_args_info.statedir_given) { fprintf(stderr, "%s: `--statedir' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->statedir_given && !override) continue; local_args_info.statedir_given = 1; args_info->statedir_given = 1; if (args_info->statedir_arg) free(args_info->statedir_arg); /* free previous string */ args_info->statedir_arg = gengetopt_strdup(optarg); if (args_info->statedir_orig) free(args_info->statedir_orig); /* free previous string */ args_info->statedir_orig = gengetopt_strdup(optarg); } /* DNS Server to use. */ else if (strcmp(long_options[option_index].name, "dns") == 0) { if (local_args_info.dns_given) { fprintf(stderr, "%s: `--dns' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->dns_given && !override) continue; local_args_info.dns_given = 1; args_info->dns_given = 1; if (args_info->dns_arg) free(args_info->dns_arg); /* free previous string */ args_info->dns_arg = gengetopt_strdup(optarg); if (args_info->dns_orig) free(args_info->dns_orig); /* free previous string */ args_info->dns_orig = gengetopt_strdup(optarg); } /* Number of contexts. */ else if (strcmp (long_options[option_index].name, "contexts") == 0) { if (local_args_info.contexts_given) { fprintf(stderr, "%s: `--contexts' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->contexts_given && !override) continue; local_args_info.contexts_given = 1; args_info->contexts_given = 1; args_info->contexts_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->contexts_orig) free(args_info->contexts_orig); /* free previous string */ args_info->contexts_orig = gengetopt_strdup(optarg); } /* Exit after timelimit seconds. */ else if (strcmp (long_options[option_index].name, "timelimit") == 0) { if (local_args_info.timelimit_given) { fprintf(stderr, "%s: `--timelimit' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->timelimit_given && !override) continue; local_args_info.timelimit_given = 1; args_info->timelimit_given = 1; args_info->timelimit_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->timelimit_orig) free(args_info->timelimit_orig); /* free previous string */ args_info->timelimit_orig = gengetopt_strdup(optarg); } /* GTP version to use. */ else if (strcmp (long_options[option_index].name, "gtpversion") == 0) { if (local_args_info.gtpversion_given) { fprintf(stderr, "%s: `--gtpversion' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->gtpversion_given && !override) continue; local_args_info.gtpversion_given = 1; args_info->gtpversion_given = 1; args_info->gtpversion_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->gtpversion_orig) free(args_info->gtpversion_orig); /* free previous string */ args_info->gtpversion_orig = gengetopt_strdup(optarg); } /* Selection mode. */ else if (strcmp (long_options[option_index].name, "selmode") == 0) { if (local_args_info.selmode_given) { fprintf(stderr, "%s: `--selmode' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->selmode_given && !override) continue; local_args_info.selmode_given = 1; args_info->selmode_given = 1; args_info->selmode_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->selmode_orig) free(args_info->selmode_orig); /* free previous string */ args_info->selmode_orig = gengetopt_strdup(optarg); } /* QoS Extension 1. */ else if (strcmp (long_options[option_index].name, "qose1") == 0) { if (args_info->qose1_given) { fprintf(stderr, "%s: `--qose1' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->qose1_given = 1; args_info->qose1_arg = strtoull(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->qose1_orig) free(args_info->qose1_orig); /* free previous string */ args_info->qose1_orig = gengetopt_strdup(optarg); break; } /* QoS Extension 2. */ else if (strcmp (long_options[option_index].name, "qose2") == 0) { if (args_info->qose2_given) { fprintf(stderr, "%s: `--qose2' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->qose2_given = 1; args_info->qose2_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->qose2_orig) free(args_info->qose2_orig); /* free previous string */ args_info->qose2_orig = gengetopt_strdup(optarg); break; } /* QoS Extension 3. */ else if (strcmp (long_options[option_index].name, "qose3") == 0) { if (args_info->qose3_given) { fprintf(stderr, "%s: `--qose3' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->qose3_given = 1; args_info->qose3_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->qose3_orig) free(args_info->qose3_orig); /* free previous string */ args_info->qose3_orig = gengetopt_strdup(optarg); break; } /* QoS Extension 4. */ else if (strcmp (long_options[option_index].name, "qose4") == 0) { if (args_info->qose4_given) { fprintf(stderr, "%s: `--qose4' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->qose4_given = 1; args_info->qose4_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->qose4_orig) free(args_info->qose4_orig); /* free previous string */ args_info->qose4_orig = gengetopt_strdup(optarg); break; } /* Radio Access Technology Type. */ else if (strcmp (long_options[option_index].name, "rattype") == 0) { if (args_info->rattype_given) { fprintf(stderr, "%s: `--rattype' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->rattype_given = 1; /* args_info->rattype_arg = strtol (optarg,&stop_char,0); */ args_info->rattype_arg = strdup(optarg); break; } /* User Location Information. */ else if (strcmp (long_options[option_index].name, "userloc") == 0) { if (args_info->userloc_given) { fprintf(stderr, "%s: `--userloc' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->userloc_given = 1; args_info->userloc_arg = strdup(optarg); break; } /* Routing Area Information. */ else if (strcmp(long_options[option_index].name, "rai") == 0) { if (args_info->rai_given) { fprintf(stderr, "%s: `--rai' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->rai_given = 1; args_info->rai_arg = strdup(optarg); break; } /* MS Time Zone */ else if (strcmp(long_options[option_index].name, "mstz") == 0) { if (args_info->mstz_given) { fprintf(stderr, "%s: `--mstz' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->mstz_given = 1; args_info->mstz_arg = strdup(optarg); break; } /* IMEI(SV) */ else if (strcmp (long_options[option_index].name, "imeisv") == 0) { if (args_info->imeisv_given) { fprintf(stderr, "%s: `--imeisv' option given more than once\n", PACKAGE); exit(EXIT_FAILURE); } args_info->imeisv_given = 1; args_info->imeisv_arg = strdup(optarg); break; } /* NSAPI. */ else if (strcmp (long_options[option_index].name, "nsapi") == 0) { if (local_args_info.nsapi_given) { fprintf(stderr, "%s: `--nsapi' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->nsapi_given && !override) continue; local_args_info.nsapi_given = 1; args_info->nsapi_given = 1; args_info->nsapi_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->nsapi_orig) free(args_info->nsapi_orig); /* free previous string */ args_info->nsapi_orig = gengetopt_strdup(optarg); } /* Charging characteristics. */ else if (strcmp (long_options[option_index].name, "charging") == 0) { if (local_args_info.charging_given) { fprintf(stderr, "%s: `--charging' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->charging_given && !override) continue; local_args_info.charging_given = 1; args_info->charging_given = 1; args_info->charging_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->charging_orig) free(args_info->charging_orig); /* free previous string */ args_info->charging_orig = gengetopt_strdup(optarg); } /* Create local network interface. */ else if (strcmp (long_options[option_index].name, "createif") == 0) { if (local_args_info.createif_given) { fprintf(stderr, "%s: `--createif' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->createif_given && !override) continue; local_args_info.createif_given = 1; args_info->createif_given = 1; args_info->createif_flag = !(args_info->createif_flag); } /* Create default route. */ else if (strcmp (long_options[option_index].name, "defaultroute") == 0) { if (local_args_info.defaultroute_given) { fprintf(stderr, "%s: `--defaultroute' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->defaultroute_given && !override) continue; local_args_info.defaultroute_given = 1; args_info->defaultroute_given = 1; args_info->defaultroute_flag = !(args_info->defaultroute_flag); } /* Script to run after link-up. */ else if (strcmp(long_options[option_index].name, "ipup") == 0) { if (local_args_info.ipup_given) { fprintf(stderr, "%s: `--ipup' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->ipup_given && !override) continue; local_args_info.ipup_given = 1; args_info->ipup_given = 1; if (args_info->ipup_arg) free(args_info->ipup_arg); /* free previous string */ args_info->ipup_arg = gengetopt_strdup(optarg); if (args_info->ipup_orig) free(args_info->ipup_orig); /* free previous string */ args_info->ipup_orig = gengetopt_strdup(optarg); } /* Script to run after link-down. */ else if (strcmp (long_options[option_index].name, "ipdown") == 0) { if (local_args_info.ipdown_given) { fprintf(stderr, "%s: `--ipdown' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->ipdown_given && !override) continue; local_args_info.ipdown_given = 1; args_info->ipdown_given = 1; if (args_info->ipdown_arg) free(args_info->ipdown_arg); /* free previous string */ args_info->ipdown_arg = gengetopt_strdup(optarg); if (args_info->ipdown_orig) free(args_info->ipdown_orig); /* free previous string */ args_info->ipdown_orig = gengetopt_strdup(optarg); } /* Ping remote host. */ else if (strcmp (long_options[option_index].name, "pinghost") == 0) { if (local_args_info.pinghost_given) { fprintf(stderr, "%s: `--pinghost' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pinghost_given && !override) continue; local_args_info.pinghost_given = 1; args_info->pinghost_given = 1; if (args_info->pinghost_arg) free(args_info->pinghost_arg); /* free previous string */ args_info->pinghost_arg = gengetopt_strdup(optarg); if (args_info->pinghost_orig) free(args_info->pinghost_orig); /* free previous string */ args_info->pinghost_orig = gengetopt_strdup(optarg); } /* Number of ping req per second. */ else if (strcmp (long_options[option_index].name, "pingrate") == 0) { if (local_args_info.pingrate_given) { fprintf(stderr, "%s: `--pingrate' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pingrate_given && !override) continue; local_args_info.pingrate_given = 1; args_info->pingrate_given = 1; args_info->pingrate_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->pingrate_orig) free(args_info->pingrate_orig); /* free previous string */ args_info->pingrate_orig = gengetopt_strdup(optarg); } /* Number of ping data bytes. */ else if (strcmp (long_options[option_index].name, "pingsize") == 0) { if (local_args_info.pingsize_given) { fprintf(stderr, "%s: `--pingsize' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pingsize_given && !override) continue; local_args_info.pingsize_given = 1; args_info->pingsize_given = 1; args_info->pingsize_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->pingsize_orig) free(args_info->pingsize_orig); /* free previous string */ args_info->pingsize_orig = gengetopt_strdup(optarg); } /* Number of ping req to send. */ else if (strcmp (long_options[option_index].name, "pingcount") == 0) { if (local_args_info.pingcount_given) { fprintf(stderr, "%s: `--pingcount' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pingcount_given && !override) continue; local_args_info.pingcount_given = 1; args_info->pingcount_given = 1; args_info->pingcount_arg = strtol(optarg, &stop_char, 0); if (!(stop_char && *stop_char == '\0')) { fprintf(stderr, "%s: invalid numeric value: %s\n", argv[0], optarg); goto failure; } if (args_info->pingcount_orig) free(args_info->pingcount_orig); /* free previous string */ args_info->pingcount_orig = gengetopt_strdup(optarg); } /* Do not print ping packet info. */ else if (strcmp (long_options[option_index].name, "pingquiet") == 0) { if (local_args_info.pingquiet_given) { fprintf(stderr, "%s: `--pingquiet' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->pingquiet_given && !override) continue; local_args_info.pingquiet_given = 1; args_info->pingquiet_given = 1; args_info->pingquiet_flag = !(args_info->pingquiet_flag); } /* Do not send recovery. */ else if (strcmp (long_options[option_index].name, "norecovery") == 0) { if (local_args_info.norecovery_given) { fprintf(stderr, "%s: `--norecovery' option given more than once%s\n", argv[0], (additional_error ? additional_error : "")); goto failure; } if (args_info->norecovery_given && !override) continue; local_args_info.norecovery_given = 1; args_info->norecovery_given = 1; args_info->norecovery_flag = !(args_info->norecovery_flag); } break; case '?': /* Invalid option. */ /* `getopt_long' already printed an error message. */ goto failure; default: /* bug: option not considered. */ fprintf(stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); abort(); } /* switch */ } /* while */ cmdline_parser_release(&local_args_info); if (error) return (EXIT_FAILURE); return 0; failure: cmdline_parser_release(&local_args_info); return (EXIT_FAILURE); } #ifndef CONFIG_FILE_LINE_SIZE #define CONFIG_FILE_LINE_SIZE 2048 #endif #define ADDITIONAL_ERROR " in configuration file " #define CONFIG_FILE_LINE_BUFFER_SIZE (CONFIG_FILE_LINE_SIZE+3) /* 3 is for "--" and "=" */ char my_argv[CONFIG_FILE_LINE_BUFFER_SIZE + 1]; int cmdline_parser_configfile(char *const filename, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) { FILE *file; char linebuf[CONFIG_FILE_LINE_SIZE]; int line_num = 0; int i, result, equal; char *fopt, *farg; char *str_index; size_t len, next_token; char delimiter; int my_argc = 0; char **my_argv_arg; char *additional_error; /* store the program name */ cmd_line_list_tmp = (struct line_list *)malloc(sizeof(struct line_list)); cmd_line_list_tmp->next = cmd_line_list; cmd_line_list = cmd_line_list_tmp; cmd_line_list->string_arg = gengetopt_strdup(CMDLINE_PARSER_PACKAGE); if ((file = fopen(filename, "r")) == NULL) { fprintf(stderr, "%s: Error opening configuration file '%s'\n", CMDLINE_PARSER_PACKAGE, filename); result = EXIT_FAILURE; goto conf_failure; } while ((fgets(linebuf, CONFIG_FILE_LINE_SIZE, file)) != NULL) { ++line_num; my_argv[0] = '\0'; len = strlen(linebuf); if (len > (CONFIG_FILE_LINE_BUFFER_SIZE - 1)) { fprintf(stderr, "%s:%s:%d: Line too long in configuration file\n", CMDLINE_PARSER_PACKAGE, filename, line_num); result = EXIT_FAILURE; goto conf_failure; } /* find first non-whitespace character in the line */ next_token = strspn(linebuf, " \t\r\n"); str_index = linebuf + next_token; if (str_index[0] == '\0' || str_index[0] == '#') continue; /* empty line or comment line is skipped */ fopt = str_index; /* truncate fopt at the end of the first non-valid character */ next_token = strcspn(fopt, " \t\r\n="); if (fopt[next_token] == '\0') { /* the line is over */ farg = NULL; equal = 0; goto noarg; } /* remember if equal sign is present */ equal = (fopt[next_token] == '='); fopt[next_token++] = '\0'; /* advance pointers to the next token after the end of fopt */ next_token += strspn(fopt + next_token, " \t\r\n"); /* check for the presence of equal sign, and if so, skip it */ if (!equal) if ((equal = (fopt[next_token] == '='))) { next_token++; next_token += strspn(fopt + next_token, " \t\r\n"); } str_index += next_token; /* find argument */ farg = str_index; if (farg[0] == '\"' || farg[0] == '\'') { /* quoted argument */ str_index = strchr(++farg, str_index[0]); /* skip opening quote */ if (!str_index) { fprintf (stderr, "%s:%s:%d: unterminated string in configuration file\n", CMDLINE_PARSER_PACKAGE, filename, line_num); result = EXIT_FAILURE; goto conf_failure; } } else { /* read up the remaining part up to a delimiter */ next_token = strcspn(farg, " \t\r\n#\'\""); str_index += next_token; } /* truncate farg at the delimiter and store it for further check */ delimiter = *str_index, *str_index++ = '\0'; /* everything but comment is illegal at the end of line */ if (delimiter != '\0' && delimiter != '#') { str_index += strspn(str_index, " \t\r\n"); if (*str_index != '\0' && *str_index != '#') { fprintf (stderr, "%s:%s:%d: malformed string in configuration file\n", CMDLINE_PARSER_PACKAGE, filename, line_num); result = EXIT_FAILURE; goto conf_failure; } } noarg: ++my_argc; len = strlen(fopt); strcat(my_argv, len > 1 ? "--" : "-"); strcat(my_argv, fopt); if (len > 1 && ((farg && *farg) || equal)) strcat(my_argv, "="); if (farg && *farg) strcat(my_argv, farg); cmd_line_list_tmp = (struct line_list *)malloc(sizeof(struct line_list)); cmd_line_list_tmp->next = cmd_line_list; cmd_line_list = cmd_line_list_tmp; cmd_line_list->string_arg = gengetopt_strdup(my_argv); } /* while */ ++my_argc; /* for program name */ my_argv_arg = (char **)malloc((my_argc + 1) * sizeof(char *)); cmd_line_list_tmp = cmd_line_list; for (i = my_argc - 1; i >= 0; --i) { my_argv_arg[i] = cmd_line_list_tmp->string_arg; cmd_line_list_tmp = cmd_line_list_tmp->next; } my_argv_arg[my_argc] = 0; additional_error = (char *)malloc(strlen(filename) + strlen(ADDITIONAL_ERROR) + 1); strcpy(additional_error, ADDITIONAL_ERROR); strcat(additional_error, filename); result = cmdline_parser_internal(my_argc, my_argv_arg, args_info, override, initialize, check_required, additional_error); free(additional_error); free(my_argv_arg); conf_failure: if (file) fclose(file); free_cmd_list(); if (result == EXIT_FAILURE) { cmdline_parser_free(args_info); exit(EXIT_FAILURE); } return result; } openggsn-0.92/sgsnemu/cmdline.ggo000066400000000000000000000052021262356443100170630ustar00rootroot00000000000000# OpenGGSN - Gateway GPRS Support Node # Copyright (C) 2002, 2003, 2004 Mondru AB. # # The contents of this file may be used under the terms of the GNU # General Public License Version 2, provided that the above copyright # notice and this permission notice is included in all copies or # substantial portions of the software. # # Use "gengetopt --conf-parser < cmdline.ggo" # to generate cmdline.c and cmdline.h option "debug" d "Run in debug mode" flag off option "conf" c "Read configuration file" string no option "pidfile" - "Filename of process id file" string default="./sgsnemu.pid" no option "statedir" - "Directory of nonvolatile data" string default="./" no option "dns" - "DNS Server to use" string no option "listen" l "Local interface" string no option "remote" r "Remote host" string no option "contexts" - "Number of contexts" int default="1" no option "timelimit" - "Exit after timelimit seconds" int default="0" no option "gtpversion" - "GTP version to use" int default="1" no option "apn" a "Access point name" string default="internet" no option "selmode" - "Selection mode" int default="0x01" no option "imsi" i "IMSI" string default="240010123456789" no option "nsapi" - "NSAPI" int default="0" no option "msisdn" m "Mobile Station ISDN number" string default="46702123456" no option "qos" q "Requested quality of service" int default="0x0b921f" no option "charging" - "Charging characteristics" int default="0x0800" no option "uid" u "Login user ID" string default="mig" no option "pwd" p "Login password" string default="hemmelig" no option "createif" - "Create local network interface" flag off option "net" n "Network address for local interface" string no option "defaultroute" - "Create default route" flag off option "ipup" - "Script to run after link-up" string no option "ipdown" - "Script to run after link-down" string no option "pinghost" - "Ping remote host" string no option "pingrate" - "Number of ping req per second" unsigned int default="1" no option "pingsize" - "Number of ping data bytes" unsigned int default="56" no option "pingcount" - "Number of ping req to send" unsigned int default="0" no option "pingquiet" - "Do not print ping packet info" flag off openggsn-0.92/sgsnemu/cmdline.h000066400000000000000000000261701262356443100165450ustar00rootroot00000000000000/* cmdline.h */ /* File autogenerated by gengetopt version 2.17 */ #ifndef CMDLINE_H #define CMDLINE_H /* If we use autoconf. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifndef CMDLINE_PARSER_PACKAGE #define CMDLINE_PARSER_PACKAGE PACKAGE #endif #ifndef CMDLINE_PARSER_VERSION #define CMDLINE_PARSER_VERSION VERSION #endif struct gengetopt_args_info { const char *help_help; /* Print help and exit help description. */ const char *version_help; /* Print version and exit help description. */ int debug_flag; /* Run in debug mode (default=off). */ const char *debug_help; /* Run in debug mode help description. */ char *conf_arg; /* Read configuration file. */ char *conf_orig; /* Read configuration file original value given at command line. */ const char *conf_help; /* Read configuration file help description. */ char *pidfile_arg; /* Filename of process id file (default='./sgsnemu.pid'). */ char *pidfile_orig; /* Filename of process id file original value given at command line. */ const char *pidfile_help; /* Filename of process id file help description. */ char *statedir_arg; /* Directory of nonvolatile data (default='./'). */ char *statedir_orig; /* Directory of nonvolatile data original value given at command line. */ const char *statedir_help; /* Directory of nonvolatile data help description. */ char *dns_arg; /* DNS Server to use. */ char *dns_orig; /* DNS Server to use original value given at command line. */ const char *dns_help; /* DNS Server to use help description. */ char *listen_arg; /* Local interface. */ char *listen_orig; /* Local interface original value given at command line. */ const char *listen_help; /* Local interface help description. */ char *remote_arg; /* Remote host. */ char *remote_orig; /* Remote host original value given at command line. */ const char *remote_help; /* Remote host help description. */ int contexts_arg; /* Number of contexts (default='1'). */ char *contexts_orig; /* Number of contexts original value given at command line. */ const char *contexts_help; /* Number of contexts help description. */ int timelimit_arg; /* Exit after timelimit seconds (default='0'). */ char *timelimit_orig; /* Exit after timelimit seconds original value given at command line. */ const char *timelimit_help; /* Exit after timelimit seconds help description. */ int gtpversion_arg; /* GTP version to use (default='1'). */ char *gtpversion_orig; /* GTP version to use original value given at command line. */ const char *gtpversion_help; /* GTP version to use help description. */ char *apn_arg; /* Access point name (default='internet'). */ char *apn_orig; /* Access point name original value given at command line. */ const char *apn_help; /* Access point name help description. */ int selmode_arg; /* Selection mode (default='0x01'). */ char *selmode_orig; /* Selection mode original value given at command line. */ const char *selmode_help; /* Selection mode help description. */ char *rattype_arg; /* Radio Access Technology Type (optional). */ char *rattype_orig; char *rattype_help; char *userloc_arg; /* User Location Information (optional). */ char *userloc_orig; char *userloc_help; char *rai_arg; /* Routing Area Information (optional). */ char *rai_orig; char *rai_help; char *mstz_arg; /* MS Time Zone (optional). */ char *mstz_orig; char *mstz_help; char *imeisv_arg; /* IMEI(SV) (optional). */ char *imeisv_orig; char *imeisv_help; char *imsi_arg; /* IMSI (default='240010123456789'). */ char *imsi_orig; /* IMSI original value given at command line. */ const char *imsi_help; /* IMSI help description. */ int nsapi_arg; /* NSAPI (default='0'). */ char *nsapi_orig; /* NSAPI original value given at command line. */ const char *nsapi_help; /* NSAPI help description. */ char *msisdn_arg; /* Mobile Station ISDN number (default='46702123456'). */ char *msisdn_orig; /* Mobile Station ISDN number original value given at command line. */ const char *msisdn_help; /* Mobile Station ISDN number help description. */ int qos_arg; /* Requested quality of service (default='0x0b921f'). */ char *qos_orig; /* Requested quality of service original value given at command line. */ const char *qos_help; /* Requested quality of service help description. */ unsigned long long int qose1_arg; /* Requested quality of service Extension 1 */ char *qose1_orig; /* Requested quality of service Extension 1 original value given at command line. */ int qose2_arg; /* Requested quality of service Extension 2 */ char *qose2_orig; /* Requested quality of service Extension 2 original value given at command line. */ int qose3_arg; /* Requested quality of service Extension 3 */ char *qose3_orig; /* Requested quality of service Extension 3 original value given at command line. */ int qose4_arg; /* Requested quality of service Extension 4 */ char *qose4_orig; /* Requested quality of service Extension 4 original value given at command line. */ int charging_arg; /* Charging characteristics (default='0x0800'). */ char *charging_orig; /* Charging characteristics original value given at command line. */ const char *charging_help; /* Charging characteristics help description. */ char *uid_arg; /* Login user ID (default='mig'). */ char *uid_orig; /* Login user ID original value given at command line. */ const char *uid_help; /* Login user ID help description. */ char *pwd_arg; /* Login password (default='hemmelig'). */ char *pwd_orig; /* Login password original value given at command line. */ const char *pwd_help; /* Login password help description. */ int createif_flag; /* Create local network interface (default=off). */ const char *createif_help; /* Create local network interface help description. */ char *net_arg; /* Network address for local interface. */ char *net_orig; /* Network address for local interface original value given at command line. */ const char *net_help; /* Network address for local interface help description. */ int defaultroute_flag; /* Create default route (default=off). */ const char *defaultroute_help; /* Create default route help description. */ char *ipup_arg; /* Script to run after link-up. */ char *ipup_orig; /* Script to run after link-up original value given at command line. */ const char *ipup_help; /* Script to run after link-up help description. */ char *ipdown_arg; /* Script to run after link-down. */ char *ipdown_orig; /* Script to run after link-down original value given at command line. */ const char *ipdown_help; /* Script to run after link-down help description. */ char *pinghost_arg; /* Ping remote host. */ char *pinghost_orig; /* Ping remote host original value given at command line. */ const char *pinghost_help; /* Ping remote host help description. */ int pingrate_arg; /* Number of ping req per second (default='1'). */ char *pingrate_orig; /* Number of ping req per second original value given at command line. */ const char *pingrate_help; /* Number of ping req per second help description. */ int pingsize_arg; /* Number of ping data bytes (default='56'). */ char *pingsize_orig; /* Number of ping data bytes original value given at command line. */ const char *pingsize_help; /* Number of ping data bytes help description. */ int pingcount_arg; /* Number of ping req to send (default='0'). */ char *pingcount_orig; /* Number of ping req to send original value given at command line. */ const char *pingcount_help; /* Number of ping req to send help description. */ int pingquiet_flag; /* Do not print ping packet info (default=off). */ const char *pingquiet_help; /* Do not print ping packet info help description. */ int norecovery_flag; /* Do not print ping packet info (default=off). */ const char *norecovery_help; /* Do not print ping packet info help description. */ int help_given; /* Whether help was given. */ int version_given; /* Whether version was given. */ int debug_given; /* Whether debug was given. */ int conf_given; /* Whether conf was given. */ int pidfile_given; /* Whether pidfile was given. */ int statedir_given; /* Whether statedir was given. */ int dns_given; /* Whether dns was given. */ int listen_given; /* Whether listen was given. */ int remote_given; /* Whether remote was given. */ int contexts_given; /* Whether contexts was given. */ int timelimit_given; /* Whether timelimit was given. */ int gtpversion_given; /* Whether gtpversion was given. */ int apn_given; /* Whether apn was given. */ int selmode_given; /* Whether selmode was given. */ int rattype_given; /* Whether rattype was given. */ int userloc_given; /* Whether userloc was given. */ int rai_given; /* Whether RAI was given. */ int mstz_given; /* Whether mstz was given. */ int imeisv_given; /* Whether imeisv was given. */ int imsi_given; /* Whether imsi was given. */ int nsapi_given; /* Whether nsapi was given. */ int msisdn_given; /* Whether msisdn was given. */ int qos_given; /* Whether qos was given. */ int qose1_given; /* Whether qos Extension 1 was given. */ int qose2_given; /* Whether qos Extension 2 was given. */ int qose3_given; /* Whether qos Extension 3 was given. */ int qose4_given; /* Whether qos Extension 4 was given. */ int charging_given; /* Whether charging was given. */ int uid_given; /* Whether uid was given. */ int pwd_given; /* Whether pwd was given. */ int createif_given; /* Whether createif was given. */ int net_given; /* Whether net was given. */ int defaultroute_given; /* Whether defaultroute was given. */ int ipup_given; /* Whether ipup was given. */ int ipdown_given; /* Whether ipdown was given. */ int pinghost_given; /* Whether pinghost was given. */ int pingrate_given; /* Whether pingrate was given. */ int pingsize_given; /* Whether pingsize was given. */ int pingcount_given; /* Whether pingcount was given. */ int pingquiet_given; /* Whether pingquiet was given. */ int norecovery_given; /* Whether norecovery was given. */ }; extern const char *gengetopt_args_info_purpose; extern const char *gengetopt_args_info_usage; extern const char *gengetopt_args_info_help[]; int cmdline_parser(int argc, char *const *argv, struct gengetopt_args_info *args_info); int cmdline_parser2(int argc, char *const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required); int cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info); void cmdline_parser_print_help(void); void cmdline_parser_print_version(void); void cmdline_parser_init(struct gengetopt_args_info *args_info); void cmdline_parser_free(struct gengetopt_args_info *args_info); int cmdline_parser_configfile(char *const filename, struct gengetopt_args_info *args_info, int override, int initialize, int check_required); int cmdline_parser_required(struct gengetopt_args_info *args_info, const char *prog_name); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* CMDLINE_H */ openggsn-0.92/sgsnemu/sgsnemu.c000066400000000000000000001326361262356443100166130ustar00rootroot00000000000000/* * OpenGGSN - Gateway GPRS Support Node * Copyright (C) 2002, 2003, 2004 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * */ /* * sgsnemu.c * */ #ifdef __linux__ #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "../lib/tun.h" #include "../lib/ippool.h" #include "../lib/syserr.h" #include "../gtp/pdp.h" #include "../gtp/gtp.h" #include "cmdline.h" #define IPADDRLEN 256 /* Character length of addresses */ #define MAXCONTEXTS 1024 /* Max number of allowed contexts */ /* HASH tables for IP address allocation */ struct iphash_t { uint8_t inuse; /* 0=free. 1=used by somebody */ struct iphash_t *ipnext; struct pdp_t *pdp; struct in_addr addr; }; struct iphash_t iparr[MAXCONTEXTS]; struct iphash_t *iphash[MAXCONTEXTS]; /* State variable used for ping */ /* 0: Idle */ /* 1: Wait_connect */ /* 2: Connected */ /* 3: Done */ /* 4: Wait_disconnect */ /* 5: Disconnected */ int state = 0; struct gsn_t *gsn = NULL; /* GSN instance */ struct tun_t *tun = NULL; /* TUN instance */ int maxfd = 0; /* For select() */ int echoversion = 1; /* First try this version */ /* Struct with local versions of gengetopt options */ struct { int debug; /* Print debug messages */ int createif; /* Create local network interface */ struct in_addr netaddr, destaddr, net, mask; /* Network interface */ char *ipup, *ipdown; /* Filename of scripts */ int defaultroute; /* Set up default route */ struct in_addr pinghost; /* Remote ping host */ int pingrate; int pingsize; int pingcount; int pingquiet; struct in_addr listen; struct in_addr remote; struct in_addr dns; int contexts; /* Number of contexts to create */ int timelimit; /* Number of seconds to be connected */ char *statedir; uint64_t imsi; uint8_t nsapi; int gtpversion; struct ul255_t pco; struct ul255_t qos; uint16_t cch; struct ul255_t apn; uint8_t selmode; struct ul255_t rattype; int rattype_given; struct ul255_t userloc; int userloc_given; struct ul255_t rai; int rai_given; struct ul255_t mstz; int mstz_given; struct ul255_t imeisv; int imeisv_given; struct ul16_t msisdn; int norecovery_given; } options; /* Definitions to use for PING. Most of the ping code was derived from */ /* the original ping program by Mike Muuss */ /* IP header and ICMP echo header */ #define CREATEPING_MAX 2048 #define CREATEPING_IP 20 #define CREATEPING_ICMP 8 struct ip_ping { uint8_t ipver; /* Type and header length */ uint8_t tos; /* Type of Service */ uint16_t length; /* Total length */ uint16_t fragid; /* Identifier */ uint16_t offset; /* Flags and fragment offset */ uint8_t ttl; /* Time to live */ uint8_t protocol; /* Protocol */ uint16_t ipcheck; /* Header checksum */ uint32_t src; /* Source address */ uint32_t dst; /* Destination */ uint8_t type; /* Type and header length */ uint8_t code; /* Code */ uint16_t checksum; /* Header checksum */ uint16_t ident; /* Identifier */ uint16_t seq; /* Sequence number */ uint8_t data[CREATEPING_MAX]; /* Data */ } __attribute__ ((packed)); /* Statistical values for ping */ int nreceived = 0; int ntreceived = 0; int ntransmitted = 0; int tmin = 999999999; int tmax = 0; int tsum = 0; int pingseq = 0; /* Ping sequence counter */ struct timeval firstping; int ipset(struct iphash_t *ipaddr, struct in_addr *addr) { int hash = ippool_hash4(addr) % MAXCONTEXTS; struct iphash_t *h; struct iphash_t *prev = NULL; ipaddr->ipnext = NULL; ipaddr->addr.s_addr = addr->s_addr; for (h = iphash[hash]; h; h = h->ipnext) prev = h; if (!prev) iphash[hash] = ipaddr; else prev->ipnext = ipaddr; return 0; } int ipdel(struct iphash_t *ipaddr) { int hash = ippool_hash4(&ipaddr->addr) % MAXCONTEXTS; struct iphash_t *h; struct iphash_t *prev = NULL; for (h = iphash[hash]; h; h = h->ipnext) { if (h == ipaddr) { if (!prev) iphash[hash] = h->ipnext; else prev->ipnext = h->ipnext; return 0; } prev = h; } return EOF; /* End of linked list and not found */ } int ipget(struct iphash_t **ipaddr, struct in_addr *addr) { int hash = ippool_hash4(addr) % MAXCONTEXTS; struct iphash_t *h; for (h = iphash[hash]; h; h = h->ipnext) { if ((h->addr.s_addr == addr->s_addr)) { *ipaddr = h; return 0; } } return EOF; /* End of linked list and not found */ } /* Used to write process ID to file. Assume someone else will delete */ void log_pid(char *pidfile) { FILE *file; mode_t oldmask; oldmask = umask(022); file = fopen(pidfile, "w"); umask(oldmask); if (!file) return; fprintf(file, "%d\n", (int)getpid()); fclose(file); } int process_options(int argc, char **argv) { /* gengeopt declarations */ struct gengetopt_args_info args_info; struct hostent *host; unsigned int n; uint16_t i; uint8_t a; uint8_t b; char *tmp; char *pch; char *type; char *mcc; char *mnc; char *tok, *apn; char *lac; int lac_d; char *rest; char *userloc_el[] = { "TYPE", "MCC", "MNC", "LAC", "REST" }; char *rai_el[] = { "MCC", "MNC", "LAC", "RAC" }; char *mstz_el[] = { "SIGN", "QUARTERS", "DST" }; int sign; int nbquarters; int DST; if (cmdline_parser(argc, argv, &args_info) != 0) return -1; if (args_info.debug_flag) { if (args_info.remote_arg) printf("remote: %s\n", args_info.remote_arg); if (args_info.listen_arg) printf("listen: %s\n", args_info.listen_arg); if (args_info.conf_arg) printf("conf: %s\n", args_info.conf_arg); printf("debug: %d\n", args_info.debug_flag); if (args_info.imsi_arg) printf("imsi: %s\n", args_info.imsi_arg); printf("qos: %#08x\n", args_info.qos_arg); printf("qose1: %#0.16llx\n", args_info.qose1_arg); printf("qose2: %#04x\n", args_info.qose2_arg); printf("qose3: %#06x\n", args_info.qose3_arg); printf("qose4: %#06x\n", args_info.qose4_arg); printf("charging: %#04x\n", args_info.charging_arg); if (args_info.apn_arg) printf("apn: %s\n", args_info.apn_arg); if (args_info.msisdn_arg) printf("msisdn: %s\n", args_info.msisdn_arg); if (args_info.uid_arg) printf("uid: %s\n", args_info.uid_arg); if (args_info.pwd_arg) printf("pwd: %s\n", args_info.pwd_arg); if (args_info.pidfile_arg) printf("pidfile: %s\n", args_info.pidfile_arg); if (args_info.statedir_arg) printf("statedir: %s\n", args_info.statedir_arg); if (args_info.dns_arg) printf("dns: %s\n", args_info.dns_arg); printf("contexts: %d\n", args_info.contexts_arg); printf("timelimit: %d\n", args_info.timelimit_arg); printf("createif: %d\n", args_info.createif_flag); if (args_info.ipup_arg) printf("ipup: %s\n", args_info.ipup_arg); if (args_info.ipdown_arg) printf("ipdown: %s\n", args_info.ipdown_arg); printf("defaultroute: %d\n", args_info.defaultroute_flag); if (args_info.pinghost_arg) printf("pinghost: %s\n", args_info.pinghost_arg); printf("pingrate: %d\n", args_info.pingrate_arg); printf("pingsize: %d\n", args_info.pingsize_arg); printf("pingcount: %d\n", args_info.pingcount_arg); printf("pingquiet: %d\n", args_info.pingquiet_flag); printf("norecovery: %d\n", args_info.norecovery_flag); } /* Try out our new parser */ if (args_info.conf_arg) { if (cmdline_parser_configfile (args_info.conf_arg, &args_info, 0, 0, 0) != 0) return -1; if (args_info.debug_flag) { printf("cmdline_parser_configfile\n"); if (args_info.remote_arg) printf("remote: %s\n", args_info.remote_arg); if (args_info.listen_arg) printf("listen: %s\n", args_info.listen_arg); if (args_info.conf_arg) printf("conf: %s\n", args_info.conf_arg); printf("debug: %d\n", args_info.debug_flag); if (args_info.imsi_arg) printf("imsi: %s\n", args_info.imsi_arg); printf("qos: %#08x\n", args_info.qos_arg); printf("qose1: %#0.16llx\n", args_info.qose1_arg); printf("qose2: %#04x\n", args_info.qose2_arg); printf("qose3: %#06x\n", args_info.qose3_arg); printf("qose4: %#06x\n", args_info.qose4_arg); printf("charging: %#04x\n", args_info.charging_arg); if (args_info.apn_arg) printf("apn: %s\n", args_info.apn_arg); if (args_info.msisdn_arg) printf("msisdn: %s\n", args_info.msisdn_arg); if (args_info.uid_arg) printf("uid: %s\n", args_info.uid_arg); if (args_info.pwd_arg) printf("pwd: %s\n", args_info.pwd_arg); if (args_info.pidfile_arg) printf("pidfile: %s\n", args_info.pidfile_arg); if (args_info.statedir_arg) printf("statedir: %s\n", args_info.statedir_arg); if (args_info.dns_arg) printf("dns: %s\n", args_info.dns_arg); printf("contexts: %d\n", args_info.contexts_arg); printf("timelimit: %d\n", args_info.timelimit_arg); printf("createif: %d\n", args_info.createif_flag); if (args_info.ipup_arg) printf("ipup: %s\n", args_info.ipup_arg); if (args_info.ipdown_arg) printf("ipdown: %s\n", args_info.ipdown_arg); printf("defaultroute: %d\n", args_info.defaultroute_flag); if (args_info.pinghost_arg) printf("pinghost: %s\n", args_info.pinghost_arg); printf("pingrate: %d\n", args_info.pingrate_arg); printf("pingsize: %d\n", args_info.pingsize_arg); printf("pingcount: %d\n", args_info.pingcount_arg); printf("pingquiet: %d\n", args_info.pingquiet_flag); printf("norecovery: %d\n", args_info.norecovery_flag); } } /* Handle each option */ /* foreground */ /* If fg flag not given run as a daemon */ /* Do not allow sgsnemu to run as deamon if (!args_info.fg_flag) { closelog(); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); freopen("/dev/null", "r", stdin); daemon(0, 0); openlog(PACKAGE, LOG_PID, LOG_DAEMON); } */ /* debug */ options.debug = args_info.debug_flag; /* pidfile */ /* This has to be done after we have our final pid */ if (args_info.pidfile_arg) { log_pid(args_info.pidfile_arg); } /* dns */ /* If no dns option is given use system default */ /* Do hostname lookup to translate hostname to IP address */ printf("\n"); if (args_info.dns_arg) { if (!(host = gethostbyname(args_info.dns_arg))) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Invalid DNS address: %s!", args_info.dns_arg); return -1; } else { memcpy(&options.dns.s_addr, host->h_addr, host->h_length); _res.nscount = 1; _res.nsaddr_list[0].sin_addr = options.dns; printf("Using DNS server: %s (%s)\n", args_info.dns_arg, inet_ntoa(options.dns)); } } else { options.dns.s_addr = 0; printf("Using default DNS server\n"); } /* listen */ /* If no listen option is specified listen to any local port */ /* Do hostname lookup to translate hostname to IP address */ if (args_info.listen_arg) { if (!(host = gethostbyname(args_info.listen_arg))) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Invalid listening address: %s!", args_info.listen_arg); return -1; } else { memcpy(&options.listen.s_addr, host->h_addr, host->h_length); printf("Local IP address is: %s (%s)\n", args_info.listen_arg, inet_ntoa(options.listen)); } } else { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Listening address must be specified: %s!", args_info.listen_arg); return -1; } /* remote */ /* If no remote option is specified terminate */ /* Do hostname lookup to translate hostname to IP address */ if (args_info.remote_arg) { if (!(host = gethostbyname(args_info.remote_arg))) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Invalid remote address: %s!", args_info.remote_arg); return -1; } else { memcpy(&options.remote.s_addr, host->h_addr, host->h_length); printf("Remote IP address is: %s (%s)\n", args_info.remote_arg, inet_ntoa(options.remote)); } } else { SYS_ERR(DSGSN, LOGL_ERROR, 0, "No remote address given!"); return -1; } /* imsi */ if (strlen(args_info.imsi_arg) != 15) { printf("Invalid IMSI\n"); return -1; } options.imsi = 0xf000000000000000ull; options.imsi |= ((uint64_t) (args_info.imsi_arg[0] - 48)); options.imsi |= ((uint64_t) (args_info.imsi_arg[1] - 48)) << 4; options.imsi |= ((uint64_t) (args_info.imsi_arg[2] - 48)) << 8; options.imsi |= ((uint64_t) (args_info.imsi_arg[3] - 48)) << 12; options.imsi |= ((uint64_t) (args_info.imsi_arg[4] - 48)) << 16; options.imsi |= ((uint64_t) (args_info.imsi_arg[5] - 48)) << 20; options.imsi |= ((uint64_t) (args_info.imsi_arg[6] - 48)) << 24; options.imsi |= ((uint64_t) (args_info.imsi_arg[7] - 48)) << 28; options.imsi |= ((uint64_t) (args_info.imsi_arg[8] - 48)) << 32; options.imsi |= ((uint64_t) (args_info.imsi_arg[9] - 48)) << 36; options.imsi |= ((uint64_t) (args_info.imsi_arg[10] - 48)) << 40; options.imsi |= ((uint64_t) (args_info.imsi_arg[11] - 48)) << 44; options.imsi |= ((uint64_t) (args_info.imsi_arg[12] - 48)) << 48; options.imsi |= ((uint64_t) (args_info.imsi_arg[13] - 48)) << 52; options.imsi |= ((uint64_t) (args_info.imsi_arg[14] - 48)) << 56; printf("IMSI is: %s (%#08llx)\n", args_info.imsi_arg, options.imsi); /* nsapi */ if ((args_info.nsapi_arg > 15) || (args_info.nsapi_arg < 0)) { printf("Invalid NSAPI\n"); return -1; } options.nsapi = args_info.nsapi_arg; printf("Using NSAPI: %d\n", args_info.nsapi_arg); /* qos */ options.qos.l = 4; options.qos.v[3] = (args_info.qos_arg) & 0xff; options.qos.v[2] = ((args_info.qos_arg) >> 8) & 0xff; options.qos.v[1] = ((args_info.qos_arg) >> 16) & 0xff; options.qos.v[0] = ((args_info.qos_arg) >> 24) & 0xff; /* Extensions according to 3GPP TS 24.008 */ if (args_info.qose1_given == 1) { options.qos.l = 12; options.qos.v[11] = (args_info.qose1_arg) & 0xff; options.qos.v[10] = ((args_info.qose1_arg) >> 8) & 0xff; options.qos.v[9] = ((args_info.qose1_arg) >> 16) & 0xff; options.qos.v[8] = ((args_info.qose1_arg) >> 24) & 0xff; options.qos.v[7] = ((args_info.qose1_arg) >> 32) & 0xff; options.qos.v[6] = ((args_info.qose1_arg) >> 40) & 0xff; options.qos.v[5] = ((args_info.qose1_arg) >> 48) & 0xff; options.qos.v[4] = ((args_info.qose1_arg) >> 56) & 0xff; if (args_info.qose2_given == 1) { options.qos.l = 13; options.qos.v[12] = (args_info.qose2_arg) & 0xff; if (args_info.qose3_given == 1) { options.qos.l = 15; options.qos.v[14] = (args_info.qose3_arg) & 0xff; options.qos.v[13] = ((args_info.qose3_arg) >> 8) & 0xff; if (args_info.qose4_given == 1) { options.qos.l = 17; options.qos.v[16] = (args_info.qose4_arg) & 0xff; options.qos.v[15] = ((args_info.qose4_arg) >> 8) & 0xff; } } } } /* charging */ options.cch = args_info.charging_arg; /* contexts */ if (args_info.contexts_arg > MAXCONTEXTS) { printf("Contexts has to be less than %d\n", MAXCONTEXTS); return -1; } options.contexts = args_info.contexts_arg; /* Timelimit */ options.timelimit = args_info.timelimit_arg; /* gtpversion */ if ((args_info.gtpversion_arg > 1) || (args_info.gtpversion_arg < 0)) { printf("Invalid GTP version\n"); return -1; } options.gtpversion = args_info.gtpversion_arg; printf("Using GTP version: %d\n", args_info.gtpversion_arg); /* apn */ if (strlen(args_info.apn_arg) > (sizeof(options.apn.v) - 1)) { printf("Invalid APN\n"); return -1; } options.apn.l = strlen(args_info.apn_arg) + 1; apn = (char *)options.apn.v; for (tok = strtok(args_info.apn_arg, "."); tok != NULL; tok = strtok(NULL, ".")) { size_t len = strlen(tok); *apn++ = (char)len; strncpy(apn, tok, len); apn += len; } printf("Using APN: %s\n", args_info.apn_arg); /* selmode */ options.selmode = args_info.selmode_arg; printf("Using selection mode: %d\n", args_info.selmode_arg); /* rattype */ if (args_info.rattype_given == 1) { options.rattype_given = 1; options.rattype.l = strlen(args_info.rattype_arg); options.rattype.v[0] = atoi(args_info.rattype_arg); printf("Using RAT Type: %s\n", args_info.rattype_arg); } /* userloc */ if (args_info.userloc_given == 1) { printf("Using User Location Information: %s\n", args_info.userloc_arg); tmp = args_info.userloc_arg; n = 0; pch = strtok(tmp, "."); while (pch != NULL) { userloc_el[n] = pch; pch = strtok(NULL, "."); n++; } options.userloc_given = 1; options.userloc.l = 8; /* 3GPP Geographic Location Type t0 / t1 / t2 */ type = userloc_el[0]; printf("->type : %c\n", type[0]); if ((strlen(type) != 1) || (!isdigit(type[0]))) { printf("Invalid type \n"); return -1; } /* options.userloc.v[0] = 0x00 */ options.userloc.v[0] = type[0] - 48; /* MCC */ mcc = userloc_el[1]; printf("->mcc : %s\n", mcc); if (strlen(mcc) != 3) { printf("Invalid MCC lenght\n"); return -1; } /* MNC */ mnc = userloc_el[2]; printf("->mnc : %s\n", mnc); /* octet 5 - MCC Digit 2 - MCC Digit 1 */ /* options.userloc.v[1] = 0x52 */ a = (uint8_t) (mcc[0] - 48); b = (uint8_t) (mcc[1] - 48); options.userloc.v[1] = 16 * b + a; /* octet 6 - MNC Digit 3 - MCC Digit 3 */ /* options.userloc.v[2] = 0xf0 */ a = (uint8_t) (mcc[2] - 48); if ((strlen(mnc) > 3) || (strlen(mnc) < 2)) { printf("Invalid MNC lenght\n"); return -1; } if (strlen(mnc) == 2) { b = 15; } if (strlen(mnc) == 3) { b = (uint8_t) (mnc[2] - 48); } options.userloc.v[2] = 16 * b + a; /* octet 7 - MNC Digit 2 - MNC Digit 1 */ /* options.userloc.v[3] = 0x99 */ a = (uint8_t) (mnc[0] - 48); b = (uint8_t) (mnc[1] - 48); options.userloc.v[3] = 16 * b + a; /* LAC */ lac = userloc_el[3]; /*options.userloc.v[4] = 0x12 ; */ /*options.userloc.v[5] = 0x10 ; */ printf("->LAC: %s\n", lac); lac_d = atoi(lac); if (lac_d > 65535 || lac_d < 1) { printf("Invalid LAC\n"); return -1; } i = lac_d >> 8; options.userloc.v[4] = i; /* octet 8 - LAC */ options.userloc.v[5] = lac_d; /* octet 9 - LAC */ /* CI/SAC/RAC */ rest = userloc_el[4]; printf("->CI/SAC/RAC : %s\n", rest); lac_d = atoi(rest); if (lac_d > 65535 || lac_d < 1) { printf("Invalid CI/SAC/RAC\n"); return -1; } /*options.userloc.v[6] = 0x04 ; */ /*options.userloc.v[7] = 0xb7 ; */ i = lac_d >> 8; options.userloc.v[6] = i; /* octet 10 - t0,CI / t1,SAC / t2,RAC */ options.userloc.v[7] = lac_d; /* octet 11 - t0,CI / t1,SAC / t2,RAC */ } /* RAI */ if (args_info.rai_given == 1) { printf("Using RAI: %s\n", args_info.rai_arg); tmp = args_info.rai_arg; n = 0; pch = strtok(tmp, "."); while (pch != NULL) { rai_el[n] = pch; pch = strtok(NULL, "."); n++; } options.rai_given = 1; options.rai.l = 6; /* MCC */ mcc = rai_el[0]; printf("->mcc : %s\n", mcc); if (strlen(mcc) != 3) { printf("Invalid MCC lenght\n"); return -1; } /* MNC */ mnc = rai_el[1]; printf("->mnc : %s\n", mnc); a = (uint8_t) (mcc[0] - 48); b = (uint8_t) (mcc[1] - 48); options.rai.v[0] = 16 * b + a; /* octet 3 - MNC Digit 3 - MCC Digit 3 */ a = (uint8_t) (mcc[2] - 48); if ((strlen(mnc) > 3) || (strlen(mnc) < 2)) { printf("Invalid MNC lenght\n"); return -1; } if (strlen(mnc) == 2) { b = 15; } if (strlen(mnc) == 3) { b = (uint8_t) (mnc[2] - 48); } options.rai.v[1] = 16 * b + a; /* octet 4 - MNC Digit 2 - MNC Digit 1 */ a = (uint8_t) (mnc[0] - 48); b = (uint8_t) (mnc[1] - 48); options.rai.v[2] = 16 * b + a; /* LAC */ lac = rai_el[2]; printf("->LAC: %s\n", lac); lac_d = atoi(lac); if (lac_d > 65535 || lac_d < 1) { printf("Invalid LAC\n"); return -1; } i = lac_d >> 8; options.rai.v[3] = i; /* octet 5 - LAC */ options.rai.v[4] = lac_d; /* octet 6 - LAC */ /* RAC */ rest = rai_el[3]; printf("->RAC : %s\n", rest); lac_d = atoi(rest); if (lac_d > 255 || lac_d < 1) { printf("Invalid RAC\n"); return -1; } options.rai.v[5] = lac_d; /* octet 7 - RAC */ } /* mstz */ if (args_info.mstz_given == 1) { options.mstz_given = 1; options.mstz.l = 2; printf("Using MS Time Zone: %s\n", args_info.mstz_arg); tmp = args_info.mstz_arg; n = 0; pch = strtok(tmp, "."); while (pch != NULL) { mstz_el[n] = pch; pch = strtok(NULL, "."); n++; } /* sign */ sign = atoi(mstz_el[0]); printf("->Sign (0=+ / 1=-): %d\n", sign); if (sign != 0 && sign != 1) { printf("Invalid Sign \n"); return -1; } /* nbquarters */ nbquarters = atoi(mstz_el[1]); printf("->Number of Quarters of an Hour : %d\n", nbquarters); if (nbquarters < 0 || nbquarters > 79) { printf("Invalid Number of Quarters \n"); return -1; } /* DST */ DST = atoi(mstz_el[2]); printf("->Daylight Saving Time Adjustment : %d\n", DST); if (DST < 0 || DST > 3) { printf("Invalid DST Adjustment \n"); return -1; } /* 12345678 bits 123 = unit of # of quarters of an hour bits 678 = # of quarters of an hour / 10 bit 5 = sign */ i = nbquarters % 10; i = i << 4; i = i + nbquarters / 10 + 8 * sign; /* options.mstz.v[0] = 0x69 ; */ /* options.mstz.v[1] = 0x01 ; */ options.mstz.v[0] = i; options.mstz.v[1] = DST; n = (i & 0x08) ? '-' : '+'; printf ("->Human Readable MS Time Zone : GMT %c %d hours %d minutes\n", n, nbquarters / 4, nbquarters % 4 * 15); } /* imeisv */ if (args_info.imeisv_given == 1) { options.imeisv_given = 1; if (strlen(args_info.imeisv_arg) != 16) { printf("Invalid IMEI(SV)\n"); return -1; } options.imeisv.l = 8; for (n = 0; n < 8; n++) { a = (uint8_t) (args_info.imeisv_arg[2 * n] - 48); b = (uint8_t) (args_info.imeisv_arg[2 * n + 1] - 48); options.imeisv.v[n] = 16 * b + a; } printf("Using IMEI(SV): %s\n", args_info.imeisv_arg); } /* msisdn */ if (strlen(args_info.msisdn_arg) > (sizeof(options.msisdn.v) - 1)) { printf("Invalid MSISDN\n"); return -1; } options.msisdn.l = 1; options.msisdn.v[0] = 0x91; /* International format */ for (n = 0; n < strlen(args_info.msisdn_arg); n++) { if ((n % 2) == 0) { options.msisdn.v[((int)n / 2) + 1] = args_info.msisdn_arg[n] - 48 + 0xf0; options.msisdn.l += 1; } else { options.msisdn.v[((int)n / 2) + 1] = (options.msisdn.v[((int)n / 2) + 1] & 0x0f) + (args_info.msisdn_arg[n] - 48) * 16; } } printf("Using MSISDN: %s\n", args_info.msisdn_arg); /* UID and PWD */ /* Might need to also insert stuff like DNS etc. */ if ((strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 10) > (sizeof(options.pco.v) - 1)) { printf("invalid UID and PWD\n"); return -1; } options.pco.l = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 10; options.pco.v[0] = 0x80; /* PPP */ options.pco.v[1] = 0xc0; /* PAP */ options.pco.v[2] = 0x23; options.pco.v[3] = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 6; options.pco.v[4] = 0x01; /* Authenticate request */ options.pco.v[5] = 0x01; options.pco.v[6] = 0x00; /* MSB of length */ options.pco.v[7] = strlen(args_info.uid_arg) + strlen(args_info.pwd_arg) + 6; options.pco.v[8] = strlen(args_info.uid_arg); memcpy(&options.pco.v[9], args_info.uid_arg, strlen(args_info.uid_arg)); options.pco.v[9 + strlen(args_info.uid_arg)] = strlen(args_info.pwd_arg); memcpy(&options.pco.v[10 + strlen(args_info.uid_arg)], args_info.pwd_arg, strlen(args_info.pwd_arg)); /* createif */ options.createif = args_info.createif_flag; /* net */ /* Store net as in_addr net and mask */ if (args_info.net_arg) { if (ippool_aton (&options.net, &options.mask, args_info.net_arg, 0)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Invalid network address: %s!", args_info.net_arg); exit(1); } #if defined (__sun__) options.netaddr.s_addr = htonl(ntohl(options.net.s_addr) + 1); options.destaddr.s_addr = htonl(ntohl(options.net.s_addr) + 1); #else options.netaddr.s_addr = options.net.s_addr; options.destaddr.s_addr = options.net.s_addr; #endif } else { options.net.s_addr = 0; options.mask.s_addr = 0; options.netaddr.s_addr = 0; options.destaddr.s_addr = 0; } /* ipup */ options.ipup = args_info.ipup_arg; /* ipdown */ options.ipdown = args_info.ipdown_arg; /* statedir */ options.statedir = args_info.statedir_arg; /* defaultroute */ options.defaultroute = args_info.defaultroute_flag; /* pinghost */ /* Store ping host as in_addr */ if (args_info.pinghost_arg) { if (!(host = gethostbyname(args_info.pinghost_arg))) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Invalid ping host: %s!", args_info.pinghost_arg); return -1; } else { memcpy(&options.pinghost.s_addr, host->h_addr, host->h_length); printf("Using ping host: %s (%s)\n", args_info.pinghost_arg, inet_ntoa(options.pinghost)); } } /* Other ping parameters */ options.pingrate = args_info.pingrate_arg; options.pingsize = args_info.pingsize_arg; options.pingcount = args_info.pingcount_arg; options.pingquiet = args_info.pingquiet_flag; /* norecovery */ options.norecovery_given = args_info.norecovery_flag; return 0; } int encaps_printf(struct pdp_t *pdp, void *pack, unsigned len) { unsigned int i; printf("The packet looks like this:\n"); for (i = 0; i < len; i++) { printf("%02x ", (unsigned char)*(char *)(pack + i)); if (!((i + 1) % 16)) printf("\n"); }; printf("\n"); return 0; } char *print_ipprot(int t) { switch (t) { case 1: return "ICMP"; case 6: return "TCP"; case 17: return "UDP"; default: return "Unknown"; }; } char *print_icmptype(int t) { static char *ttab[] = { "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable", "Source Quench", "Redirect", "ICMP 6", "ICMP 7", "Echo", "ICMP 9", "ICMP 10", "Time Exceeded", "Parameter Problem", "Timestamp", "Timestamp Reply", "Info Request", "Info Reply" }; if (t < 0 || t > 16) return ("OUT-OF-RANGE"); return (ttab[t]); } int msisdn_add(struct ul16_t *src, struct ul16_t *dst, int add) { unsigned int n; uint64_t i64 = 0; uint8_t msa[sizeof(i64) * 3]; /* Allocate 3 digits per octet (0..255) */ unsigned int msalen = 0; /* Convert to uint64_t from ul16_t format (most significant digit first) */ /* ul16_t format always starts with 0x91 to indicate international format */ /* In ul16_t format 0x0f/0xf0 indicates that digit is not used */ for (n = 0; n < src->l; n++) { if ((src->v[n] & 0x0f) != 0x0f) { i64 *= 10; i64 += src->v[n] & 0x0f; } if ((src->v[n] & 0xf0) != 0xf0) { i64 *= 10; i64 += (src->v[n] & 0xf0) >> 4; } } i64 += add; /* Generate array with least significant digit in first octet */ while (i64) { msa[msalen++] = i64 % 10; i64 = i64 / 10; } /* Convert back to ul16_t format */ for (n = 0; n < msalen; n++) { if ((n % 2) == 0) { dst->v[((int)n / 2)] = msa[msalen - n - 1] + 0xf0; dst->l += 1; } else { dst->v[((int)n / 2)] = (dst->v[((int)n / 2)] & 0x0f) + msa[msalen - n - 1] * 16; } } return 0; } int imsi_add(uint64_t src, uint64_t * dst, int add) { /* TODO: big endian / small endian ??? */ uint64_t i64 = 0; /* Convert from uint64_t bcd to uint64_t integer format */ /* The resulting integer format is multiplied by 10 */ while (src) { if ((src & 0x0f) != 0x0f) { i64 *= 10; i64 += (src & 0x0f); } if ((src & 0xf0) != 0xf0) { i64 *= 10; i64 += (src & 0xf0) >> 4; } src = src >> 8; } i64 += add * 10; *dst = 0; while (i64) { *dst = *dst << 4; *dst += (i64 % 10); i64 = i64 / 10; } *dst |= 0xf000000000000000ull; return 0; } /* Calculate time left until we have to send off next ping packet */ int ping_timeout(struct timeval *tp) { struct timezone tz; struct timeval tv; int diff; if ((options.pinghost.s_addr) && (2 == state) && ((pingseq < options.pingcount) || (options.pingcount == 0))) { gettimeofday(&tv, &tz); diff = 1000000 / options.pingrate * pingseq - 1000000 * (tv.tv_sec - firstping.tv_sec) - (tv.tv_usec - firstping.tv_usec); /* Microseconds safe up to 500 sec */ tp->tv_sec = 0; if (diff > 0) tp->tv_usec = diff; else { /* For some reason we get packet loss if set to zero */ tp->tv_usec = 100000 / options.pingrate; /* 10 times pingrate */ tp->tv_usec = 0; } } return 0; } /* Print out statistics when at the end of ping sequence */ int ping_finish() { struct timezone tz; struct timeval tv; int elapsed; gettimeofday(&tv, &tz); elapsed = 1000000 * (tv.tv_sec - firstping.tv_sec) + (tv.tv_usec - firstping.tv_usec); /* Microseconds */ printf("\n"); printf("\n----%s PING Statistics----\n", inet_ntoa(options.pinghost)); printf("%d packets transmitted in %.3f seconds, ", ntransmitted, elapsed / 1000000.0); printf("%d packets received, ", nreceived); if (ntransmitted) { if (nreceived > ntransmitted) printf("-- somebody's printing up packets!"); else printf("%d%% packet loss", (int)(((ntransmitted - nreceived) * 100) / ntransmitted)); } printf("\n"); if (options.debug) printf("%d packets received in total\n", ntreceived); if (nreceived && tsum) printf("round-trip (ms) min/avg/max = %.3f/%.3f/%.3f\n\n", tmin / 1000.0, tsum / 1000.0 / nreceived, tmax / 1000.0); printf("%d packets transmitted \n", ntreceived); ntransmitted = 0; return 0; } /* Handle a received ping packet. Print out line and update statistics. */ int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len) { struct timezone tz; struct timeval tv; struct timeval *tp; struct ip_ping *pingpack = pack; struct in_addr src; int triptime; src.s_addr = pingpack->src; gettimeofday(&tv, &tz); if (options.debug) printf("%d.%6d ", (int)tv.tv_sec, (int)tv.tv_usec); if (len < CREATEPING_IP + CREATEPING_ICMP) { printf("packet too short (%d bytes) from %s\n", len, inet_ntoa(src)); return 0; } ntreceived++; if (pingpack->protocol != 1) { if (!options.pingquiet) printf("%d bytes from %s: ip_protocol=%d (%s)\n", len, inet_ntoa(src), pingpack->protocol, print_ipprot(pingpack->protocol)); return 0; } if (pingpack->type != 0) { if (!options.pingquiet) printf ("%d bytes from %s: icmp_type=%d (%s) icmp_code=%d\n", len, inet_ntoa(src), pingpack->type, print_icmptype(pingpack->type), pingpack->code); return 0; } nreceived++; if (!options.pingquiet) printf("%d bytes from %s: icmp_seq=%d", len, inet_ntoa(src), ntohs(pingpack->seq)); if (len >= sizeof(struct timeval) + CREATEPING_IP + CREATEPING_ICMP) { gettimeofday(&tv, &tz); tp = (struct timeval *)pingpack->data; if ((tv.tv_usec -= tp->tv_usec) < 0) { tv.tv_sec--; tv.tv_usec += 1000000; } tv.tv_sec -= tp->tv_sec; triptime = tv.tv_sec * 1000000 + (tv.tv_usec); tsum += triptime; if (triptime < tmin) tmin = triptime; if (triptime > tmax) tmax = triptime; if (!options.pingquiet) printf(" time=%.3f ms\n", triptime / 1000.0); } else if (!options.pingquiet) printf("\n"); return 0; } /* Create a new ping packet and send it off to peer. */ int create_ping(void *gsn, struct pdp_t *pdp, struct in_addr *dst, int seq, unsigned int datasize) { struct ip_ping pack; uint16_t *p = (uint16_t *) & pack; uint8_t *p8 = (uint8_t *) & pack; struct in_addr src; unsigned int n; long int sum = 0; int count = 0; struct timezone tz; struct timeval *tp = (struct timeval *)&p8[CREATEPING_IP + CREATEPING_ICMP]; if (datasize > CREATEPING_MAX) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Ping size to large: %d!", datasize); return -1; } memcpy(&src, &(pdp->eua.v[2]), 4); /* Copy a 4 byte address */ pack.ipver = 0x45; pack.tos = 0x00; pack.length = htons(CREATEPING_IP + CREATEPING_ICMP + datasize); pack.fragid = 0x0000; pack.offset = 0x0040; pack.ttl = 0x40; pack.protocol = 0x01; pack.ipcheck = 0x0000; pack.src = src.s_addr; pack.dst = dst->s_addr; pack.type = 0x08; pack.code = 0x00; pack.checksum = 0x0000; pack.ident = 0x0000; pack.seq = htons(seq); /* Generate ICMP payload */ p8 = (uint8_t *) & pack + CREATEPING_IP + CREATEPING_ICMP; for (n = 0; n < (datasize); n++) p8[n] = n; if (datasize >= sizeof(struct timeval)) gettimeofday(tp, &tz); /* Calculate IP header checksum */ p = (uint16_t *) & pack; count = CREATEPING_IP; sum = 0; while (count > 1) { sum += *p++; count -= 2; } while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); pack.ipcheck = ~sum; /* Calculate ICMP checksum */ count = CREATEPING_ICMP + datasize; /* Length of ICMP message */ sum = 0; p = (uint16_t *) & pack; p += CREATEPING_IP / 2; while (count > 1) { sum += *p++; count -= 2; } if (count > 0) sum += *(unsigned char *)p; while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); pack.checksum = ~sum; ntransmitted++; return gtp_data_req(gsn, pdp, &pack, 28 + datasize); } int delete_context(struct pdp_t *pdp) { if (tun && options.ipdown) tun_runscript(tun, options.ipdown); ipdel((struct iphash_t *)pdp->peer); memset(pdp->peer, 0, sizeof(struct iphash_t)); /* To be sure */ if (1 == options.contexts) state = 5; /* Disconnected */ return 0; } /* Callback for receiving messages from tun */ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) { struct iphash_t *ipm; struct in_addr src; struct tun_packet_t *iph = (struct tun_packet_t *)pack; src.s_addr = iph->src; if (ipget(&ipm, &src)) { printf("Dropping packet from invalid source address: %s\n", inet_ntoa(src)); return 0; } if (ipm->pdp) /* Check if a peer protocol is defined */ gtp_data_req(gsn, ipm->pdp, pack, len); return 0; } int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) { struct in_addr addr; struct iphash_t *iph = (struct iphash_t *)cbp; if (cause < 0) { printf("Create PDP Context Request timed out\n"); if (iph->pdp->version == 1) { printf("Retrying with version 0\n"); iph->pdp->version = 0; gtp_create_context_req(gsn, iph->pdp, iph); return 0; } else { state = 0; pdp_freepdp(iph->pdp); iph->pdp = NULL; return EOF; } } if (cause != 128) { printf ("Received create PDP context response. Cause value: %d\n", cause); state = 0; pdp_freepdp(iph->pdp); iph->pdp = NULL; return EOF; /* Not what we expected */ } if (pdp_euaton(&pdp->eua, &addr)) { printf ("Received create PDP context response. Cause value: %d\n", cause); pdp_freepdp(iph->pdp); iph->pdp = NULL; state = 0; return EOF; /* Not a valid IP address */ } printf("Received create PDP context response. IP address: %s\n", inet_ntoa(addr)); if ((options.createif) && (!options.net.s_addr)) { struct in_addr m; #ifdef HAVE_INET_ATON inet_aton("255.255.255.255", &m); #else m.s_addr = -1; #endif /* printf("Setting up interface and routing\n"); */ tun_addaddr(tun, &addr, &addr, &m); if (options.defaultroute) { struct in_addr rm; rm.s_addr = 0; tun_addroute(tun, &rm, &addr, &rm); } if (options.ipup) tun_runscript(tun, options.ipup); } ipset((struct iphash_t *)pdp->peer, &addr); state = 2; /* Connected */ return 0; } int delete_pdp_conf(struct pdp_t *pdp, int cause) { printf("Received delete PDP context response. Cause value: %d\n", cause); return 0; } int echo_conf(int recovery) { if (recovery < 0) { printf("Echo Request timed out\n"); if (echoversion == 1) { printf("Retrying with version 0\n"); echoversion = 0; gtp_echo_req(gsn, echoversion, NULL, &options.remote); return 0; } else { state = 0; return EOF; } } else { printf("Received echo response\n"); if (!options.contexts) state = 5; } return 0; } int conf(int type, int cause, struct pdp_t *pdp, void *cbp) { /* if (cause < 0) return 0; Some error occurred. We don't care */ switch (type) { case GTP_ECHO_REQ: return echo_conf(cause); case GTP_CREATE_PDP_REQ: return create_pdp_conf(pdp, cbp, cause); case GTP_DELETE_PDP_REQ: if (cause != 128) return 0; /* Request not accepted. We don't care */ return delete_pdp_conf(pdp, cause); default: return 0; } } int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len) { /* printf("encaps_tun. Packet received: forwarding to tun\n"); */ return tun_encaps((struct tun_t *)pdp->ipif, pack, len); } int main(int argc, char **argv) { fd_set fds; /* For select() */ struct timeval idleTime; /* How long to select() */ struct pdp_t *pdp; int n; int starttime = time(NULL); /* Time program was started */ int stoptime = 0; /* Time to exit */ int pingtimeout = 0; /* Time to print ping statistics */ struct timezone tz; /* Used for calculating ping times */ struct timeval tv; int diff; osmo_init_logging(&log_info); /* Process options given in configuration file and command line */ if (process_options(argc, argv)) exit(1); printf("\nInitialising GTP library\n"); if (gtp_new(&gsn, options.statedir, &options.listen, GTP_MODE_SGSN)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Failed to create gtp"); exit(1); } if (gsn->fd0 > maxfd) maxfd = gsn->fd0; if (gsn->fd1c > maxfd) maxfd = gsn->fd1c; if (gsn->fd1u > maxfd) maxfd = gsn->fd1u; gtp_set_cb_delete_context(gsn, delete_context); gtp_set_cb_conf(gsn, conf); if (options.createif) gtp_set_cb_data_ind(gsn, encaps_tun); else gtp_set_cb_data_ind(gsn, encaps_ping); if (options.createif) { printf("Setting up interface\n"); /* Create a tunnel interface */ if (tun_new((struct tun_t **)&tun)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Failed to create tun"); exit(1); } tun_set_cb_ind(tun, cb_tun_ind); if (tun->fd > maxfd) maxfd = tun->fd; } if ((options.createif) && (options.net.s_addr)) { /* printf("Setting up interface and routing\n"); */ tun_addaddr(tun, &options.netaddr, &options.destaddr, &options.mask); if (options.defaultroute) { struct in_addr rm; rm.s_addr = 0; tun_addroute(tun, &rm, &options.destaddr, &rm); } if (options.ipup) tun_runscript(tun, options.ipup); } /* Initialise hash tables */ memset(&iphash, 0, sizeof(iphash)); memset(&iparr, 0, sizeof(iparr)); printf("Done initialising GTP library\n\n"); /* See if anybody is there */ printf("Sending off echo request\n"); echoversion = options.gtpversion; gtp_echo_req(gsn, echoversion, NULL, &options.remote); /* Is remote alive? */ for (n = 0; n < options.contexts; n++) { uint64_t myimsi; printf("Setting up PDP context #%d\n", n); iparr[n].inuse = 1; /* TODO */ imsi_add(options.imsi, &myimsi, n); /* Allocated here. */ /* If create context failes we have to deallocate ourselves. */ /* Otherwise it is deallocated by gtplib */ pdp_newpdp(&pdp, myimsi, options.nsapi, NULL); pdp->peer = &iparr[n]; pdp->ipif = tun; /* TODO */ iparr[n].pdp = pdp; if (options.gtpversion == 0) { if (options.qos.l - 1 > sizeof(pdp->qos_req0)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "QoS length too big"); exit(1); } else { memcpy(pdp->qos_req0, options.qos.v, options.qos.l); } } pdp->qos_req.l = options.qos.l; memcpy(pdp->qos_req.v, options.qos.v, options.qos.l); pdp->selmode = options.selmode; pdp->rattype.l = options.rattype.l; memcpy(pdp->rattype.v, options.rattype.v, options.rattype.l); pdp->rattype_given = options.rattype_given; pdp->userloc.l = options.userloc.l; memcpy(pdp->userloc.v, options.userloc.v, options.userloc.l); pdp->userloc_given = options.userloc_given; pdp->rai.l = options.rai.l; memcpy(pdp->rai.v, options.rai.v, options.rai.l); pdp->rai_given = options.rai_given; pdp->mstz.l = options.mstz.l; memcpy(pdp->mstz.v, options.mstz.v, options.mstz.l); pdp->mstz_given = options.mstz_given; pdp->imeisv.l = options.imeisv.l; memcpy(pdp->imeisv.v, options.imeisv.v, options.imeisv.l); pdp->imeisv_given = options.imeisv_given; pdp->norecovery_given = options.norecovery_given; if (options.apn.l > sizeof(pdp->apn_use.v)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "APN length too big"); exit(1); } else { pdp->apn_use.l = options.apn.l; memcpy(pdp->apn_use.v, options.apn.v, options.apn.l); } pdp->gsnlc.l = sizeof(options.listen); memcpy(pdp->gsnlc.v, &options.listen, sizeof(options.listen)); pdp->gsnlu.l = sizeof(options.listen); memcpy(pdp->gsnlu.v, &options.listen, sizeof(options.listen)); if (options.msisdn.l > sizeof(pdp->msisdn.v)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "MSISDN length too big"); exit(1); } else { msisdn_add(&options.msisdn, &pdp->msisdn, n); } ipv42eua(&pdp->eua, NULL); /* Request dynamic IP address */ if (options.pco.l > sizeof(pdp->pco_req.v)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "PCO length too big"); exit(1); } else { pdp->pco_req.l = options.pco.l; memcpy(pdp->pco_req.v, options.pco.v, options.pco.l); } pdp->version = options.gtpversion; pdp->hisaddr0 = options.remote; pdp->hisaddr1 = options.remote; pdp->cch_pdp = options.cch; /* 2048 = Normal, 1024 = Prepaid, 512 = Flat rate, 256 = Hot billing */ /* Create context */ /* We send this of once. Retransmissions are handled by gtplib */ gtp_create_context_req(gsn, pdp, &iparr[n]); } state = 1; /* Enter wait_connection state */ printf("Waiting for response from ggsn........\n\n"); /******************************************************************/ /* Main select loop */ /******************************************************************/ while ((0 != state) && (5 != state)) { /* Take down client after timeout after disconnect */ if ((4 == state) && ((stoptime) <= time(NULL))) { state = 5; } /* Take down client after timelimit timeout */ if ((2 == state) && (options.timelimit) && ((starttime + options.timelimit) <= time(NULL))) { state = 3; } /* Take down client after ping timeout */ if ((2 == state) && (pingtimeout) && (pingtimeout <= time(NULL))) { state = 3; } /* Set pingtimeout for later disconnection */ if (options.pingcount && ntransmitted >= options.pingcount) { pingtimeout = time(NULL) + 5; /* Extra seconds */ } /* Print statistics if no more ping packets are missing */ if (ntransmitted && options.pingcount && nreceived >= options.pingcount) { ping_finish(); if (!options.createif) state = 3; } /* Send off disconnect */ if (3 == state) { state = 4; stoptime = time(NULL) + 5; /* Extra seconds to allow disconnect */ for (n = 0; n < options.contexts; n++) { /* Delete context */ printf("Disconnecting PDP context #%d\n", n); gtp_delete_context_req(gsn, iparr[n].pdp, NULL, 1); if ((options.pinghost.s_addr != 0) && ntransmitted) ping_finish(); } } /* Send of ping packets */ diff = 0; while ((diff <= 0) && /* Send off an ICMP ping packet */ /*if ( */ (options.pinghost.s_addr) && (2 == state) && ((pingseq < options.pingcount) || (options.pingcount == 0))) { if (!pingseq) gettimeofday(&firstping, &tz); /* Set time of first ping */ gettimeofday(&tv, &tz); diff = 1000000 / options.pingrate * pingseq - 1000000 * (tv.tv_sec - firstping.tv_sec) - (tv.tv_usec - firstping.tv_usec); /* Microseconds safe up to 500 sec */ if (diff <= 0) { if (options.debug) printf("Create_ping %d\n", diff); create_ping(gsn, iparr[pingseq % options.contexts].pdp, &options.pinghost, pingseq, options.pingsize); pingseq++; } } FD_ZERO(&fds); if (tun) FD_SET(tun->fd, &fds); FD_SET(gsn->fd0, &fds); FD_SET(gsn->fd1c, &fds); FD_SET(gsn->fd1u, &fds); gtp_retranstimeout(gsn, &idleTime); ping_timeout(&idleTime); if (options.debug) printf("idletime.tv_sec %d, idleTime.tv_usec %d\n", (int)idleTime.tv_sec, (int)idleTime.tv_usec); switch (select(maxfd + 1, &fds, NULL, NULL, &idleTime)) { case -1: SYS_ERR(DSGSN, LOGL_ERROR, 0, "Select returned -1"); break; case 0: gtp_retrans(gsn); /* Only retransmit if nothing else */ break; default: break; } if ((tun) && FD_ISSET(tun->fd, &fds) && tun_decaps(tun) < 0) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "TUN decaps failed"); } if (FD_ISSET(gsn->fd0, &fds)) gtp_decaps0(gsn); if (FD_ISSET(gsn->fd1c, &fds)) gtp_decaps1c(gsn); if (FD_ISSET(gsn->fd1u, &fds)) gtp_decaps1u(gsn); } gtp_free(gsn); /* Clean up the gsn instance */ if (options.createif) tun_free(tun); if (0 == state) exit(1); /* Indicate error */ return 0; } openggsn-0.92/src/000077500000000000000000000000001262356443100140615ustar00rootroot00000000000000openggsn-0.92/src/Makefile.in000066400000000000000000000000001262356443100161140ustar00rootroot00000000000000openggsn-0.92/tests/000077500000000000000000000000001262356443100144345ustar00rootroot00000000000000openggsn-0.92/tests/Makefile.in000066400000000000000000000000001262356443100164670ustar00rootroot00000000000000openggsn-0.92/version000077500000000000000000000007051262356443100147070ustar00rootroot00000000000000#!/bin/sh # # Little shell script to grab current version number from configure.in # # $Id: version,v 1.3 2003/01/28 22:39:30 jjako Exp $ VER=`grep AC_INIT configure.in | awk -F'[(),]' '{print $3}'` if [ "$1" == "-VERSION" ] then echo $VER | awk -F'.' '{print $1}' exit fi if [ "$1" == "-PATCHLEVEL" ] then echo $VER | awk -F'.' '{print $2}' exit fi if [ "$1" == "-SUBLEVEL" ] then echo $VER | awk -F'.' '{print $3}' exit fi echo $VER exit