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$ 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 95 static void DoLoop(struct bundle *); 96 static void TerminalStop(int); 97 98 static struct bundle *SignalBundle; 99 static struct prompt *SignalPrompt; 100 101 void 102 Cleanup(int excode) 103 { 104 SignalBundle->CleaningUp = 1; 105 bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN); 106 } 107 108 void 109 AbortProgram(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 119 static void 120 CloseConnection(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 129 static void 130 CloseSession(int signo) 131 { 132 log_Printf(LogPHASE, "Signal %d, terminate.\n", signo); 133 Cleanup(EX_TERM); 134 } 135 136 static pid_t BGPid = 0; 137 138 static void 139 KillChild(int signo) 140 { 141 signal(signo, SIG_IGN); 142 log_Printf(LogPHASE, "Parent: Signal %d\n", signo); 143 kill(BGPid, SIGINT); 144 } 145 146 static void 147 TerminalCont(int signo) 148 { 149 signal(SIGCONT, SIG_DFL); 150 prompt_Continue(SignalPrompt); 151 } 152 153 static void 154 TerminalStop(int signo) 155 { 156 prompt_Suspend(SignalPrompt); 157 signal(SIGCONT, TerminalCont); 158 raise(SIGSTOP); 159 } 160 161 static void 162 BringDownServer(int signo) 163 { 164 /* Drops all child prompts too ! */ 165 server_Close(SignalBundle); 166 } 167 168 static void 169 Usage(void) 170 { 171 fprintf(stderr, 172 "Usage: ppp [-auto | -foreground | -background | -direct | -dedicated | -ddial | -interactive]" 173 #ifndef NOALIAS 174 " [-nat]" 175 #endif 176 " [-quiet] [-unit N] [system ...]\n"); 177 exit(EX_START); 178 } 179 180 struct switches { 181 unsigned nat : 1; 182 unsigned fg : 1; 183 unsigned quiet : 1; 184 int mode; 185 int unit; 186 }; 187 188 static int 189 ProcessArgs(int argc, char **argv, struct switches *sw) 190 { 191 int optc, newmode, arg; 192 char *cp; 193 194 optc = 0; 195 memset(sw, '\0', sizeof *sw); 196 sw->mode = PHYS_INTERACTIVE; 197 sw->unit = -1; 198 199 for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) { 200 cp = argv[arg] + 1; 201 newmode = Nam2mode(cp); 202 switch (newmode) { 203 case PHYS_NONE: 204 if (strcmp(cp, "nat") == 0) { 205 #ifdef NONAT 206 log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]); 207 #else 208 sw->nat = 1; 209 #endif 210 optc--; /* this option isn't exclusive */ 211 } else if (strcmp(cp, "alias") == 0) { 212 #ifdef NONAT 213 log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]); 214 fprintf(stderr, "%s ignored: NAT is compiled out\n", argv[arg]); 215 #else 216 log_Printf(LogWARN, "%s is deprecated\n", argv[arg]); 217 fprintf(stderr, "%s is deprecated\n", argv[arg]); 218 sw->nat = 1; 219 #endif 220 optc--; /* this option isn't exclusive */ 221 } else if (strncmp(cp, "unit", 4) == 0) { 222 optc--; /* this option isn't exclusive */ 223 if (cp[4] == '\0') { 224 optc--; /* nor is the argument */ 225 if (++arg == argc) { 226 fprintf(stderr, "-unit: Expected unit number\n"); 227 Usage(); 228 } else 229 sw->unit = atoi(argv[arg]); 230 } else 231 sw->unit = atoi(cp + 4); 232 } else if (strcmp(cp, "quiet") == 0) { 233 sw->quiet = 1; 234 optc--; /* this option isn't exclusive */ 235 } else 236 Usage(); 237 break; 238 239 case PHYS_ALL: 240 Usage(); 241 break; 242 243 default: 244 sw->mode = newmode; 245 if (newmode == PHYS_FOREGROUND) 246 sw->fg = 1; 247 } 248 } 249 250 if (optc > 1) { 251 fprintf(stderr, "You may specify only one mode.\n"); 252 exit(EX_START); 253 } 254 255 if (sw->mode == PHYS_AUTO && arg == argc) { 256 fprintf(stderr, "A system must be specified in auto mode.\n"); 257 exit(EX_START); 258 } 259 260 return arg; /* Don't SetLabel yet ! */ 261 } 262 263 static void 264 CheckLabel(const char *label, struct prompt *prompt, int mode) 265 { 266 const char *err; 267 268 if ((err = system_IsValid(label, prompt, mode)) != NULL) { 269 fprintf(stderr, "%s: %s\n", label, err); 270 if (mode == PHYS_DIRECT) 271 log_Printf(LogWARN, "Label %s rejected -direct connection: %s\n", 272 label, err); 273 log_Close(); 274 exit(1); 275 } 276 } 277 278 279 int 280 main(int argc, char **argv) 281 { 282 char *name; 283 const char *lastlabel; 284 int nfds, label, arg; 285 struct bundle *bundle; 286 struct prompt *prompt; 287 struct switches sw; 288 289 nfds = getdtablesize(); 290 if (nfds >= FD_SETSIZE) 291 /* 292 * If we've got loads of file descriptors, make sure they're all 293 * closed. If they aren't, we may end up with a seg fault when our 294 * `fd_set's get too big when select()ing ! 295 */ 296 while (--nfds > 2) 297 close(nfds); 298 299 name = strrchr(argv[0], '/'); 300 log_Open(name ? name + 1 : argv[0]); 301 302 #ifndef NONAT 303 PacketAliasInit(); 304 #endif 305 label = ProcessArgs(argc, argv, &sw); 306 307 /* 308 * A FreeBSD & OpenBSD hack to dodge a bug in the tty driver that drops 309 * output occasionally.... I must find the real reason some time. To 310 * display the dodgy behaviour, comment out this bit, make yourself a large 311 * routing table and then run ppp in interactive mode. The `show route' 312 * command will drop chunks of data !!! 313 */ 314 if (sw.mode == PHYS_INTERACTIVE) { 315 close(STDIN_FILENO); 316 if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) { 317 fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY); 318 return 2; 319 } 320 } 321 322 /* Allow output for the moment (except in direct mode) */ 323 if (sw.mode == PHYS_DIRECT) 324 prompt = NULL; 325 else 326 SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD); 327 328 ID0init(); 329 if (ID0realuid() != 0) { 330 char conf[200], *ptr; 331 332 snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE); 333 do { 334 struct stat sb; 335 336 if (stat(conf, &sb) == 0 && sb.st_mode & S_IWOTH) { 337 log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n", 338 conf); 339 return -1; 340 } 341 ptr = conf + strlen(conf)-2; 342 while (ptr > conf && *ptr != '/') 343 *ptr-- = '\0'; 344 } while (ptr >= conf); 345 } 346 347 if (label < argc) 348 for (arg = label; arg < argc; arg++) 349 CheckLabel(argv[arg], prompt, sw.mode); 350 else 351 CheckLabel("default", prompt, sw.mode); 352 353 if (!sw.quiet) 354 prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(sw.mode)); 355 356 if ((bundle = bundle_Create(TUN_PREFIX, sw.mode, sw.unit)) == NULL) 357 return EX_START; 358 359 /* NOTE: We may now have changed argv[1] via a ``set proctitle'' */ 360 361 if (prompt) { 362 prompt->bundle = bundle; /* couldn't do it earlier */ 363 if (!sw.quiet) 364 prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name); 365 } 366 SignalBundle = bundle; 367 bundle->NatEnabled = sw.nat; 368 if (sw.nat) 369 bundle->cfg.opt |= OPT_IFACEALIAS; 370 371 if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0) 372 prompt_Printf(prompt, "Warning: No default entry found in config file.\n"); 373 374 sig_signal(SIGHUP, CloseSession); 375 sig_signal(SIGTERM, CloseSession); 376 sig_signal(SIGINT, CloseConnection); 377 sig_signal(SIGQUIT, CloseSession); 378 sig_signal(SIGALRM, SIG_IGN); 379 signal(SIGPIPE, SIG_IGN); 380 381 if (sw.mode == PHYS_INTERACTIVE) 382 sig_signal(SIGTSTP, TerminalStop); 383 384 sig_signal(SIGUSR2, BringDownServer); 385 386 lastlabel = argv[argc - 1]; 387 for (arg = label; arg < argc; arg++) { 388 /* In case we use LABEL or ``set enddisc label'' */ 389 bundle_SetLabel(bundle, lastlabel); 390 system_Select(bundle, argv[arg], CONFFILE, prompt, NULL); 391 } 392 393 if (label < argc) 394 /* In case the last label did a ``load'' */ 395 bundle_SetLabel(bundle, lastlabel); 396 397 if (sw.mode == PHYS_AUTO && 398 bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) { 399 prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address " 400 "in auto mode.\n"); 401 AbortProgram(EX_START); 402 } 403 404 if (sw.mode != PHYS_INTERACTIVE) { 405 if (sw.mode != PHYS_DIRECT) { 406 if (!sw.fg) { 407 int bgpipe[2]; 408 pid_t bgpid; 409 410 if (sw.mode == PHYS_BACKGROUND && pipe(bgpipe)) { 411 log_Printf(LogERROR, "pipe: %s\n", strerror(errno)); 412 AbortProgram(EX_SOCK); 413 } 414 415 bgpid = fork(); 416 if (bgpid == -1) { 417 log_Printf(LogERROR, "fork: %s\n", strerror(errno)); 418 AbortProgram(EX_SOCK); 419 } 420 421 if (bgpid) { 422 char c = EX_NORMAL; 423 int ret; 424 425 if (sw.mode == PHYS_BACKGROUND) { 426 close(bgpipe[1]); 427 BGPid = bgpid; 428 /* If we get a signal, kill the child */ 429 signal(SIGHUP, KillChild); 430 signal(SIGTERM, KillChild); 431 signal(SIGINT, KillChild); 432 signal(SIGQUIT, KillChild); 433 434 /* Wait for our child to close its pipe before we exit */ 435 while ((ret = read(bgpipe[0], &c, 1)) == 1) { 436 switch (c) { 437 case EX_NORMAL: 438 prompt_Printf(prompt, "PPP enabled\n"); 439 log_Printf(LogPHASE, "Parent: PPP enabled\n"); 440 break; 441 case EX_REDIAL: 442 if (!sw.quiet) 443 prompt_Printf(prompt, "Attempting redial\n"); 444 continue; 445 case EX_RECONNECT: 446 if (!sw.quiet) 447 prompt_Printf(prompt, "Attempting reconnect\n"); 448 continue; 449 default: 450 prompt_Printf(prompt, "Child failed (%s)\n", 451 ex_desc((int)c)); 452 log_Printf(LogPHASE, "Parent: Child failed (%s)\n", 453 ex_desc((int) c)); 454 } 455 break; 456 } 457 if (ret != 1) { 458 prompt_Printf(prompt, "Child exit, no status.\n"); 459 log_Printf(LogPHASE, "Parent: Child exit, no status.\n"); 460 } 461 close(bgpipe[0]); 462 } 463 return c; 464 } else if (sw.mode == PHYS_BACKGROUND) { 465 close(bgpipe[0]); 466 bundle->notify.fd = bgpipe[1]; 467 } 468 469 bundle_ChangedPID(bundle); 470 bundle_LockTun(bundle); /* we have a new pid */ 471 } 472 473 /* -auto, -dedicated, -ddial, -foreground & -background */ 474 prompt_Destroy(prompt, 0); 475 close(STDOUT_FILENO); 476 close(STDERR_FILENO); 477 close(STDIN_FILENO); 478 if (!sw.fg) 479 setsid(); 480 } else { 481 /* -direct - STDIN_FILENO gets used by physical_Open */ 482 prompt_TtyInit(NULL); 483 close(STDOUT_FILENO); 484 close(STDERR_FILENO); 485 } 486 } else { 487 /* -interactive */ 488 close(STDERR_FILENO); 489 prompt_TtyInit(prompt); 490 prompt_TtyCommandMode(prompt); 491 prompt_Required(prompt); 492 } 493 494 log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode)); 495 DoLoop(bundle); 496 AbortProgram(EX_NORMAL); 497 498 return EX_NORMAL; 499 } 500 501 static void 502 DoLoop(struct bundle *bundle) 503 { 504 fd_set rfds, wfds, efds; 505 int i, nfds, nothing_done; 506 struct probe probe; 507 508 probe_Init(&probe); 509 510 for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) { 511 nfds = 0; 512 FD_ZERO(&rfds); 513 FD_ZERO(&wfds); 514 FD_ZERO(&efds); 515 516 /* All our datalinks, the tun device and the MP socket */ 517 descriptor_UpdateSet(&bundle->desc, &rfds, &wfds, &efds, &nfds); 518 519 /* All our prompts and the diagnostic socket */ 520 descriptor_UpdateSet(&server.desc, &rfds, NULL, NULL, &nfds); 521 522 bundle_CleanDatalinks(bundle); 523 if (bundle_IsDead(bundle)) 524 /* Don't select - we'll be here forever */ 525 break; 526 527 /* 528 * It's possible that we've had a signal since we last checked. If 529 * we don't check again before calling select(), we may end up stuck 530 * after having missed the event.... sig_Handle() tries to be as 531 * quick as possible if nothing is likely to have happened. 532 * This is only really likely if we block in open(... O_NONBLOCK) 533 * which will happen with a misconfigured device. 534 */ 535 if (sig_Handle()) 536 continue; 537 538 i = select(nfds, &rfds, &wfds, &efds, NULL); 539 540 if (i < 0 && errno != EINTR) { 541 log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno)); 542 if (log_IsKept(LogTIMER)) { 543 struct timeval t; 544 545 for (i = 0; i <= nfds; i++) { 546 if (FD_ISSET(i, &rfds)) { 547 log_Printf(LogTIMER, "Read set contains %d\n", i); 548 FD_CLR(i, &rfds); 549 t.tv_sec = t.tv_usec = 0; 550 if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { 551 log_Printf(LogTIMER, "The culprit !\n"); 552 break; 553 } 554 } 555 if (FD_ISSET(i, &wfds)) { 556 log_Printf(LogTIMER, "Write set contains %d\n", i); 557 FD_CLR(i, &wfds); 558 t.tv_sec = t.tv_usec = 0; 559 if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { 560 log_Printf(LogTIMER, "The culprit !\n"); 561 break; 562 } 563 } 564 if (FD_ISSET(i, &efds)) { 565 log_Printf(LogTIMER, "Error set contains %d\n", i); 566 FD_CLR(i, &efds); 567 t.tv_sec = t.tv_usec = 0; 568 if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { 569 log_Printf(LogTIMER, "The culprit !\n"); 570 break; 571 } 572 } 573 } 574 } 575 break; 576 } 577 578 log_Printf(LogTIMER, "Select returns %d\n", i); 579 580 sig_Handle(); 581 582 if (i <= 0) 583 continue; 584 585 for (i = 0; i <= nfds; i++) 586 if (FD_ISSET(i, &efds)) { 587 log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i); 588 /* We deal gracefully with link descriptor exceptions */ 589 if (!bundle_Exception(bundle, i)) { 590 log_Printf(LogERROR, "Exception cannot be handled !\n"); 591 break; 592 } 593 } 594 595 if (i <= nfds) 596 break; 597 598 nothing_done = 1; 599 600 if (descriptor_IsSet(&server.desc, &rfds)) { 601 descriptor_Read(&server.desc, bundle, &rfds); 602 nothing_done = 0; 603 } 604 605 if (descriptor_IsSet(&bundle->desc, &rfds)) { 606 descriptor_Read(&bundle->desc, bundle, &rfds); 607 nothing_done = 0; 608 } 609 610 if (descriptor_IsSet(&bundle->desc, &wfds)) 611 if (!descriptor_Write(&bundle->desc, bundle, &wfds) && nothing_done) { 612 /* 613 * This is disasterous. The OS has told us that something is 614 * writable, and all our write()s have failed. Rather than 615 * going back immediately to do our UpdateSet()s and select(), 616 * we sleep for a bit to avoid gobbling up all cpu time. 617 */ 618 struct timeval t; 619 620 t.tv_sec = 0; 621 t.tv_usec = 100000; 622 select(0, NULL, NULL, NULL, &t); 623 } 624 } 625 626 log_Printf(LogDEBUG, "DoLoop done.\n"); 627 } 628