]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/options.c
add stuff for packet filtering
[ppp.git] / pppd / options.c
index 59c7b5b9f6e0353bc2335f7587633ea155bb0d9a..c92e2ddfbf8e120253f1793693cc95a58ac93fcb 100644 (file)
@@ -17,9 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#ifndef lint
-static const char rcsid[] = "$Id: options.c,v 1.61 1999/08/12 04:17:07 paulus Exp $";
-#endif
+#define RCSID  "$Id: options.c,v 1.76 2000/08/01 01:38:30 paulus Exp $"
 
 #include <ctype.h>
 #include <stdio.h>
@@ -36,6 +34,9 @@ static const char rcsid[] = "$Id: options.c,v 1.61 1999/08/12 04:17:07 paulus Ex
 #include <sys/stat.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#ifdef PLUGIN
+#include <dlfcn.h>
+#endif
 #ifdef PPP_FILTER
 #include <pcap.h>
 #include <pcap-int.h>  /* XXX: To get struct pcap */
@@ -57,6 +58,8 @@ static const char rcsid[] = "$Id: options.c,v 1.61 1999/08/12 04:17:07 paulus Ex
 char *strdup __P((char *));
 #endif
 
+static const char rcsid[] = RCSID;
+
 /*
  * Option variables and default values.
  */
@@ -67,18 +70,9 @@ int  debug = 0;              /* Debug flag */
 int    kdebugflag = 0;         /* Tell kernel to print debug messages */
 int    default_device = 1;     /* Using /dev/tty or equivalent */
 char   devnam[MAXPATHLEN];     /* Device name */
-int    crtscts = 0;            /* Use hardware flow control */
-bool   modem = 1;              /* Use modem control lines */
-int    inspeed = 0;            /* Input/Output speed requested */
 u_int32_t netmask = 0;         /* IP netmask to set on interface */
-bool   lockflag = 0;           /* Create lock file to lock the serial dev */
 bool   nodetach = 0;           /* Don't detach from controlling tty */
 bool   updetach = 0;           /* Detach once link is up */
-char   *initializer = NULL;    /* Script to initialize physical link */
-char   *connector = NULL;      /* Script to establish physical link */
-char   *disconnector = NULL;   /* Script to disestablish physical link */
-char   *welcomer = NULL;       /* Script to run after phys link estab. */
-char   *ptycommand = NULL;     /* Command to run on other side of pty */
 int    maxconnect = 0;         /* Maximum connect time */
 char   user[MAXNAMELEN];       /* Username for PAP */
 char   passwd[MAXSECRETLEN];   /* Password for PAP */
@@ -88,20 +82,22 @@ bool        demand = 0;             /* do dial-on-demand */
 char   *ipparam = NULL;        /* Extra parameter for ip up/down scripts */
 int    idle_time_limit = 0;    /* Disconnect if idle for this many seconds */
 int    holdoff = 30;           /* # seconds to pause before reconnecting */
-bool   notty = 0;              /* Stdin/out is not a tty */
-char   *record_file = NULL;    /* File to record chars sent/received */
-int    using_pty = 0;
-bool   sync_serial = 0;        /* Device is synchronous serial device */
+bool   holdoff_specified;      /* true if a holdoff value has been given */
 int    log_to_fd = 1;          /* send log messages to this fd too */
-int    maxfail;                /* max # of unsuccessful connection attempts */
+int    maxfail = 10;           /* max # of unsuccessful connection attempts */
+char   linkname[MAXPATHLEN];   /* logical name for link */
+bool   tune_kernel;            /* may alter kernel settings */
+int    connect_delay = 1000;   /* wait this many ms after connect script */
+int    req_unit = -1;          /* requested interface unit */
+bool   multilink = 0;          /* Enable multilink operation */
+char   *bundle_name = NULL;    /* bundle name for multilink */
 
 extern option_t auth_options[];
 extern struct stat devstat;
-extern int prepass;            /* Doing pre-pass to find device name */
 
 struct option_info initializer_info;
-struct option_info connector_info;
-struct option_info disconnector_info;
+struct option_info connect_script_info;
+struct option_info disconnect_script_info;
 struct option_info welcomer_info;
 struct option_info devnam_info;
 struct option_info ptycommand_info;
@@ -116,6 +112,8 @@ char *current_option;               /* the name of the option being parsed */
 int  privileged_option;                /* set iff the current option came from root */
 char *option_source;           /* string saying where the option came from */
 bool log_to_file;              /* log_to_fd is a file opened by us */
+bool log_to_specific_fd;       /* log_to_fd was specified by user option */
+bool no_override;              /* don't override previously-set options */
 
 /*
  * Prototypes
@@ -126,25 +124,36 @@ static int setspeed __P((char *));
 static int noopt __P((char **));
 static int setdomain __P((char **));
 static int setnetmask __P((char **));
-static int setxonxoff __P((char **));
 static int readfile __P((char **));
 static int callfile __P((char **));
 static int showversion __P((char **));
 static int showhelp __P((char **));
 static void usage __P((void));
 static int setlogfile __P((char **));
+#ifdef PLUGIN
+static int loadplugin __P((char **));
+#endif
 
 #ifdef PPP_FILTER
 static int setpassfilter __P((char **));
 static int setactivefilter __P((char **));
 #endif
 
-
 static option_t *find_option __P((char *name));
 static int process_option __P((option_t *, char **));
 static int n_arguments __P((option_t *));
 static int number_option __P((char *, u_int32_t *, int));
 
+/*
+ * Structure to store extra lists of options.
+ */
+struct option_list {
+    option_t *options;
+    struct option_list *next;
+};
+
+static struct option_list *extra_options = NULL;
+
 /*
  * Valid arguments.
  */
@@ -165,56 +174,19 @@ option_t general_options[] = {
       "Set time in seconds before retrying connection" },
     { "idle", o_int, &idle_time_limit,
       "Set time in seconds before disconnecting idle link" },
-    { "lock", o_bool, &lockflag,
-      "Lock serial device with UUCP-style lock file", 1 },
-    { "-all", o_special_noarg, noopt,
+    { "-all", o_special_noarg, (void *)noopt,
       "Don't request/allow any LCP or IPCP options (useless)" },
-    { "init", o_string, &initializer,
-      "A program to initialize the device",
-      OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
-    { "connect", o_string, &connector,
-      "A program to set up a connection",
-      OPT_A2INFO | OPT_PRIVFIX, &connector_info },
-    { "disconnect", o_string, &disconnector,
-      "Program to disconnect serial device",
-      OPT_A2INFO | OPT_PRIVFIX, &disconnector_info },
-    { "welcome", o_string, &welcomer,
-      "Script to welcome client",
-      OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
-    { "pty", o_string, &ptycommand,
-      "Script to run on pseudo-tty master side",
-      OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
-    { "notty", o_bool, &notty,
-      "Input/output is not a tty", OPT_DEVNAM | 1 },
-    { "record", o_string, &record_file,
-      "Record characters sent/received to file" },
     { "maxconnect", o_int, &maxconnect,
       "Set connection time limit", OPT_LLIMIT|OPT_NOINCR|OPT_ZEROINF },
-    { "crtscts", o_int, &crtscts,
-      "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
-    { "nocrtscts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
-    { "-crtscts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
-    { "cdtrcts", o_int, &crtscts,
-      "Set alternate hardware (DTR/CTS) flow control", OPT_NOARG|OPT_VAL(2) },
-    { "nocdtrcts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
-    { "xonxoff", o_special_noarg, setxonxoff,
-      "Set software (XON/XOFF) flow control" },
-    { "domain", o_special, setdomain,
+    { "domain", o_special, (void *)setdomain,
       "Add given domain name to hostname" },
     { "mtu", o_int, &lcp_allowoptions[0].mru,
       "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
-    { "netmask", o_special, setnetmask,
+    { "netmask", o_special, (void *)setnetmask,
       "set netmask" },
-    { "modem", o_bool, &modem,
-      "Use modem control lines", 1 },
-    { "local", o_bool, &modem,
-      "Don't use modem control lines" },
-    { "file", o_special, readfile,
+    { "file", o_special, (void *)readfile,
       "Take options from a file", OPT_PREPASS },
-    { "call", o_special, callfile,
+    { "call", o_special, (void *)callfile,
       "Take options from a privileged file", OPT_PREPASS },
     { "persist", o_bool, &persist,
       "Keep on reopening connection after close", 1 },
@@ -222,17 +194,16 @@ option_t general_options[] = {
       "Turn off persist option" },
     { "demand", o_bool, &demand,
       "Dial on demand", OPT_INITONLY | 1, &persist },
-    { "--version", o_special_noarg, showversion,
+    { "--version", o_special_noarg, (void *)showversion,
       "Show version number" },
-    { "--help", o_special_noarg, showhelp,
+    { "--help", o_special_noarg, (void *)showhelp,
       "Show brief listing of options" },
-    { "-h", o_special_noarg, showhelp,
+    { "-h", o_special_noarg, (void *)showhelp,
       "Show brief listing of options" },
-    { "sync", o_bool, &sync_serial,
-      "Use synchronous HDLC serial encoding", 1 },
     { "logfd", o_int, &log_to_fd,
-      "Send log messages to this file descriptor" },
-    { "logfile", o_special, setlogfile,
+      "Send log messages to this file descriptor",
+      0, &log_to_specific_fd },
+    { "logfile", o_special, (void *)setlogfile,
       "Append log messages to this file" },
     { "nolog", o_int, &log_to_fd,
       "Don't send log messages to any file",
@@ -240,8 +211,35 @@ option_t general_options[] = {
     { "nologfd", o_int, &log_to_fd,
       "Don't send log messages to any file descriptor",
       OPT_NOARG | OPT_VAL(-1) },
+    { "linkname", o_string, linkname,
+      "Set logical name for link",
+      OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
     { "maxfail", o_int, &maxfail,
       "Maximum number of unsuccessful connection attempts to allow" },
+    { "ktune", o_bool, &tune_kernel,
+      "Alter kernel settings as necessary", 1 },
+    { "noktune", o_bool, &tune_kernel,
+      "Don't alter kernel settings", 0 },
+    { "connect-delay", o_int, &connect_delay,
+      "Maximum time (in ms) to wait after connect script finishes" },
+    { "unit", o_int, &req_unit,
+      "PPP interface unit number to use if possible", OPT_LLIMIT, 0, 0 },
+#ifdef HAVE_MULTILINK
+    { "multilink", o_bool, &multilink,
+      "Enable multilink operation", 1 },
+    { "nomultilink", o_bool, &multilink,
+      "Disable multilink operation", 0 },
+    { "mp", o_bool, &multilink,
+      "Enable multilink operation", 1 },
+    { "nomp", o_bool, &multilink,
+      "Disable multilink operation", 0 },
+    { "bundle", o_string, &bundle_name,
+      "Bundle name for multilink" },
+#endif /* HAVE_MULTILINK */
+#ifdef PLUGIN
+    { "plugin", o_special, (void *)loadplugin,
+      "Load a plug-in module into pppd", OPT_PRIV },
+#endif
 
 #ifdef PPP_FILTER
     { "pdebug", o_int, &dflag,
@@ -279,8 +277,6 @@ See pppd(8) for more options.\n\
 
 /*
  * parse_args - parse a string of arguments from the command line.
- * If prepass is true, we are scanning for the device name and only
- * processing a few options, so error messages are suppressed.
  */
 int
 parse_args(argc, argv)
@@ -394,9 +390,12 @@ options_from_file(filename, must_exist, check_prot, priv)
     if (check_prot)
        seteuid(0);
     if (f == NULL) {
-       if (!must_exist && err == ENOENT)
-           return 1;
        errno = err;
+       if (!must_exist) {
+           if (err != ENOENT && err != ENOTDIR)
+               warn("Warning: can't open options file %s: %m", filename);
+           return 1;
+       }
        option_error("Can't open options file %s: %m", filename);
        return 0;
     }
@@ -425,7 +424,7 @@ options_from_file(filename, must_exist, check_prot, priv)
                argv[i] = args[i];
            }
            current_option = cmd;
-           if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
+           if ((opt->flags & OPT_DEVEQUIV) && no_override) {
                option_error("the %s option may not be used in the %s file",
                             cmd, filename);
                goto err;
@@ -486,6 +485,9 @@ options_from_user()
 /*
  * options_for_tty - See if an options file exists for the serial
  * device, and if so, interpret options from it.
+ * We only allow the per-tty options file to override anything from
+ * the command line if it is something that the user can't override
+ * once it has been set by root.
  */
 int
 options_for_tty()
@@ -508,7 +510,9 @@ options_for_tty()
     for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p)
        if (*p == '/')
            *p = '.';
+    no_override = 1;
     ret = options_from_file(path, 0, 0, 1);
+    no_override = 0;
     free(path);
     return ret;
 }
@@ -581,8 +585,13 @@ find_option(name)
     char *name;
 {
     option_t *opt;
+    struct option_list *list;
     int i;
 
+    for (list = extra_options; list != NULL; list = list->next)
+       for (opt = list->options; opt->name != NULL; ++opt)
+           if (strcmp(name, opt->name) == 0)
+               return opt;
     for (opt = general_options; opt->name != NULL; ++opt)
        if (strcmp(name, opt->name) == 0)
            return opt;
@@ -610,8 +619,14 @@ process_option(opt, argv)
     char *sv;
     int (*parser) __P((char **));
 
-    if ((opt->flags & OPT_PREPASS) == 0 && prepass)
-       return 1;
+    if (no_override && (opt->flags & OPT_SEENIT)) {
+       struct option_info *ip = (struct option_info *) opt->addr2;
+       if (!(privileged && (opt->flags & OPT_PRIVFIX)))
+           return 1;
+       if (!ip || ip->priv)
+           return 1;
+       warn("%s option from per-tty file overrides command line", opt->name);
+    }
     if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
        option_error("it's too late to use the %s option", opt->name);
        return 0;
@@ -631,6 +646,7 @@ process_option(opt, argv)
            return 0;
        }
     }
+    opt->flags |= OPT_SEENIT;
 
     switch (opt->type) {
     case o_bool:
@@ -739,6 +755,23 @@ n_arguments(opt)
            || (opt->flags & OPT_NOARG))? 0: 1;
 }
 
+/*
+ * add_options - add a list of options to the set we grok.
+ */
+void
+add_options(opt)
+    option_t *opt;
+{
+    struct option_list *list;
+
+    list = malloc(sizeof(*list));
+    if (list == 0)
+       novm("option list entry");
+    list->options = opt;
+    list->next = extra_options;
+    extra_options = list;
+}
+
 /*
  * usage - print out a message telling how to use the program.
  */
@@ -790,17 +823,13 @@ option_error __V((char *fmt, ...))
     va_list args;
     char buf[256];
 
-#if __STDC__
+#if defined(__STDC__)
     va_start(args, fmt);
 #else
     char *fmt;
     va_start(args);
     fmt = va_arg(args, char *);
 #endif
-    if (prepass) {
-       va_end(args);
-       return;
-    }
     vslprintf(buf, sizeof(buf), fmt, args);
     va_end(args);
     if (phase == PHASE_INITIALIZE)
@@ -1180,16 +1209,6 @@ callfile(argv)
 }
 
 #ifdef PPP_FILTER
-/*
- * setpdebug - Set libpcap debugging level.
- */
-static int
-setpdebug(argv)
-    char **argv;
-{
-    return int_option(*argv, &dflag);
-}
-
 /*
  * setpassfilter - Set the pass filter for packets
  */
@@ -1270,10 +1289,13 @@ setspeed(arg)
     char *ptr;
     int spd;
 
+    if (no_override && inspeed != 0)
+       return 1;
     spd = strtol(arg, &ptr, 0);
     if (ptr == arg || *ptr != 0 || spd == 0)
        return 0;
-    inspeed = spd;
+    if (!no_override || inspeed == 0)
+       inspeed = spd;
     return 1;
 }
 
@@ -1314,7 +1336,8 @@ setdevname(cp)
     if (phase != PHASE_INITIALIZE) {
        option_error("device name cannot be changed after initialization");
        return -1;
-    } else if (devnam_fixed) {
+    }
+    if (no_override) {
        option_error("per-tty options file may not specify device name");
        return -1;
     }
@@ -1345,21 +1368,20 @@ setipaddr(arg)
     char *colon;
     u_int32_t local, remote;
     ipcp_options *wo = &ipcp_wantoptions[0];
-  
+    static int seen_local = 0, seen_remote = 0;
+
     /*
      * IP address pair separated by ":".
      */
     if ((colon = strchr(arg, ':')) == NULL)
        return 0;
-    if (prepass)
-       return 1;
   
     /*
      * If colon first character, then no local addr.
      */
-    if (colon != arg) {
+    if (colon != arg && !(no_override && seen_local)) {
        *colon = '\0';
-       if ((local = inet_addr(arg)) == -1) {
+       if ((local = inet_addr(arg)) == (u_int32_t) -1) {
            if ((hp = gethostbyname(arg)) == NULL) {
                option_error("unknown host: %s", arg);
                return -1;
@@ -1374,13 +1396,14 @@ setipaddr(arg)
        if (local != 0)
            wo->ouraddr = local;
        *colon = ':';
+       seen_local = 1;
     }
   
     /*
      * If colon last character, then no remote addr.
      */
-    if (*++colon != '\0') {
-       if ((remote = inet_addr(colon)) == -1) {
+    if (*++colon != '\0' && !(no_override && seen_remote)) {
+       if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
            if ((hp = gethostbyname(colon)) == NULL) {
                option_error("unknown host: %s", colon);
                return -1;
@@ -1396,6 +1419,7 @@ setipaddr(arg)
        }
        if (remote != 0)
            wo->hisaddr = remote;
+       seen_remote = 1;
     }
 
     return 1;
@@ -1409,39 +1433,20 @@ static int
 setnetmask(argv)
     char **argv;
 {
-    u_int32_t mask, b;
-    int n, ok;
-    char *p, *endp;
+    u_int32_t mask;
+    int n;
+    char *p;
 
     /*
      * Unfortunately, if we use inet_addr, we can't tell whether
      * a result of all 1s is an error or a valid 255.255.255.255.
      */
     p = *argv;
-    ok = 0;
-    mask = 0;
-    for (n = 3;; --n) {
-       b = strtoul(p, &endp, 0);
-       if (endp == p)
-           break;
-       if (b < 0 || b > 255) {
-           if (n == 3) {
-               /* accept e.g. 0xffffff00 */
-               p = endp;
-               mask = b;
-           }
-           break;
-       }
-       mask |= b << (n * 8);
-       p = endp;
-       if (*p != '.' || n == 0)
-           break;
-       ++p;
-    }
+    n = parse_dotted_ip(p, &mask);
 
     mask = htonl(mask);
 
-    if (*p != 0 || (netmask & ~mask) != 0) {
+    if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
        option_error("invalid netmask value '%s'", *argv);
        return 0;
     }
@@ -1450,15 +1455,37 @@ setnetmask(argv)
     return (1);
 }
 
-static int
-setxonxoff(argv)
-    char **argv;
+int
+parse_dotted_ip(p, vp)
+    char *p;
+    u_int32_t *vp;
 {
-    lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
-    lcp_wantoptions[0].neg_asyncmap = 1;
+    int n;
+    u_int32_t v, b;
+    char *endp, *p0 = p;
 
-    crtscts = -2;
-    return (1);
+    v = 0;
+    for (n = 3;; --n) {
+       b = strtoul(p, &endp, 0);
+       if (endp == p)
+           return 0;
+       if (b > 255) {
+           if (n < 3)
+               return 0;
+           /* accept e.g. 0xffffff00 */
+           *vp = b;
+           return endp - p0;
+       }
+       v |= b << (n * 8);
+       p = endp;
+       if (n == 0)
+           break;
+       if (*p != '.')
+           return 0;
+       ++p;
+    }
+    *vp = v;
+    return p - p0;
 }
 
 static int
@@ -1469,7 +1496,9 @@ setlogfile(argv)
 
     if (!privileged_option)
        seteuid(getuid());
-    fd = open(*argv, O_WRONLY | O_APPEND);
+    fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
+    if (fd < 0 && errno == EEXIST)
+       fd = open(*argv, O_WRONLY | O_APPEND);
     err = errno;
     if (!privileged_option)
        seteuid(0);
@@ -1484,3 +1513,33 @@ setlogfile(argv)
     log_to_file = 1;
     return 1;
 }
+
+#ifdef PLUGIN
+static int
+loadplugin(argv)
+    char **argv;
+{
+    char *arg = *argv;
+    void *handle;
+    const char *err;
+    void (*init) __P((void));
+
+    handle = dlopen(arg, RTLD_GLOBAL | RTLD_NOW);
+    if (handle == 0) {
+       err = dlerror();
+       if (err != 0)
+           option_error("%s", err);
+       option_error("Couldn't load plugin %s", arg);
+       return 0;
+    }
+    init = (void (*)(void))dlsym(handle, "plugin_init");
+    if (init == 0) {
+       option_error("%s has no initialization entry point", arg);
+       dlclose(handle);
+       return 0;
+    }
+    info("Plugin %s loaded.", arg);
+    (*init)();
+    return 1;
+}
+#endif /* PLUGIN */