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