main.c revision 74687
1/*
2 *			User Process PPP
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $FreeBSD: head/usr.sbin/ppp/main.c 74687 2001-03-23 11:43:22Z brian $
21 *
22 *	TODO:
23 */
24
25#include <sys/param.h>
26#include <netinet/in.h>
27#include <netinet/in_systm.h>
28#include <netinet/ip.h>
29#include <sys/un.h>
30#include <sys/socket.h>
31#include <net/route.h>
32
33#include <errno.h>
34#include <fcntl.h>
35#include <paths.h>
36#include <signal.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/time.h>
41#include <termios.h>
42#include <unistd.h>
43#include <sys/stat.h>
44
45#ifndef NONAT
46#ifdef LOCALNAT
47#include "alias.h"
48#else
49#include <alias.h>
50#endif
51#endif
52
53#include "layer.h"
54#include "probe.h"
55#include "mbuf.h"
56#include "log.h"
57#include "defs.h"
58#include "id.h"
59#include "timer.h"
60#include "fsm.h"
61#include "lqr.h"
62#include "hdlc.h"
63#include "lcp.h"
64#include "ccp.h"
65#include "iplist.h"
66#include "throughput.h"
67#include "slcompress.h"
68#include "ipcp.h"
69#include "filter.h"
70#include "descriptor.h"
71#include "link.h"
72#include "mp.h"
73#ifndef NORADIUS
74#include "radius.h"
75#endif
76#include "bundle.h"
77#include "auth.h"
78#include "systems.h"
79#include "sig.h"
80#include "main.h"
81#include "server.h"
82#include "prompt.h"
83#include "chat.h"
84#include "chap.h"
85#include "cbcp.h"
86#include "datalink.h"
87#include "iface.h"
88
89#ifndef O_NONBLOCK
90#ifdef O_NDELAY
91#define	O_NONBLOCK O_NDELAY
92#endif
93#endif
94
95static void DoLoop(struct bundle *);
96static void TerminalStop(int);
97
98static struct bundle *SignalBundle;
99static struct prompt *SignalPrompt;
100
101void
102Cleanup(int excode)
103{
104  SignalBundle->CleaningUp = 1;
105  bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
106}
107
108void
109AbortProgram(int excode)
110{
111  server_Close(SignalBundle);
112  log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
113  bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
114  bundle_Destroy(SignalBundle);
115  log_Close();
116  exit(excode);
117}
118
119static void
120CloseConnection(int signo)
121{
122  /* NOTE, these are manual, we've done a setsid() */
123  sig_signal(SIGINT, SIG_IGN);
124  log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo);
125  bundle_Down(SignalBundle, CLOSE_STAYDOWN);
126  sig_signal(SIGINT, CloseConnection);
127}
128
129static void
130CloseSession(int signo)
131{
132  log_Printf(LogPHASE, "Signal %d, terminate.\n", signo);
133  Cleanup(EX_TERM);
134}
135
136static pid_t BGPid = 0;
137
138static void
139KillChild(int signo)
140{
141  signal(signo, SIG_IGN);
142  log_Printf(LogPHASE, "Parent: Signal %d\n", signo);
143  kill(BGPid, SIGINT);
144}
145
146static void
147TerminalCont(int signo)
148{
149  signal(SIGCONT, SIG_DFL);
150  prompt_Continue(SignalPrompt);
151}
152
153static void
154TerminalStop(int signo)
155{
156  prompt_Suspend(SignalPrompt);
157  signal(SIGCONT, TerminalCont);
158  raise(SIGSTOP);
159}
160
161static void
162BringDownServer(int signo)
163{
164  /* Drops all child prompts too ! */
165  if (server_Close(SignalBundle))
166    log_Printf(LogPHASE, "Closed server socket\n");
167}
168
169static void
170RestartServer(int signo)
171{
172  /* Drops all child prompts and re-opens the socket */
173  server_Reopen(SignalBundle);
174}
175
176static void
177Usage(void)
178{
179  fprintf(stderr, "Usage: ppp [-auto | -foreground | -background | -direct |"
180          " -dedicated | -ddial | -interactive]"
181#ifndef NOALIAS
182          " [-nat]"
183#endif
184          " [-quiet] [-unit N] [system ...]\n");
185  exit(EX_START);
186}
187
188struct switches {
189  unsigned nat : 1;
190  unsigned fg : 1;
191  unsigned quiet : 1;
192  int mode;
193  int unit;
194};
195
196static int
197ProcessArgs(int argc, char **argv, struct switches *sw)
198{
199  int optc, newmode, arg;
200  char *cp;
201
202  optc = 0;
203  memset(sw, '\0', sizeof *sw);
204  sw->mode = PHYS_INTERACTIVE;
205  sw->unit = -1;
206
207  for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) {
208    cp = argv[arg] + 1;
209    newmode = Nam2mode(cp);
210    switch (newmode) {
211      case PHYS_NONE:
212        if (strcmp(cp, "nat") == 0) {
213#ifdef NONAT
214          log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
215#else
216          sw->nat = 1;
217#endif
218          optc--;			/* this option isn't exclusive */
219        } else if (strcmp(cp, "alias") == 0) {
220#ifdef NONAT
221          log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
222          fprintf(stderr, "%s ignored: NAT is compiled out\n", argv[arg]);
223#else
224          log_Printf(LogWARN, "%s is deprecated\n", argv[arg]);
225          fprintf(stderr, "%s is deprecated\n", argv[arg]);
226          sw->nat = 1;
227#endif
228          optc--;			/* this option isn't exclusive */
229        } else if (strncmp(cp, "unit", 4) == 0) {
230          optc--;			/* this option isn't exclusive */
231          if (cp[4] == '\0') {
232            optc--;			/* nor is the argument */
233            if (++arg == argc) {
234              fprintf(stderr, "-unit: Expected unit number\n");
235              Usage();
236            } else
237              sw->unit = atoi(argv[arg]);
238          } else
239            sw->unit = atoi(cp + 4);
240        } else if (strcmp(cp, "quiet") == 0) {
241          sw->quiet = 1;
242          optc--;			/* this option isn't exclusive */
243        } else
244          Usage();
245        break;
246
247      case PHYS_ALL:
248        Usage();
249        break;
250
251      default:
252        sw->mode = newmode;
253        if (newmode == PHYS_FOREGROUND)
254          sw->fg = 1;
255    }
256  }
257
258  if (optc > 1) {
259    fprintf(stderr, "You may specify only one mode.\n");
260    exit(EX_START);
261  }
262
263  if (sw->mode == PHYS_AUTO && arg == argc) {
264    fprintf(stderr, "A system must be specified in auto mode.\n");
265    exit(EX_START);
266  }
267
268  return arg;		/* Don't SetLabel yet ! */
269}
270
271static void
272CheckLabel(const char *label, struct prompt *prompt, int mode)
273{
274  const char *err;
275
276  if ((err = system_IsValid(label, prompt, mode)) != NULL) {
277    fprintf(stderr, "%s: %s\n", label, err);
278    if (mode == PHYS_DIRECT)
279      log_Printf(LogWARN, "Label %s rejected -direct connection: %s\n",
280                 label, err);
281    log_Close();
282    exit(1);
283  }
284}
285
286
287int
288main(int argc, char **argv)
289{
290  char *name;
291  const char *lastlabel;
292  int label, arg;
293  struct bundle *bundle;
294  struct prompt *prompt;
295  struct switches sw;
296
297  name = strrchr(argv[0], '/');
298  log_Open(name ? name + 1 : argv[0]);
299
300#ifndef NONAT
301  PacketAliasInit();
302#endif
303  label = ProcessArgs(argc, argv, &sw);
304
305  /*
306   * A FreeBSD & OpenBSD hack to dodge a bug in the tty driver that drops
307   * output occasionally.... I must find the real reason some time.  To
308   * display the dodgy behaviour, comment out this bit, make yourself a large
309   * routing table and then run ppp in interactive mode.  The `show route'
310   * command will drop chunks of data !!!
311   */
312  if (sw.mode == PHYS_INTERACTIVE) {
313    close(STDIN_FILENO);
314    if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) {
315      fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY);
316      return 2;
317    }
318  }
319
320  /* Allow output for the moment (except in direct mode) */
321  if (sw.mode == PHYS_DIRECT)
322    prompt = NULL;
323  else
324    SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD);
325
326  ID0init();
327  if (ID0realuid() != 0) {
328    char conf[200], *ptr;
329
330    snprintf(conf, sizeof conf, "%s/%s", PPP_CONFDIR, CONFFILE);
331    do {
332      struct stat sb;
333
334      if (stat(conf, &sb) == 0 && sb.st_mode & S_IWOTH) {
335        log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n",
336                   conf);
337        return -1;
338      }
339      ptr = conf + strlen(conf)-2;
340      while (ptr > conf && *ptr != '/')
341        *ptr-- = '\0';
342    } while (ptr >= conf);
343  }
344
345  if (label < argc)
346    for (arg = label; arg < argc; arg++)
347      CheckLabel(argv[arg], prompt, sw.mode);
348  else
349    CheckLabel("default", prompt, sw.mode);
350
351  if (!sw.quiet)
352    prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(sw.mode));
353
354  if ((bundle = bundle_Create(TUN_PREFIX, sw.mode, sw.unit)) == NULL)
355    return EX_START;
356
357  /* NOTE:  We may now have changed argv[1] via a ``set proctitle'' */
358
359  if (prompt) {
360    prompt->bundle = bundle;	/* couldn't do it earlier */
361    if (!sw.quiet)
362      prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name);
363  }
364  SignalBundle = bundle;
365  bundle->NatEnabled = sw.nat;
366  if (sw.nat)
367    bundle->cfg.opt |= OPT_IFACEALIAS;
368
369  if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0)
370    prompt_Printf(prompt, "Warning: No default entry found in config file.\n");
371
372  sig_signal(SIGHUP, CloseSession);
373  sig_signal(SIGTERM, CloseSession);
374  sig_signal(SIGINT, CloseConnection);
375  sig_signal(SIGQUIT, CloseSession);
376  sig_signal(SIGALRM, SIG_IGN);
377  signal(SIGPIPE, SIG_IGN);
378
379  if (sw.mode == PHYS_INTERACTIVE)
380    sig_signal(SIGTSTP, TerminalStop);
381
382  sig_signal(SIGUSR1, RestartServer);
383  sig_signal(SIGUSR2, BringDownServer);
384
385  lastlabel = argv[argc - 1];
386  for (arg = label; arg < argc; arg++) {
387    /* In case we use LABEL or ``set enddisc label'' */
388    bundle_SetLabel(bundle, lastlabel);
389    system_Select(bundle, argv[arg], CONFFILE, prompt, NULL);
390  }
391
392  if (label < argc)
393    /* In case the last label did a ``load'' */
394    bundle_SetLabel(bundle, lastlabel);
395
396  if (sw.mode == PHYS_AUTO &&
397      bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
398    prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address "
399                  "in auto mode.\n");
400    AbortProgram(EX_START);
401  }
402
403  if (sw.mode != PHYS_INTERACTIVE) {
404    if (sw.mode != PHYS_DIRECT) {
405      if (!sw.fg) {
406        int bgpipe[2];
407        pid_t bgpid;
408
409        if (sw.mode == PHYS_BACKGROUND && pipe(bgpipe)) {
410          log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
411	  AbortProgram(EX_SOCK);
412        }
413
414        bgpid = fork();
415        if (bgpid == -1) {
416	  log_Printf(LogERROR, "fork: %s\n", strerror(errno));
417	  AbortProgram(EX_SOCK);
418        }
419
420        if (bgpid) {
421	  char c = EX_NORMAL;
422          int ret;
423
424	  if (sw.mode == PHYS_BACKGROUND) {
425	    close(bgpipe[1]);
426	    BGPid = bgpid;
427            /* If we get a signal, kill the child */
428            signal(SIGHUP, KillChild);
429            signal(SIGTERM, KillChild);
430            signal(SIGINT, KillChild);
431            signal(SIGQUIT, KillChild);
432
433	    /* Wait for our child to close its pipe before we exit */
434            while ((ret = read(bgpipe[0], &c, 1)) == 1) {
435              switch (c) {
436                case EX_NORMAL:
437	          prompt_Printf(prompt, "PPP enabled\n");
438	          log_Printf(LogPHASE, "Parent: PPP enabled\n");
439	          break;
440                case EX_REDIAL:
441                  if (!sw.quiet)
442	            prompt_Printf(prompt, "Attempting redial\n");
443                  continue;
444                case EX_RECONNECT:
445                  if (!sw.quiet)
446	            prompt_Printf(prompt, "Attempting reconnect\n");
447                  continue;
448	        default:
449	          prompt_Printf(prompt, "Child failed (%s)\n",
450                                ex_desc((int)c));
451	          log_Printf(LogPHASE, "Parent: Child failed (%s)\n",
452		             ex_desc((int) c));
453	      }
454	      break;
455            }
456            if (ret != 1) {
457	      prompt_Printf(prompt, "Child exit, no status.\n");
458	      log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
459	    }
460	    close(bgpipe[0]);
461	  }
462	  return c;
463        } else if (sw.mode == PHYS_BACKGROUND) {
464	  close(bgpipe[0]);
465          bundle->notify.fd = bgpipe[1];
466        }
467
468        bundle_ChangedPID(bundle);
469        bundle_LockTun(bundle);	/* we have a new pid */
470      }
471
472      /* -auto, -dedicated, -ddial, -foreground & -background */
473      prompt_Destroy(prompt, 0);
474      close(STDOUT_FILENO);
475      close(STDERR_FILENO);
476      close(STDIN_FILENO);
477      if (!sw.fg)
478        setsid();
479    } else {
480      /* -direct - STDIN_FILENO gets used by physical_Open */
481      prompt_TtyInit(NULL);
482      close(STDOUT_FILENO);
483      close(STDERR_FILENO);
484    }
485  } else {
486    /* -interactive */
487    close(STDERR_FILENO);
488    prompt_TtyInit(prompt);
489    prompt_TtyCommandMode(prompt);
490    prompt_Required(prompt);
491  }
492
493  log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode));
494  DoLoop(bundle);
495  AbortProgram(EX_NORMAL);
496
497  return EX_NORMAL;
498}
499
500static void
501DoLoop(struct bundle *bundle)
502{
503  fd_set *rfds, *wfds, *efds;
504  int i, nfds, nothing_done;
505  struct probe probe;
506
507  probe_Init(&probe);
508
509  if ((rfds = mkfdset()) == NULL) {
510    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
511    return;
512  }
513
514  if ((wfds = mkfdset()) == NULL) {
515    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
516    free(rfds);
517    return;
518  }
519
520  if ((efds = mkfdset()) == NULL) {
521    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
522    free(rfds);
523    free(wfds);
524    return;
525  }
526
527  for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) {
528    nfds = 0;
529    zerofdset(rfds);
530    zerofdset(wfds);
531    zerofdset(efds);
532
533    /* All our datalinks, the tun device and the MP socket */
534    descriptor_UpdateSet(&bundle->desc, rfds, wfds, efds, &nfds);
535
536    /* All our prompts and the diagnostic socket */
537    descriptor_UpdateSet(&server.desc, rfds, NULL, NULL, &nfds);
538
539    bundle_CleanDatalinks(bundle);
540    if (bundle_IsDead(bundle))
541      /* Don't select - we'll be here forever */
542      break;
543
544    /*
545     * It's possible that we've had a signal since we last checked.  If
546     * we don't check again before calling select(), we may end up stuck
547     * after having missed the event.... sig_Handle() tries to be as
548     * quick as possible if nothing is likely to have happened.
549     * This is only really likely if we block in open(... O_NONBLOCK)
550     * which will happen with a misconfigured device.
551     */
552    if (sig_Handle())
553      continue;
554
555    i = select(nfds, rfds, wfds, efds, NULL);
556
557    if (i < 0 && errno != EINTR) {
558      log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
559      if (log_IsKept(LogTIMER)) {
560        struct timeval t;
561
562        for (i = 0; i <= nfds; i++) {
563          if (FD_ISSET(i, rfds)) {
564            log_Printf(LogTIMER, "Read set contains %d\n", i);
565            FD_CLR(i, rfds);
566            t.tv_sec = t.tv_usec = 0;
567            if (select(nfds, rfds, wfds, efds, &t) != -1) {
568              log_Printf(LogTIMER, "The culprit !\n");
569              break;
570            }
571          }
572          if (FD_ISSET(i, wfds)) {
573            log_Printf(LogTIMER, "Write set contains %d\n", i);
574            FD_CLR(i, wfds);
575            t.tv_sec = t.tv_usec = 0;
576            if (select(nfds, rfds, wfds, efds, &t) != -1) {
577              log_Printf(LogTIMER, "The culprit !\n");
578              break;
579            }
580          }
581          if (FD_ISSET(i, efds)) {
582            log_Printf(LogTIMER, "Error set contains %d\n", i);
583            FD_CLR(i, efds);
584            t.tv_sec = t.tv_usec = 0;
585            if (select(nfds, rfds, wfds, efds, &t) != -1) {
586              log_Printf(LogTIMER, "The culprit !\n");
587              break;
588            }
589          }
590        }
591      }
592      break;
593    }
594
595    log_Printf(LogTIMER, "Select returns %d\n", i);
596
597    sig_Handle();
598
599    if (i <= 0)
600      continue;
601
602    for (i = 0; i <= nfds; i++)
603      if (FD_ISSET(i, efds)) {
604        log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i);
605        /* We deal gracefully with link descriptor exceptions */
606        if (!bundle_Exception(bundle, i)) {
607          log_Printf(LogERROR, "Exception cannot be handled !\n");
608          break;
609        }
610      }
611
612    if (i <= nfds)
613      break;
614
615    nothing_done = 1;
616
617    if (descriptor_IsSet(&server.desc, rfds)) {
618      descriptor_Read(&server.desc, bundle, rfds);
619      nothing_done = 0;
620    }
621
622    if (descriptor_IsSet(&bundle->desc, rfds)) {
623      descriptor_Read(&bundle->desc, bundle, rfds);
624      nothing_done = 0;
625    }
626
627    if (descriptor_IsSet(&bundle->desc, wfds))
628      if (!descriptor_Write(&bundle->desc, bundle, wfds) && nothing_done) {
629        /*
630         * This is disasterous.  The OS has told us that something is
631         * writable, and all our write()s have failed.  Rather than
632         * going back immediately to do our UpdateSet()s and select(),
633         * we sleep for a bit to avoid gobbling up all cpu time.
634         */
635        struct timeval t;
636
637        t.tv_sec = 0;
638        t.tv_usec = 100000;
639        select(0, NULL, NULL, NULL, &t);
640      }
641  }
642
643  log_Printf(LogDEBUG, "DoLoop done.\n");
644}
645