main.c revision 75120
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 75120 2001-04-03 08:20:20Z 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                  if (!sw.quiet) {
438	            prompt_Printf(prompt, "PPP enabled\n");
439	            log_Printf(LogPHASE, "Parent: PPP enabled\n");
440                  }
441	          break;
442                case EX_REDIAL:
443                  if (!sw.quiet)
444	            prompt_Printf(prompt, "Attempting redial\n");
445                  continue;
446                case EX_RECONNECT:
447                  if (!sw.quiet)
448	            prompt_Printf(prompt, "Attempting reconnect\n");
449                  continue;
450	        default:
451	          prompt_Printf(prompt, "Child failed (%s)\n",
452                                ex_desc((int)c));
453	          log_Printf(LogPHASE, "Parent: Child failed (%s)\n",
454		             ex_desc((int) c));
455	      }
456	      break;
457            }
458            if (ret != 1) {
459	      prompt_Printf(prompt, "Child exit, no status.\n");
460	      log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
461	    }
462	    close(bgpipe[0]);
463	  }
464	  return c;
465        } else if (sw.mode == PHYS_BACKGROUND) {
466	  close(bgpipe[0]);
467          bundle->notify.fd = bgpipe[1];
468        }
469
470        bundle_ChangedPID(bundle);
471        bundle_LockTun(bundle);	/* we have a new pid */
472      }
473
474      /* -auto, -dedicated, -ddial, -foreground & -background */
475      prompt_Destroy(prompt, 0);
476      close(STDOUT_FILENO);
477      close(STDERR_FILENO);
478      close(STDIN_FILENO);
479      if (!sw.fg)
480        setsid();
481    } else {
482      /* -direct - STDIN_FILENO gets used by physical_Open */
483      prompt_TtyInit(NULL);
484      close(STDOUT_FILENO);
485      close(STDERR_FILENO);
486    }
487  } else {
488    /* -interactive */
489    close(STDERR_FILENO);
490    prompt_TtyInit(prompt);
491    prompt_TtyCommandMode(prompt);
492    prompt_Required(prompt);
493  }
494
495  log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode));
496  DoLoop(bundle);
497  AbortProgram(EX_NORMAL);
498
499  return EX_NORMAL;
500}
501
502static void
503DoLoop(struct bundle *bundle)
504{
505  fd_set *rfds, *wfds, *efds;
506  int i, nfds, nothing_done;
507  struct probe probe;
508
509  probe_Init(&probe);
510
511  if ((rfds = mkfdset()) == NULL) {
512    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
513    return;
514  }
515
516  if ((wfds = mkfdset()) == NULL) {
517    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
518    free(rfds);
519    return;
520  }
521
522  if ((efds = mkfdset()) == NULL) {
523    log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
524    free(rfds);
525    free(wfds);
526    return;
527  }
528
529  for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) {
530    nfds = 0;
531    zerofdset(rfds);
532    zerofdset(wfds);
533    zerofdset(efds);
534
535    /* All our datalinks, the tun device and the MP socket */
536    descriptor_UpdateSet(&bundle->desc, rfds, wfds, efds, &nfds);
537
538    /* All our prompts and the diagnostic socket */
539    descriptor_UpdateSet(&server.desc, rfds, NULL, NULL, &nfds);
540
541    bundle_CleanDatalinks(bundle);
542    if (bundle_IsDead(bundle))
543      /* Don't select - we'll be here forever */
544      break;
545
546    /*
547     * It's possible that we've had a signal since we last checked.  If
548     * we don't check again before calling select(), we may end up stuck
549     * after having missed the event.... sig_Handle() tries to be as
550     * quick as possible if nothing is likely to have happened.
551     * This is only really likely if we block in open(... O_NONBLOCK)
552     * which will happen with a misconfigured device.
553     */
554    if (sig_Handle())
555      continue;
556
557    i = select(nfds, rfds, wfds, efds, NULL);
558
559    if (i < 0 && errno != EINTR) {
560      log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
561      if (log_IsKept(LogTIMER)) {
562        struct timeval t;
563
564        for (i = 0; i <= nfds; i++) {
565          if (FD_ISSET(i, rfds)) {
566            log_Printf(LogTIMER, "Read set contains %d\n", i);
567            FD_CLR(i, rfds);
568            t.tv_sec = t.tv_usec = 0;
569            if (select(nfds, rfds, wfds, efds, &t) != -1) {
570              log_Printf(LogTIMER, "The culprit !\n");
571              break;
572            }
573          }
574          if (FD_ISSET(i, wfds)) {
575            log_Printf(LogTIMER, "Write set contains %d\n", i);
576            FD_CLR(i, wfds);
577            t.tv_sec = t.tv_usec = 0;
578            if (select(nfds, rfds, wfds, efds, &t) != -1) {
579              log_Printf(LogTIMER, "The culprit !\n");
580              break;
581            }
582          }
583          if (FD_ISSET(i, efds)) {
584            log_Printf(LogTIMER, "Error set contains %d\n", i);
585            FD_CLR(i, efds);
586            t.tv_sec = t.tv_usec = 0;
587            if (select(nfds, rfds, wfds, efds, &t) != -1) {
588              log_Printf(LogTIMER, "The culprit !\n");
589              break;
590            }
591          }
592        }
593      }
594      break;
595    }
596
597    log_Printf(LogTIMER, "Select returns %d\n", i);
598
599    sig_Handle();
600
601    if (i <= 0)
602      continue;
603
604    for (i = 0; i <= nfds; i++)
605      if (FD_ISSET(i, efds)) {
606        log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i);
607        /* We deal gracefully with link descriptor exceptions */
608        if (!bundle_Exception(bundle, i)) {
609          log_Printf(LogERROR, "Exception cannot be handled !\n");
610          break;
611        }
612      }
613
614    if (i <= nfds)
615      break;
616
617    nothing_done = 1;
618
619    if (descriptor_IsSet(&server.desc, rfds)) {
620      descriptor_Read(&server.desc, bundle, rfds);
621      nothing_done = 0;
622    }
623
624    if (descriptor_IsSet(&bundle->desc, rfds)) {
625      descriptor_Read(&bundle->desc, bundle, rfds);
626      nothing_done = 0;
627    }
628
629    if (descriptor_IsSet(&bundle->desc, wfds))
630      if (!descriptor_Write(&bundle->desc, bundle, wfds) && nothing_done) {
631        /*
632         * This is disasterous.  The OS has told us that something is
633         * writable, and all our write()s have failed.  Rather than
634         * going back immediately to do our UpdateSet()s and select(),
635         * we sleep for a bit to avoid gobbling up all cpu time.
636         */
637        struct timeval t;
638
639        t.tv_sec = 0;
640        t.tv_usec = 100000;
641        select(0, NULL, NULL, NULL, &t);
642      }
643  }
644
645  log_Printf(LogDEBUG, "DoLoop done.\n");
646}
647