xref: /freebsd/usr.sbin/ppp/main.c (revision 85602e5267f413a849370d3c6fe3d0a87cdd5b49)
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  * $Id: main.c,v 1.121.2.46 1998/04/07 00:54:07 brian Exp $
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 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <paths.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <termios.h>
37 #include <unistd.h>
38 
39 #include "mbuf.h"
40 #include "log.h"
41 #include "defs.h"
42 #include "id.h"
43 #include "timer.h"
44 #include "fsm.h"
45 #include "lqr.h"
46 #include "hdlc.h"
47 #include "lcp.h"
48 #include "ccp.h"
49 #include "iplist.h"
50 #include "throughput.h"
51 #include "slcompress.h"
52 #include "ipcp.h"
53 #include "filter.h"
54 #include "descriptor.h"
55 #include "link.h"
56 #include "mp.h"
57 #include "bundle.h"
58 #include "loadalias.h"
59 #include "vars.h"
60 #include "auth.h"
61 #include "systems.h"
62 #include "ip.h"
63 #include "sig.h"
64 #include "main.h"
65 #include "pathnames.h"
66 #include "tun.h"
67 #include "server.h"
68 #include "prompt.h"
69 #include "chat.h"
70 #include "chap.h"
71 #include "datalink.h"
72 
73 #ifndef O_NONBLOCK
74 #ifdef O_NDELAY
75 #define	O_NONBLOCK O_NDELAY
76 #endif
77 #endif
78 
79 static char pid_filename[MAXPATHLEN];
80 
81 static void DoLoop(struct bundle *, struct prompt *);
82 static void TerminalStop(int);
83 static const char *ex_desc(int);
84 
85 static struct bundle *SignalBundle;
86 static struct prompt *SignalPrompt;
87 
88 void
89 Cleanup(int excode)
90 {
91   SignalBundle->CleaningUp = 1;
92   if (bundle_Phase(SignalBundle) != PHASE_DEAD)
93     bundle_Close(SignalBundle, NULL, 1);
94 }
95 
96 void
97 AbortProgram(int excode)
98 {
99   ServerClose(SignalBundle);
100   ID0unlink(pid_filename);
101   LogPrintf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
102   bundle_Close(SignalBundle, NULL, 1);
103   bundle_Destroy(SignalBundle);
104   LogClose();
105   exit(excode);
106 }
107 
108 static void
109 CloseConnection(int signo)
110 {
111   /* NOTE, these are manual, we've done a setsid() */
112   struct datalink *dl;
113 
114   pending_signal(SIGINT, SIG_IGN);
115   LogPrintf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo);
116   for (dl = SignalBundle->links; dl; dl = dl->next)
117     datalink_Down(dl, 1);
118   pending_signal(SIGINT, CloseConnection);
119 }
120 
121 static void
122 CloseSession(int signo)
123 {
124   LogPrintf(LogPHASE, "Signal %d, terminate.\n", signo);
125   Cleanup(EX_TERM);
126 }
127 
128 static pid_t BGPid = 0;
129 
130 static void
131 KillChild(int signo)
132 {
133   LogPrintf(LogPHASE, "Parent: Signal %d\n", signo);
134   kill(BGPid, SIGINT);
135 }
136 
137 static void
138 TerminalCont(int signo)
139 {
140   signal(SIGCONT, SIG_DFL);
141   prompt_Continue(SignalPrompt);
142 }
143 
144 static void
145 TerminalStop(int signo)
146 {
147   prompt_Suspend(SignalPrompt);
148   signal(SIGCONT, TerminalCont);
149   raise(SIGSTOP);
150 }
151 
152 #if 0 /* What's our passwd :-O */
153 static void
154 SetUpServer(int signo)
155 {
156   int res;
157 
158   VarHaveLocalAuthKey = 0;
159   LocalAuthInit();
160   if ((res = ServerTcpOpen(SERVER_PORT + SignalBundle->unit)) != 0)
161     LogPrintf(LogERROR, "SIGUSR1: Failed %d to open port %d\n",
162 	      res, SERVER_PORT + SignalBundle->unit);
163 }
164 #endif
165 
166 static void
167 BringDownServer(int signo)
168 {
169   /* Drops all child prompts too ! */
170   ServerClose(SignalBundle);
171 }
172 
173 static const char *
174 ex_desc(int ex)
175 {
176   static char num[12];
177   static const char *desc[] = {
178     "normal", "start", "sock", "modem", "dial", "dead", "done",
179     "reboot", "errdead", "hangup", "term", "nodial", "nologin"
180   };
181 
182   if (ex >= 0 && ex < sizeof desc / sizeof *desc)
183     return desc[ex];
184   snprintf(num, sizeof num, "%d", ex);
185   return num;
186 }
187 
188 static void
189 Usage(void)
190 {
191   fprintf(stderr,
192 	  "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ]"
193 #ifndef NOALIAS
194           " [ -alias ]"
195 #endif
196           " [system]\n");
197   exit(EX_START);
198 }
199 
200 static char *
201 ProcessArgs(int argc, char **argv)
202 {
203   int optc;
204   char *cp;
205 
206   optc = 0;
207   mode = MODE_INTER;
208   while (argc > 0 && **argv == '-') {
209     cp = *argv + 1;
210     if (strcmp(cp, "auto") == 0) {
211       mode |= MODE_AUTO;
212       mode &= ~MODE_INTER;
213     } else if (strcmp(cp, "background") == 0) {
214       mode |= MODE_BACKGROUND;
215       mode &= ~MODE_INTER;
216     } else if (strcmp(cp, "direct") == 0) {
217       mode |= MODE_DIRECT;
218       mode &= ~MODE_INTER;
219     } else if (strcmp(cp, "dedicated") == 0) {
220       mode |= MODE_DEDICATED;
221       mode &= ~MODE_INTER;
222     } else if (strcmp(cp, "ddial") == 0) {
223       mode |= MODE_DDIAL;
224       mode &= ~MODE_INTER;
225 #ifndef NOALIAS
226     } else if (strcmp(cp, "alias") == 0) {
227       if (loadAliasHandlers() != 0)
228 	LogPrintf(LogWARN, "Cannot load alias library\n");
229       optc--;			/* this option isn't exclusive */
230 #endif
231     } else
232       Usage();
233     optc++;
234     argv++;
235     argc--;
236   }
237   if (argc > 1) {
238     fprintf(stderr, "You may specify only one system label.\n");
239     exit(EX_START);
240   }
241 
242   if (optc > 1) {
243     fprintf(stderr, "You may specify only one mode.\n");
244     exit(EX_START);
245   }
246 
247   return argc == 1 ? *argv : NULL;	/* Don't SetLabel yet ! */
248 }
249 
250 int
251 main(int argc, char **argv)
252 {
253   FILE *lockfile;
254   char *name, *label;
255   int nfds;
256   struct bundle *bundle;
257   struct prompt *prompt;
258 
259   nfds = getdtablesize();
260   if (nfds >= FD_SETSIZE)
261     /*
262      * If we've got loads of file descriptors, make sure they're all
263      * closed.  If they aren't, we may end up with a seg fault when our
264      * `fd_set's get too big when select()ing !
265      */
266     while (--nfds > 2)
267       close(nfds);
268 
269   name = strrchr(argv[0], '/');
270   LogOpen(name ? name + 1 : argv[0]);
271 
272   argc--;
273   argv++;
274   label = ProcessArgs(argc, argv);
275 
276 #ifdef __FreeBSD__
277   /*
278    * A FreeBSD hack to dodge a bug in the tty driver that drops output
279    * occasionally.... I must find the real reason some time.  To display
280    * the dodgy behaviour, comment out this bit, make yourself a large
281    * routing table and then run ppp in interactive mode.  The `show route'
282    * command will drop chunks of data !!!
283    */
284   if (mode & MODE_INTER) {
285     close(STDIN_FILENO);
286     if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) {
287       fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY);
288       return 2;
289     }
290   }
291 #endif
292 
293   /* Allow output for the moment (except in direct mode) */
294   if (mode & MODE_DIRECT)
295     prompt = NULL;
296   else {
297     const char *m;
298 
299     SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD);
300     if (mode & MODE_DDIAL)
301       m = "direct dial";
302     else if (mode & MODE_BACKGROUND)
303       m = "background";
304     else if (mode & MODE_AUTO)
305       m = "auto";
306     else if (mode & MODE_DEDICATED)
307       m = "dedicated";
308     else if (mode & MODE_INTER)
309       m = "interactive";
310     else
311       m = NULL;
312 
313     if (m)
314       prompt_Printf(prompt, "Working in %s mode\n", m);
315   }
316 
317   ID0init();
318   if (ID0realuid() != 0) {
319     char conf[200], *ptr;
320 
321     snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE);
322     do {
323       if (!access(conf, W_OK)) {
324         LogPrintf(LogALERT, "ppp: Access violation: Please protect %s\n", conf);
325         return -1;
326       }
327       ptr = conf + strlen(conf)-2;
328       while (ptr > conf && *ptr != '/')
329         *ptr-- = '\0';
330     } while (ptr >= conf);
331   }
332 
333   if (!ValidSystem(label, prompt)) {
334     fprintf(stderr, "You may not use ppp in this mode with this label\n");
335     if (mode & MODE_DIRECT) {
336       const char *l;
337       l = label ? label : "default";
338       LogPrintf(LogWARN, "Label %s rejected -direct connection\n", l);
339     }
340     LogClose();
341     return 1;
342   }
343 
344   if ((bundle = bundle_Create("/dev/tun", prompt)) == NULL) {
345     LogPrintf(LogWARN, "bundle_Create: %s\n", strerror(errno));
346     return EX_START;
347   }
348   SignalBundle = bundle;
349 
350   if (SelectSystem(bundle, "default", CONFFILE, prompt) < 0)
351     prompt_Printf(prompt,
352                   "Warning: No default entry is given in config file.\n");
353 
354   if ((mode & MODE_OUTGOING_DAEMON) && !(mode & MODE_DEDICATED))
355     if (label == NULL) {
356       prompt_Printf(prompt, "Destination system must be specified in"
357 		    " auto, background or ddial mode.\n");
358       return EX_START;
359     }
360 
361   pending_signal(SIGHUP, CloseSession);
362   pending_signal(SIGTERM, CloseSession);
363   pending_signal(SIGINT, CloseConnection);
364   pending_signal(SIGQUIT, CloseSession);
365   pending_signal(SIGALRM, SIG_IGN);
366   signal(SIGPIPE, SIG_IGN);
367   if (mode & MODE_INTER)
368     pending_signal(SIGTSTP, TerminalStop);
369 
370 #if 0 /* What's our passwd :-O */
371   pending_signal(SIGUSR1, SetUpServer);
372 #endif
373   pending_signal(SIGUSR2, BringDownServer);
374 
375   if (label) {
376     if (SelectSystem(bundle, label, CONFFILE, prompt) < 0) {
377       prompt_Printf(prompt, "Destination system (%s) not found.\n", label);
378       AbortProgram(EX_START);
379     }
380     /*
381      * We don't SetLabel() 'till now in case SelectSystem() has an
382      * embeded load "otherlabel" command.
383      */
384     SetLabel(label);
385     if (mode & MODE_AUTO &&
386 	bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
387       prompt_Printf(prompt, "You must \"set ifaddr\" in label %s for "
388                     "auto mode.\n", label);
389       AbortProgram(EX_START);
390     }
391   }
392 
393   if (mode & MODE_DAEMON) {
394     if (!(mode & MODE_DIRECT)) {
395       int bgpipe[2];
396       pid_t bgpid;
397 
398       if (mode & MODE_BACKGROUND && pipe(bgpipe)) {
399         LogPrintf(LogERROR, "pipe: %s\n", strerror(errno));
400 	AbortProgram(EX_SOCK);
401       }
402 
403       bgpid = fork();
404       if (bgpid == -1) {
405 	LogPrintf(LogERROR, "fork: %s\n", strerror(errno));
406 	AbortProgram(EX_SOCK);
407       }
408 
409       if (bgpid) {
410 	char c = EX_NORMAL;
411 
412 	if (mode & MODE_BACKGROUND) {
413 	  close(bgpipe[1]);
414 	  BGPid = bgpid;
415           /* If we get a signal, kill the child */
416           signal(SIGHUP, KillChild);
417           signal(SIGTERM, KillChild);
418           signal(SIGINT, KillChild);
419           signal(SIGQUIT, KillChild);
420 
421 	  /* Wait for our child to close its pipe before we exit */
422 	  if (read(bgpipe[0], &c, 1) != 1) {
423 	    prompt_Printf(prompt, "Child exit, no status.\n");
424 	    LogPrintf(LogPHASE, "Parent: Child exit, no status.\n");
425 	  } else if (c == EX_NORMAL) {
426 	    prompt_Printf(prompt, "PPP enabled.\n");
427 	    LogPrintf(LogPHASE, "Parent: PPP enabled.\n");
428 	  } else {
429 	    prompt_Printf(prompt, "Child failed (%s).\n", ex_desc((int) c));
430 	    LogPrintf(LogPHASE, "Parent: Child failed (%s).\n",
431 		      ex_desc((int) c));
432 	  }
433 	  close(bgpipe[0]);
434 	}
435 	return c;
436       } else if (mode & MODE_BACKGROUND) {
437 	close(bgpipe[0]);
438         bundle->notify.fd = bgpipe[1];
439       }
440     }
441 
442     if (mode & MODE_DIRECT) {
443       /* STDIN_FILENO gets used by modem_Open in DIRECT mode */
444       prompt = prompt_Create(NULL, bundle, PROMPT_STD);
445       prompt_TtyInit(prompt, PROMPT_DONT_WANT_INT);
446       prompt_DestroyUnclean(prompt);
447       close(STDOUT_FILENO);
448       close(STDERR_FILENO);
449     } else if (mode & MODE_DAEMON) {
450       prompt_Destroy(prompt, 0);
451       close(STDOUT_FILENO);
452       close(STDERR_FILENO);
453       close(STDIN_FILENO);
454       setsid();
455     }
456   } else {
457     close(STDERR_FILENO);
458     prompt_TtyInit(prompt, PROMPT_WANT_INT);
459     prompt_TtyCommandMode(prompt);
460     prompt_Required(prompt);
461   }
462 
463   snprintf(pid_filename, sizeof pid_filename, "%stun%d.pid",
464            _PATH_VARRUN, bundle->unit);
465   lockfile = ID0fopen(pid_filename, "w");
466   if (lockfile != NULL) {
467     fprintf(lockfile, "%d\n", (int) getpid());
468     fclose(lockfile);
469   }
470 #ifndef RELEASE_CRUNCH
471   else
472     LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
473               pid_filename, strerror(errno));
474 #endif
475 
476   LogPrintf(LogPHASE, "PPP Started.\n");
477 
478 
479   do
480     DoLoop(bundle, prompt);
481   while (mode & MODE_DEDICATED);
482 
483   AbortProgram(EX_NORMAL);
484 
485   return EX_NORMAL;
486 }
487 
488 static void
489 DoLoop(struct bundle *bundle, struct prompt *prompt)
490 {
491   fd_set rfds, wfds, efds;
492   int pri, i, n, nfds;
493   int qlen;
494   struct tun_data tun;
495 
496   if (mode & (MODE_DIRECT|MODE_DEDICATED|MODE_BACKGROUND))
497     bundle_Open(bundle, NULL);
498 
499   while (!bundle_IsDead(bundle)) {
500     nfds = 0;
501     FD_ZERO(&rfds);
502     FD_ZERO(&wfds);
503     FD_ZERO(&efds);
504 
505     qlen = bundle_FillQueues(bundle);
506 
507     handle_signals();
508 
509     descriptor_UpdateSet(&bundle->desc, &rfds, &wfds, &efds, &nfds);
510     descriptor_UpdateSet(&server.desc, &rfds, &wfds, &efds, &nfds);
511 
512     /* If there are aren't many packets queued, look for some more. */
513     if (qlen < 20 && bundle->tun_fd >= 0) {
514       if (bundle->tun_fd + 1 > nfds)
515 	nfds = bundle->tun_fd + 1;
516       FD_SET(bundle->tun_fd, &rfds);
517     }
518 
519     if (bundle_IsDead(bundle))
520       /* Don't select - we'll be here forever */
521       break;
522 
523     i = select(nfds, &rfds, &wfds, &efds, NULL);
524 
525     if (i == 0)
526       continue;
527 
528     if (i < 0) {
529       if (errno == EINTR) {
530 	handle_signals();
531 	continue;
532       }
533       LogPrintf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
534       break;
535     }
536 
537     for (i = 0; i <= nfds; i++)
538       if (FD_ISSET(i, &efds)) {
539         LogPrintf(LogALERT, "Exception detected on descriptor %d\n", i);
540         break;
541       }
542 
543     if (descriptor_IsSet(&server.desc, &rfds))
544       descriptor_Read(&server.desc, bundle, &rfds);
545 
546     if (descriptor_IsSet(&bundle->desc, &wfds))
547       descriptor_Write(&bundle->desc, bundle, &wfds);
548 
549     if (descriptor_IsSet(&bundle->desc, &rfds))
550       descriptor_Read(&bundle->desc, bundle, &rfds);
551 
552     if (bundle->tun_fd >= 0 && FD_ISSET(bundle->tun_fd, &rfds)) {
553       /* something to read from tun */
554       n = read(bundle->tun_fd, &tun, sizeof tun);
555       if (n < 0) {
556 	LogPrintf(LogERROR, "read from tun: %s\n", strerror(errno));
557 	continue;
558       }
559       n -= sizeof tun - sizeof tun.data;
560       if (n <= 0) {
561 	LogPrintf(LogERROR, "read from tun: Only %d bytes read\n", n);
562 	continue;
563       }
564       if (!tun_check_header(tun, AF_INET))
565           continue;
566       if (((struct ip *)tun.data)->ip_dst.s_addr ==
567           bundle->ncp.ipcp.my_ip.s_addr) {
568 	/* we've been asked to send something addressed *to* us :( */
569 	if (Enabled(ConfLoopback)) {
570 	  pri = PacketCheck(bundle, tun.data, n, &bundle->filter.in);
571 	  if (pri >= 0) {
572 	    struct mbuf *bp;
573 
574 #ifndef NOALIAS
575             if (AliasEnabled()) {
576 	      (*PacketAlias.In)(tun.data, sizeof tun.data);
577 	      n = ntohs(((struct ip *)tun.data)->ip_len);
578 	    }
579 #endif
580 	    bp = mballoc(n, MB_IPIN);
581 	    memcpy(MBUF_CTOP(bp), tun.data, n);
582 	    IpInput(bundle, bp);
583 	    LogPrintf(LogDEBUG, "Looped back packet addressed to myself\n");
584 	  }
585 	  continue;
586 	} else
587 	  LogPrintf(LogDEBUG, "Oops - forwarding packet addressed to myself\n");
588       }
589 
590       /*
591        * Process on-demand dialup. Output packets are queued within tunnel
592        * device until IPCP is opened.
593        */
594       if (bundle_Phase(bundle) == PHASE_DEAD)
595         /*
596          * Note, we must be in AUTO mode :-/ otherwise our interface should
597          * *not* be UP and we can't receive data
598          */
599         if ((mode & MODE_AUTO) &&
600             (pri = PacketCheck(bundle, tun.data, n, &bundle->filter.dial)) >= 0)
601           bundle_Open(bundle, NULL);
602         else
603           /*
604            * Drop the packet.  If we were to queue it, we'd just end up with
605            * a pile of timed-out data in our output queue by the time we get
606            * around to actually dialing.  We'd also prematurely reach the
607            * threshold at which we stop select()ing to read() the tun
608            * device - breaking auto-dial.
609            */
610           continue;
611 
612       pri = PacketCheck(bundle, tun.data, n, &bundle->filter.out);
613       if (pri >= 0) {
614 #ifndef NOALIAS
615         if (AliasEnabled()) {
616 	  (*PacketAlias.Out)(tun.data, sizeof tun.data);
617 	  n = ntohs(((struct ip *)tun.data)->ip_len);
618 	}
619 #endif
620 	IpEnqueue(pri, tun.data, n);
621       }
622     }
623   }
624   LogPrintf(LogDEBUG, "Job (DoLoop) done.\n");
625 }
626