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