From 2a103687b6c08c95b96ccf447ef096bc7e54d7ae Mon Sep 17 00:00:00 2001 From: Emmanuel Bretelle Date: Tue, 7 Sep 2010 17:01:20 +0200 Subject: Move common sgsnemu/ggsn files to directory "lib" Some files like in sgsnemu and ggsn directory where exactly the same. They are now moved to the same directory for easier maintenance Signed-off-by: Emmanuel Bretelle --- lib/getopt.c | 1055 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/getopt1.c | 188 ++++++++++ lib/gnugetopt.h | 180 ++++++++++ lib/ippool.c | 526 +++++++++++++++++++++++++++ lib/ippool.h | 105 ++++++ lib/lookup.c | 80 +++++ lib/lookup.h | 25 ++ lib/syserr.c | 71 ++++ lib/syserr.h | 21 ++ lib/tun.c | 897 ++++++++++++++++++++++++++++++++++++++++++++++ lib/tun.h | 74 ++++ 11 files changed, 3222 insertions(+) create mode 100644 lib/getopt.c create mode 100644 lib/getopt1.c create mode 100644 lib/gnugetopt.h create mode 100644 lib/ippool.c create mode 100644 lib/ippool.h create mode 100755 lib/lookup.c create mode 100755 lib/lookup.h create mode 100644 lib/syserr.c create mode 100644 lib/syserr.h create mode 100644 lib/tun.c create mode 100644 lib/tun.h (limited to 'lib') diff --git a/lib/getopt.c b/lib/getopt.c new file mode 100644 index 0000000..9bafa45 --- /dev/null +++ b/lib/getopt.c @@ -0,0 +1,1055 @@ +/* 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 */ diff --git a/lib/getopt1.c b/lib/getopt1.c new file mode 100644 index 0000000..22a7efb --- /dev/null +++ b/lib/getopt1.c @@ -0,0 +1,188 @@ +/* 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 */ diff --git a/lib/gnugetopt.h b/lib/gnugetopt.h new file mode 100644 index 0000000..a1b8dd6 --- /dev/null +++ b/lib/gnugetopt.h @@ -0,0 +1,180 @@ +/* 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 */ diff --git a/lib/ippool.c b/lib/ippool.c new file mode 100644 index 0000000..fa3d8af --- /dev/null +++ b/lib/ippool.c @@ -0,0 +1,526 @@ +/* + * 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 +#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; nlistsize; 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask"); + return -1; /* Wrong mask format (not all ones followed by all zeros)*/ + } + mask->s_addr = htonl(m); + break; + default: + sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Invalid mask"); + return -1; /* Invalid mask */ + } + + if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, + "Failed to allocate memory for hash members in ippool"); + return -1; + } + + (*this)->firstdyn = NULL; + (*this)->lastdyn = NULL; + for (i = 0; imember[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; imember[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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 0, "Static IP address not allowed"); + return -1; + } + if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, "Static out of range"); + return -1; + } + } + else { + if (!this->allowdyn) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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 diff --git a/lib/ippool.h b/lib/ippool.h new file mode 100644 index 0000000..02691a6 --- /dev/null +++ b/lib/ippool.h @@ -0,0 +1,105 @@ +/* + * 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 */ diff --git a/lib/lookup.c b/lib/lookup.c new file mode 100755 index 0000000..02e2491 --- /dev/null +++ b/lib/lookup.c @@ -0,0 +1,80 @@ +/* + * 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; +} + diff --git a/lib/lookup.h b/lib/lookup.h new file mode 100755 index 0000000..18b94c1 --- /dev/null +++ b/lib/lookup.h @@ -0,0 +1,25 @@ +/* + * 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 */ diff --git a/lib/syserr.c b/lib/syserr.c new file mode 100644 index 0000000..002d8c3 --- /dev/null +++ b/lib/syserr.c @@ -0,0 +1,71 @@ +/* + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syserr.h" + + +void sys_err(int pri, char *fn, int ln, int en, char *fmt, ...) { + va_list args; + char buf[SYSERR_MSGSIZE]; + + va_start(args, fmt); + vsnprintf(buf, SYSERR_MSGSIZE, fmt, args); + va_end(args); + buf[SYSERR_MSGSIZE-1] = 0; /* Make sure it is null terminated */ + if (en) + syslog(pri, "%s: %d: %d (%s) %s", fn, ln, en, strerror(en), buf); + else + syslog(pri, "%s: %d: %s", fn, ln, buf); +} + +void sys_errpack(int pri, char *fn, int ln, int en, struct sockaddr_in *peer, + void *pack, unsigned len, char *fmt, ...) { + + va_list args; + char buf[SYSERR_MSGSIZE]; + char buf2[SYSERR_MSGSIZE]; + unsigned int n; + int pos; + + va_start(args, fmt); + vsnprintf(buf, SYSERR_MSGSIZE, fmt, args); + va_end(args); + buf[SYSERR_MSGSIZE-1] = 0; + + snprintf(buf2, SYSERR_MSGSIZE, "Packet from %s:%u, length: %d, content:", + inet_ntoa(peer->sin_addr), + ntohs(peer->sin_port), + len); + buf2[SYSERR_MSGSIZE-1] = 0; + pos = strlen(buf2); + for(n=0; n +#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, int *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(LOG_ERR, __FILE__, __LINE__, errno, + "socket() failed"); + } + if (ioctl(fd, SIOCGIFINDEX, &ifr)) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, + "socket() failed"); + } + if (ioctl(fd, SIOCSIFFLAGS, &ifr)) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, + "bind() failed"); + close(fd); + return -1; + } + + addr_len = sizeof(local); + if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, errno, + "getsockname() failed"); + close(fd); + return -1; + } + + if (addr_len != sizeof(local)) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "Wrong address length %d", addr_len); + close(fd); + return -1; + } + + if (local.nl_family != AF_NETLINK) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, + "bind() failed"); + close(fd); + return -1; + } + + addr_len = sizeof(local); + if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, errno, + "getsockname() failed"); + close(fd); + return -1; + } + + if (addr_len != sizeof(local)) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "Wrong address length %d", addr_len); + close(fd); + return -1; + } + + if (local.nl_family != AF_NETLINK) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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 */ + + tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */ + 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(LOG_ERR, __FILE__, __LINE__, errno, + "socket() failed"); + return -1; + } + + if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, + "socket() failed"); + return -1; + } + + if (addr) { /* Set the interface address */ + this->addr.s_addr = addr->s_addr; + ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = addr->s_addr; + if (ioctl(fd, SIOCSIFADDR, (void *) &ifr) < 0) { + if (errno != EEXIST) { + sys_err(LOG_ERR, __FILE__, __LINE__, errno, + "ioctl(SIOCSIFADDR) failed"); + } + else { + sys_err(LOG_WARNING, __FILE__, __LINE__, errno, + "ioctl(SIOCSIFADDR): Address already exists"); + } + close(fd); + return -1; + } + } + + if (dstaddr) { /* Set the destination address */ + this->dstaddr.s_addr = dstaddr->s_addr; + ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = + dstaddr->s_addr; + if (ioctl(fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, errno, + "ioctl(SIOCSIFDSTADDR) failed"); + close(fd); + return -1; + } + } + + if (netmask) { /* Set the netmask */ + this->netmask.s_addr = netmask->s_addr; +#if defined(__linux__) + ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = + netmask->s_addr; + +#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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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; + ((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr = dst->s_addr; + ((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr = gateway->s_addr; + ((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr = mask->s_addr; + + if (delete) { + if (ioctl(fd, SIOCDELRT, (void *) &r) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, errno, + "ioctl(SIOCDELRT) failed"); + close(fd); + return -1; + } + } + else { + if (ioctl(fd, SIOCADDRT, (void *) &r) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, + "write() failed"); + close(fd); + return -1; + } + close(fd); + return 0; + +#elif defined(__sun__) + sys_err(LOG_WARNING, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, "ioctl() failed"); + close((*tun)->fd); + return -1; + } + + strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ); + (*tun)->devname[IFNAMSIZ] = 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); + devname[sizeof(devname)] = 0; + if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break; + if (errno != EBUSY) break; + } + if ((*tun)->fd < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/udp"); + return -1; + } + + if( ((*tun)->fd = open("/dev/tun", O_RDWR, 0)) < 0){ + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, errno, "Can't assign new interface"); + return -1; + } + + if( (if_fd = open("/dev/tun", O_RDWR, 0)) < 0){ + sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/tun (2)"); + return -1; + } + if(ioctl(if_fd, I_PUSH, "ip") < 0){ + sys_err(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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(LOG_ERR, __FILE__, __LINE__, 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]; + + 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; + system(buf); + return 0; +} diff --git a/lib/tun.h b/lib/tun.h new file mode 100644 index 0000000..7972c53 --- /dev/null +++ b/lib/tun.h @@ -0,0 +1,74 @@ +/* + * 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 */ -- cgit v1.2.3