main.c revision 10858
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.8 1995/09/02 17:20:52 amurai Exp $ 21 * 22 * TODO: 23 * o Add commands for traffic summary, version display, etc. 24 * o Add signal handler for misc controls. 25 */ 26#include "fsm.h" 27#include <fcntl.h> 28#include <sys/time.h> 29#include <termios.h> 30#include <signal.h> 31#include <sys/wait.h> 32#include <errno.h> 33#include <netdb.h> 34#include <sys/socket.h> 35#include <arpa/inet.h> 36#include "modem.h" 37#include "os.h" 38#include "hdlc.h" 39#include "lcp.h" 40#include "ipcp.h" 41#include "vars.h" 42#include "auth.h" 43#include "filter.h" 44 45#define LAUTH_M1 "Warning: No password entry for this host in ppp.secret\n" 46#define LAUTH_M2 "Warning: All manipulation is allowed by anyone in the world\n" 47 48#ifndef O_NONBLOCK 49#ifdef O_NDELAY 50#define O_NONBLOCK O_NDELAY 51#endif 52#endif 53 54extern void VjInit(), AsyncInit(); 55extern void AsyncInput(), IpOutput(); 56extern int SelectSystem(); 57 58extern void DecodeCommand(), Prompt(); 59extern int IsInteractive(); 60extern struct in_addr ifnetmask; 61static void DoLoop(void); 62static void TerminalStop(); 63 64static struct termios oldtio; /* Original tty mode */ 65static struct termios comtio; /* Command level tty mode */ 66static int TermMode; 67static int server, update; 68struct sockaddr_in ifsin; 69 70static void 71TtyInit() 72{ 73 struct termios newtio; 74 int stat; 75 76 stat = fcntl(0, F_GETFL, 0); 77 stat |= O_NONBLOCK; 78 fcntl(0, F_SETFL, stat); 79 newtio = oldtio; 80 newtio.c_lflag &= ~(ECHO|ISIG|ICANON); 81 newtio.c_iflag = 0; 82 newtio.c_oflag &= ~OPOST; 83 newtio.c_cc[VEOF] = _POSIX_VDISABLE; 84 newtio.c_cc[VINTR] = _POSIX_VDISABLE; 85 newtio.c_cc[VMIN] = 1; 86 newtio.c_cc[VTIME] = 0; 87 newtio.c_cflag |= CS8; 88 tcsetattr(0, TCSADRAIN, &newtio); 89 comtio = newtio; 90} 91 92/* 93 * Set tty into command mode. We allow canonical input and echo processing. 94 */ 95void 96TtyCommandMode(prompt) 97int prompt; 98{ 99 struct termios newtio; 100 int stat; 101 102 if (!(mode & MODE_INTER)) 103 return; 104 tcgetattr(0, &newtio); 105 newtio.c_lflag |= (ECHO|ISIG|ICANON); 106 newtio.c_iflag = oldtio.c_iflag; 107 newtio.c_oflag |= OPOST; 108 tcsetattr(0, TCSADRAIN, &newtio); 109 stat = fcntl(0, F_GETFL, 0); 110 stat |= O_NONBLOCK; 111 fcntl(0, F_SETFL, stat); 112 TermMode = 0; 113 if(prompt) Prompt(0); 114} 115 116/* 117 * Set tty into terminal mode which is used while we invoke term command. 118 */ 119void 120TtyTermMode() 121{ 122 int stat; 123 124 tcsetattr(0, TCSADRAIN, &comtio); 125 stat = fcntl(0, F_GETFL, 0); 126 stat &= ~O_NONBLOCK; 127 fcntl(0, F_SETFL, stat); 128 TermMode = 1; 129} 130 131void 132TtyOldMode() 133{ 134 int stat; 135 136 stat = fcntl(0, F_GETFL, 0); 137 stat &= ~O_NONBLOCK; 138 fcntl(0, F_SETFL, stat); 139 tcsetattr(0, TCSANOW, &oldtio); 140} 141 142void 143Cleanup(excode) 144int excode; 145{ 146 int stat; 147 148 OsLinkdown(); 149 OsCloseLink(1); 150 sleep(1); 151 if (mode & MODE_AUTO) 152 DeleteIfRoutes(1); 153 OsInterfaceDown(1); 154 LogPrintf(LOG_PHASE, "PPP Terminated.\n"); 155 LogClose(); 156 if (server > 0) 157 close(server); 158 TtyOldMode(); 159 160 exit(excode); 161} 162 163static void 164Hangup() 165{ 166 LogPrintf(LOG_PHASE, "SIGHUP\n"); 167 Cleanup(EX_HANGUP); 168} 169 170static void 171CloseSession() 172{ 173 LogPrintf(LOG_PHASE, "SIGTERM\n"); 174 LcpClose(); 175 Cleanup(EX_TERM); 176} 177 178 179static void 180TerminalCont() 181{ 182 (void)signal(SIGCONT, SIG_DFL); 183 (void)signal(SIGTSTP, TerminalStop); 184 TtyCommandMode(getpgrp() == tcgetpgrp(0)); 185} 186 187static void 188TerminalStop(signo) 189int signo; 190{ 191 (void)signal(SIGCONT, TerminalCont); 192 TtyOldMode(); 193 signal(SIGTSTP, SIG_DFL); 194 kill(getpid(), signo); 195} 196 197 198void 199Usage() 200{ 201 fprintf(stderr, "Usage: ppp [-auto | -direct | -dedicated] [system]\n"); 202 exit(EX_START); 203} 204 205void 206ProcessArgs(int argc, char **argv) 207{ 208 int optc; 209 char *cp; 210 211 optc = 0; 212 while (argc > 0 && **argv == '-') { 213 cp = *argv + 1; 214 if (strcmp(cp, "auto") == 0) 215 mode |= MODE_AUTO; 216 else if (strcmp(cp, "direct") == 0) 217 mode |= MODE_DIRECT; 218 else if (strcmp(cp, "dedicated") == 0) 219 mode |= MODE_DEDICATED; 220 else 221 Usage(); 222 optc++; 223 argv++; argc--; 224 } 225 if (argc > 1) { 226 fprintf(stderr, "specify only one system label.\n"); 227 exit(EX_START); 228 } 229 if (argc == 1) dstsystem = *argv; 230 231 if (optc > 1) { 232 fprintf(stderr, "specify only one mode.\n"); 233 exit(EX_START); 234 } 235} 236 237static void 238Greetings() 239{ 240 printf("User Process PPP. Written by Toshiharu OHNO.\r\n"); 241 fflush(stdout); 242} 243 244void 245main(argc, argv) 246int argc; 247char **argv; 248{ 249 int tunno; 250 int on = 1; 251 252 argc--; argv++; 253 254 mode = MODE_INTER; /* default operation is interactive mode */ 255 netfd = -1; 256 ProcessArgs(argc, argv); 257 Greetings(); 258 GetUid(); 259 IpcpDefAddress(); 260 261 if (SelectSystem("default", CONFFILE) < 0) { 262 fprintf(stderr, "Warning: No default entry is given in config file.\n"); 263 } 264 265 if (LogOpen()) 266 exit(EX_START); 267 268 switch ( LocalAuthInit() ) { 269 case NOT_FOUND: 270 fprintf(stderr,LAUTH_M1); 271 fprintf(stderr,LAUTH_M2); 272 fflush (stderr); 273 /* Fall down */ 274 case VALID: 275 VarLocalAuth = LOCAL_AUTH; 276 break; 277 default: 278 break; 279 } 280 281 if (OpenTunnel(&tunno) < 0) { 282 perror("open_tun"); 283 exit(EX_START); 284 } 285 286 if (mode & (MODE_AUTO|MODE_DIRECT|MODE_DEDICATED)) 287 mode &= ~MODE_INTER; 288 if (mode & MODE_INTER) { 289 printf("Interactive mode\n"); 290 netfd = 0; 291 } else if (mode & MODE_AUTO) { 292 printf("Automatic mode\n"); 293 if (dstsystem == NULL) { 294 fprintf(stderr, "Destination system must be specified in auto mode.\n"); 295 exit(EX_START); 296 } 297 } 298 299 tcgetattr(0, &oldtio); /* Save original tty mode */ 300 301 signal(SIGHUP, Hangup); 302 signal(SIGTERM, CloseSession); 303 signal(SIGINT, CloseSession); 304 signal(SIGQUIT, CloseSession); 305#ifdef SIGSEGV 306 signal(SIGSEGV, Hangup); 307#endif 308#ifdef SIGPIPE 309 signal(SIGPIPE, Hangup); 310#endif 311#ifdef SIGALRM 312 signal(SIGALRM, SIG_IGN); 313#endif 314 if(mode & MODE_INTER) 315 { 316#ifdef SIGTSTP 317 signal(SIGTSTP, TerminalStop); 318#endif 319#ifdef SIGTTIN 320 signal(SIGTTIN, TerminalStop); 321#endif 322#ifdef SIGTTOU 323 signal(SIGTTOU, SIG_IGN); 324#endif 325 } 326 327 if (dstsystem) { 328 if (SelectSystem(dstsystem, CONFFILE) < 0) { 329 fprintf(stderr, "Destination system not found in conf file.\n"); 330 Cleanup(EX_START); 331 } 332 if ((mode & MODE_AUTO) && DefHisAddress.ipaddr.s_addr == INADDR_ANY) { 333 fprintf(stderr, "Must specify dstaddr with auto mode.\n"); 334 Cleanup(EX_START); 335 } 336 } 337 if (mode & MODE_DIRECT) 338 printf("Packet mode enabled.\n"); 339 340#ifdef notdef 341 if (mode & MODE_AUTO) { 342 OsSetIpaddress(IpcpInfo.want_ipaddr, IpcpInfo.his_ipaddr, ifnetmask); 343 } 344#endif 345 346 if (!(mode & MODE_INTER)) { 347 int port = SERVER_PORT + tunno; 348 /* 349 * Create server socket and listen at there. 350 */ 351 server = socket(PF_INET, SOCK_STREAM, 0); 352 if (server < 0) { 353 perror("socket"); 354 Cleanup(EX_SOCK); 355 } 356 ifsin.sin_family = AF_INET; 357 ifsin.sin_addr.s_addr = INADDR_ANY; 358 ifsin.sin_port = htons(port); 359 if (bind(server, (struct sockaddr *) &ifsin, sizeof(ifsin)) < 0) { 360 perror("bind"); 361 if (errno == EADDRINUSE) 362 fprintf(stderr, "Wait for a while, then try again.\n"); 363 Cleanup(EX_SOCK); 364 } 365 listen(server, 5); 366 367 DupLog(); 368 if (!(mode & MODE_DIRECT)) { 369 if (fork()) 370 exit(0); 371 } 372 LogPrintf(LOG_PHASE, "Listening at %d.\n", port); 373#ifdef DOTTYINIT 374 if (mode & (MODE_DIRECT|MODE_DEDICATED)) { /* } */ 375#else 376 if (mode & MODE_DIRECT) { 377#endif 378 TtyInit(); 379 } else { 380 setsid(); /* detach control tty */ 381 } 382 } else { 383 server = -1; 384 TtyInit(); 385 TtyCommandMode(1); 386 } 387 LogPrintf(LOG_PHASE, "PPP Started.\n"); 388 389 390 do 391 DoLoop(); 392 while (mode & MODE_DEDICATED); 393 394 Cleanup(EX_DONE); 395} 396 397/* 398 * Turn into packet mode, where we speek PPP. 399 */ 400void 401PacketMode() 402{ 403 if (RawModem(modem) < 0) { 404 fprintf(stderr, "Not connected.\r\n"); 405 return; 406 } 407 408 AsyncInit(); 409 VjInit(); 410 LcpInit(); 411 IpcpInit(); 412 CcpInit(); 413 LcpUp(); 414 415 if (mode & (MODE_DIRECT|MODE_DEDICATED)) 416 LcpOpen(OPEN_ACTIVE); 417 else 418 LcpOpen(VarOpenMode); 419 if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER) { 420 TtyCommandMode(1); 421 fprintf(stderr, "Packet mode.\r\n"); 422 } 423} 424 425static void 426ShowHelp() 427{ 428 fprintf(stderr, "The following commands are available:\r\n"); 429 fprintf(stderr, " ~p\tEnter to Packet mode\r\n"); 430 fprintf(stderr, " ~.\tTerminate program\r\n"); 431} 432 433static void 434ReadTty() 435{ 436 int n; 437 char ch; 438 static int ttystate; 439#define MAXLINESIZE 200 440 char linebuff[MAXLINESIZE]; 441 442#ifdef DEBUG 443 logprintf("termode = %d, netfd = %d, mode = %d\n", TermMode, netfd, mode); 444#endif 445 if (!TermMode) { 446 n = read(netfd, linebuff, sizeof(linebuff)-1); 447 if (n > 0) { 448 DecodeCommand(linebuff, n, 1); 449 } else { 450#ifdef DEBUG 451 logprintf("connection closed.\n"); 452#endif 453 close(netfd); 454 netfd = -1; 455 mode &= ~MODE_INTER; 456 } 457 return; 458 } 459 460 /* 461 * We are in terminal mode, decode special sequences 462 */ 463 n = read(0, &ch, 1); 464#ifdef DEBUG 465 logprintf("got %d bytes\n", n); 466#endif 467 468 if (n > 0) { 469 switch (ttystate) { 470 case 0: 471 if (ch == '~') 472 ttystate++; 473 else 474 write(modem, &ch, n); 475 break; 476 case 1: 477 switch (ch) { 478 case '?': 479 ShowHelp(); 480 break; 481 case '-': 482 if (loglevel > 0) { 483 loglevel--; 484 fprintf(stderr, "New loglevel is %d\r\n", loglevel); 485 } 486 break; 487 case '+': 488 loglevel++; 489 fprintf(stderr, "New loglevel is %d\r\n", loglevel); 490 break; 491#ifdef DEBUG 492 case 'm': 493 ShowMemMap(); 494 break; 495#endif 496 case 'p': 497 /* 498 * XXX: Should check carrier. 499 */ 500 if (LcpFsm.state <= ST_CLOSED) { 501 VarOpenMode = OPEN_ACTIVE; 502 PacketMode(); 503 } 504 break; 505#ifdef DEBUG 506 case 't': 507 ShowTimers(); 508 break; 509#endif 510 case '.': 511 TermMode = 1; 512 TtyCommandMode(1); 513 break; 514 default: 515 if (write(modem, &ch, n) < 0) 516 fprintf(stderr, "err in write.\r\n"); 517 break; 518 } 519 ttystate = 0; 520 break; 521 } 522 } 523} 524 525 526/* 527 * Here, we'll try to detect HDLC frame 528 */ 529 530static char *FrameHeaders[] = { 531 "\176\377\003\300\041", 532 "\176\377\175\043\300\041", 533 "\176\177\175\043\100\041", 534 "\176\175\337\175\043\300\041", 535 "\176\175\137\175\043\100\041", 536 NULL, 537}; 538 539u_char * 540HdlcDetect(cp, n) 541u_char *cp; 542int n; 543{ 544 char *ptr, *fp, **hp; 545 546 cp[n] = '\0'; /* be sure to null terminated */ 547 ptr = NULL; 548 for (hp = FrameHeaders; *hp; hp++) { 549 fp = *hp; 550 if (DEV_IS_SYNC) 551 fp++; 552 if (ptr = strstr((char *)cp, fp)) 553 break; 554 } 555 return((u_char *)ptr); 556} 557 558static struct pppTimer RedialTimer; 559 560static void 561RedialTimeout() 562{ 563 StopTimer(&RedialTimer); 564 LogPrintf(LOG_PHASE, "Redialing timer expired.\n"); 565} 566 567static void 568StartRedialTimer() 569{ 570 LogPrintf(LOG_PHASE, "Enter pause for redialing.\n"); 571 StopTimer(&RedialTimer); 572 RedialTimer.state = TIMER_STOPPED; 573 RedialTimer.load = REDIAL_PERIOD * SECTICKS; 574 RedialTimer.func = RedialTimeout; 575 StartTimer(&RedialTimer); 576} 577 578 579static void 580DoLoop() 581{ 582 fd_set rfds, wfds, efds; 583 int pri, i, n, wfd; 584 struct sockaddr_in hisaddr; 585 struct timeval timeout, *tp; 586 int ssize = sizeof(hisaddr); 587 u_char *cp; 588 u_char rbuff[MAX_MRU]; 589 int dial_up; 590 int qlen; 591 pid_t pgroup; 592 593 pgroup = getpgrp(); 594 595 if (mode & MODE_DIRECT) { 596 modem = OpenModem(mode); 597 LogPrintf(LOG_PHASE, "Packet mode enabled\n"); 598 PacketMode(); 599 } else if (mode & MODE_DEDICATED) { 600 if (!modem) 601 modem = OpenModem(mode); 602 } 603 604 fflush(stdout); 605 606#ifdef SIGALRM 607 timeout.tv_sec = 0; 608#else 609 timeout.tv_usec = 0; 610#endif 611 612 dial_up = FALSE; /* XXXX */ 613 for (;;) { 614 if ( modem ) 615 IpStartOutput(); 616 FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); 617 618 /* 619 * If Ip packet for output is enqueued and require dial up, 620 * Just do it! 621 */ 622 if ( dial_up && RedialTimer.state != TIMER_RUNNING ) { /* XXX */ 623#ifdef DEBUG 624 logprintf("going to dial: modem = %d\n", modem); 625#endif 626 modem = OpenModem(mode); 627 if (modem < 0) { 628 modem = 0; /* Set intial value for next OpenModem */ 629 StartRedialTimer(); 630 } else { 631 if (DialModem()) { 632 sleep(1); /* little pause to allow peer starts */ 633 ModemTimeout(); 634 PacketMode(); 635 dial_up = FALSE; 636 } else { 637 CloseModem(); 638 /* Dial failed. Keep quite during redial wait period. */ 639 StartRedialTimer(); 640 } 641 } 642 } 643 qlen = ModemQlen(); 644 if (modem) { 645 FD_SET(modem, &rfds); 646 FD_SET(modem, &efds); 647 if (qlen > 0) { 648 FD_SET(modem, &wfds); 649 } 650 } 651 if (server > 0) FD_SET(server, &rfds); 652 653 /* *** IMPORTANT *** 654 * 655 * CPU is serviced every TICKUNIT micro seconds. 656 * This value must be chosen with great care. If this values is 657 * too big, it results loss of characters from modem and poor responce. 658 * If this values is too small, ppp process eats many CPU time. 659 */ 660#ifndef SIGALRM 661 usleep(TICKUNIT); 662 TimerService(); 663#endif 664 if ( qlen < 20 ) { 665 /* 666 * If there are many packets queued, wait until they are drained. 667 */ 668 FD_SET(tun_in, &rfds); 669 } 670 if (netfd > -1) { 671 FD_SET(netfd, &rfds); 672 FD_SET(netfd, &efds); 673 } 674 675 676#ifndef SIGALRM 677 /* 678 * Normally, select() will not block because modem is writable. 679 * In AUTO mode, select will block until we find packet from tun 680 */ 681 tp = (RedialTimer.state == TIMER_RUNNING)? &timeout : NULL; 682 i = select(tun_in+10, &rfds, &wfds, &efds, tp); 683#else 684 /* 685 * When SIGALRM timer is running, a select function will be 686 * return -1 and EINTR after a Time Service signal hundler 687 * is done. 688 */ 689 i = select(tun_in+10, &rfds, &wfds, &efds, NULL); 690#endif 691 if ( i == 0 ) { 692 continue; 693 } 694 695 if ( i < 0 ) { 696 if ( errno == EINTR ) { 697 continue; /* Got SIGALRM, Do check a queue for dailing */ 698 } 699 perror("select"); 700 break; 701 } 702 703 if ((netfd > 0 && FD_ISSET(netfd, &efds)) || FD_ISSET(modem, &efds)) { 704 logprintf("Exception detected.\n"); 705 break; 706 } 707 708 if (server > 0 && FD_ISSET(server, &rfds)) { 709#ifdef DEBUG 710 logprintf("connected to client.\n"); 711#endif 712 wfd = accept(server, (struct sockaddr *)&hisaddr, &ssize); 713 if (netfd > 0) { 714 write(wfd, "already in use.\n", 16); 715 close(wfd); 716 continue; 717 } else 718 netfd = wfd; 719 if (dup2(netfd, 1) < 0) 720 perror("dup2"); 721 mode |= MODE_INTER; 722 Greetings(); 723 switch ( LocalAuthInit() ) { 724 case NOT_FOUND: 725 fprintf(stdout,LAUTH_M1); 726 fprintf(stdout,LAUTH_M2); 727 fflush(stdout); 728 /* Fall down */ 729 case VALID: 730 VarLocalAuth = LOCAL_AUTH; 731 break; 732 default: 733 break; 734 } 735 (void) IsInteractive(); 736 Prompt(0); 737 } 738 739 if ((mode & MODE_INTER) && FD_ISSET(netfd, &rfds) && 740 ((mode & MODE_AUTO) || pgroup == tcgetpgrp(0))) { 741 /* something to read from tty */ 742 ReadTty(); 743 } 744 if (modem) { 745 if (FD_ISSET(modem, &wfds)) { /* ready to write into modem */ 746 ModemStartOutput(modem); 747 } 748 if (FD_ISSET(modem, &rfds)) { /* something to read from modem */ 749 if (LcpFsm.state <= ST_CLOSED) 750 usleep(10000); 751 n = read(modem, rbuff, sizeof(rbuff)); 752 if ((mode & MODE_DIRECT) && n <= 0) { 753 DownConnection(); 754 } else 755 LogDumpBuff(LOG_ASYNC, "ReadFromModem", rbuff, n); 756 757 if (LcpFsm.state <= ST_CLOSED) { 758 /* 759 * In dedicated mode, we just discard input until LCP is started. 760 */ 761 if (!(mode & MODE_DEDICATED)) { 762 cp = HdlcDetect(rbuff, n); 763 if (cp) { 764 /* 765 * LCP packet is detected. Turn ourselves into packet mode. 766 */ 767 if (cp != rbuff) { 768 write(1, rbuff, cp - rbuff); 769 write(1, "\r\n", 2); 770 } 771 PacketMode(); 772#ifdef notdef 773 AsyncInput(cp, n - (cp - rbuff)); 774#endif 775 } else 776 write(1, rbuff, n); 777 } 778 } else { 779 if (n > 0) 780 AsyncInput(rbuff, n); 781#ifdef notdef 782 continue; /* THIS LINE RESULT AS POOR PERFORMANCE */ 783#endif 784 } 785 } 786 } 787 788 if (FD_ISSET(tun_in, &rfds)) { /* something to read from tun */ 789 n = read(tun_in, rbuff, sizeof(rbuff)); 790 if (n < 0) { 791 perror("read from tun"); 792 continue; 793 } 794 /* 795 * Process on-demand dialup. Output packets are queued within tunnel 796 * device until IPCP is opened. 797 */ 798 if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) { 799 pri = PacketCheck(rbuff, n, FL_DIAL); 800 if (pri >= 0) { 801 IpEnqueue(pri, rbuff, n); 802 dial_up = TRUE; /* XXX */ 803 } 804 continue; 805 } 806 pri = PacketCheck(rbuff, n, FL_OUT); 807 if (pri >= 0) 808 IpEnqueue(pri, rbuff, n); 809 } 810 } 811 logprintf("job done.\n"); 812} 813