/* Silicon Graphics PPP */ #ident "$Revision: 1.58 $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFINE #include "ppp.h" #include "pppinfo.h" #include "keyword.h" static struct ppp *newest, *oldest; static enum connmodes callmode; /* when the bundle will be busy, and time to add bandwidth */ static struct timeval busy_time; /* when the bundle will be idle, and time to reduce bandwidth */ static struct timeval idle_time; static int idle; /* ignore busy or idle indications from the kernel for a while after * changing the available bandwidth */ static struct timeval beepok_time; /* slow down attemps to add lines as consecutive attempts fail */ static struct timeval add_time; #define BACKOFF0 5 /* initial value in seconds */ #define BACKOFF_MAX (15*60) static time_t add_backoff = BACKOFF0; /* to infer speeds of individual links */ static float hi_bps; /* add links when load above this */ static float lo_bps; /* too many links if load below this */ static float avail_bps; static int avail_bps_ok; /* 0=unknown, 1=computed, 2=config */ static float prev_avail_bps; static int prev_avail_bps_ok; static time_t beep_tstamp; /* previous counts to get rates */ u_int beep_ibytes, beep_obytes; /* info given `pppstat` */ static struct ppp_status ppp_status; static struct timeval ppp_status_time; /* status file should be updated */ /* initial packets entangled with banner on async links */ #define BANSIZE 512 #define BANPAD 5 static char banbuf[BANSIZE+BANPAD]; static int banlen, banoff; static flg seen_framer; /* >=1 HDLC frame through framer */ static flg all_sync; /* all links in bundle are sync */ #define mpsize 512 static struct ppp_buf mp_buf; static int mp_sn = -2; static int mp_len; static struct ppp *rcv_msg_ppp; /* set by rcv_msg() */ struct timeval rcv_msg_ts; static char bad_beep[20]; static char *beep_name; static char *beeps[] = { "TCP/IP", /* BEEP_ACTIVE */ "IP", /* BEEP_WEAK */ "line off", /* BEEP_DEAD */ "CP bad", /* BEEP_CP_BAD */ "Frame", /* BEEP_FRAME */ "bad FCS", /* BEEP_FCS */ "aborted frame", /* BEEP_ABORT */ "tiny frame", /* BEEP_TINY */ "dropped", /* BEEP_BABBLE */ "MP", /* BEEP_MP */ "MP error" /* BEEP_MP_ERROR */ }; #define NUM_BEEPS (sizeof(beeps)/sizeof(beeps[0])) static void usage(void); static void newest_oldest(void); static void init_ppp(struct ppp*); static void update_acct(int); static void ck_rend(char*); static int make_ip(struct ppp*); static void update_status(void); static int rcv_msg(int); static void ipkt(struct ppp*); static void log_pkt(int lvl, char*, char*, char*, int, u_char*); static void log_beep_act(char*, char*, char*, char*, struct beep*); static int log_mp_ipkt_str(int, char*, char*); static int addline(void); static int fork_addline(int); static void unfork(char *); static void die(struct ppp *); static void last_dev(void); static void get_hi_lo_bps(void); static void numdevs_inc(struct ppp*); static void deadline(struct ppp*); static void reap_child(void); static void devs_off(int, char*); static void stopint(int); static void moredebug(); static void lessdebug(); /* FCS table from RFC-1331 */ u_short ppp_fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; void main(int argc, char **argv) { register int i; register struct ppp *ppp; char *p; fd_set err, in; int nfds; struct timeval timer; time_t c_dly; long ms, ms1, ms2; if (0 != (p = strrchr(argv[0], '/'))) { pgmname = p+1; } else { pgmname = argv[0]; if (!strcmp(pgmname,"-ppp")) pgmname++; } ctty = open("/dev/tty", O_RDONLY|O_NDELAY); if (isatty(stderrfd)) { interact = 1; } else { no_interact(); } openlog(pgmname, LOG_PID | LOG_ODELAY | LOG_NOWAIT, LOG_DAEMON); opterr = 0; while (i = getopt(argc,argv,"dLf:r:"), i != EOF) switch (i) { case 'd': debug = ++arg_debug; SET_Debug(); break; case 'L': (void)printf("keywords:\n"); for (i = 0; i < KEYTBL_LEN-1; i++) (void)printf("\t%s\n",keytbl[i].str); exit(0); break; case 'f': cfilename = optarg; break; case 'r': remote = optarg; break; default: usage(); } if (argc != optind) usage(); if (remote) { assume_callee = 0; } else { assume_callee = 1; connmode = CALLEE; no_interact(); remote = getenv("USER"); if (!remote) { remote = ""; log_complain("","-r not specified and $USER not set"); exit(1); } } if (remote) remote = strdup(remote); if ((int)strlen(remote) > SYSNAME_LEN) { log_complain("","\"%s\" is too long", remote); remote[SYSNAME_LEN] = '\0'; } if (geteuid() != 0) { log_complain("","requires UID 0"); exit(1); } /* from now on, send complaints to the system log */ if (stderrpid <= 0) stderrfd = -1; /* avoid being affected by odinary processes */ (void)nice(-40); init_rand(); ppp_status.ps_version = PS_VERSION; ppp_status.ps_initial_date = time(0); get_clk(); clr_acct(&ppp0.dv); if (assume_callee) ppp0.dv.acct.answered++; last_dev(); idle_time.tv_sec = TIME_NEVER; busy_time.tv_sec = TIME_NEVER; beepok_time.tv_sec = TIME_NOW; add_time.tv_sec = TIME_NOW; init_ppp(&ppp0); (void)parse_conf(0); (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, stopint); (void)signal(SIGTERM, stopint); (void)signal(SIGPIPE, stopint); (void)signal(SIGUSR1, moredebug); (void)signal(SIGUSR2, lessdebug); (void)signal(SIGCHLD, reap_child); if (connmode == CALLEE) { ppp0.dv.callmode = CALLEE; grab_dev("starting PPP", &ppp0.dv); numdevs_inc(&ppp0); closefds(); /* avoid signals from the terminal */ if (!rdy_tty_dev(&ppp0.dv)) cleanup(1); if (0 > reset_accm(&ppp0,1)) cleanup(1); if (ppp0.dv.sync == SYNC_ON) ppp0.nowait = 1; /* assume no getty on sync line */ } else { if (lochost.sin_addr.s_addr != 0 && !def_lochost && remhost.sin_addr.s_addr != 0 && !def_remhost) { if (!add_rend_name("",remhost_str)) cleanup(1); ck_rend("existing caller connected to %s"); if (!make_ip(&ppp0)) cleanup(1); } } for (;;) { restart: /* Come here either at the very start, after a child has * been forked via fork_addline(), or after a camping * CALLER decides to try again. * * Here there are 0 or 1 active lines. */ closefds(); /* avoid signals from the terminal */ if (connmode == Q_CALLER) { if (numdevs == 0) goto shut; } else if (connmode == CALLER) { /* The second time around or when we know the remote * IP address, use a child to make the link. * This lets us listen for incoming connections. */ if (rendnode >= 0) { if (fork_addline(1)) goto shut; } else if (!addline()) { goto shut; } } /* if we have a device and are just starting with it, * then get LCP going. */ if (ppp0.dv.devfd >= 0 && ppp0.lcp.fsm.state <= FSM_CLOSED_2) { log_debug(1,"", "starting to use %s", ppp0.dv.line); lcp_event(&ppp0,FSM_OPEN); lcp_event(&ppp0,FSM_UP); } /* While in this loop there is at least one active line * and more might be added. */ idle = 0; act_devs(2,0); for (; numdevs > 0; get_clk()) { FD_ZERO(&in); nfds = 0; ms = HEARTBEAT*1000; if (numdevs > mindevs) { ms1 = cktime(&idle_time); ms = MIN(ms, ms1); } if (add_pid < 0 && numdevs < outdevs) { ms1 = cktime(&busy_time); ms2 = cktime(&add_time); ms1 = MAX(ms1,ms2); ms = MIN(ms, ms1); } if (banlen != 0) ms = 0; /* get frame saved from banner */ /* run state machines for the RFC 1717 bundle */ if (ms != 0 && mp_ppp != 0) { ms1 = cktime(&mp_ppp->ipcp.fsm.timer); ms = MIN(ms, ms1); ms1 = cktime(&mp_ppp->ccp.fsm.timer); ms = MIN(ms, ms1); } for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0') continue; if (ms != 0) { /* note earliest PPP protocol timer */ ms1 = cktime(&ppp->lcp.fsm.timer); ms = MIN(ms, ms1); ms1 = cktime(&ppp->auth.timer); ms = MIN(ms, ms1); if (mp_ppp == 0) { ms1 = cktime(&ppp->ipcp.fsm.timer); ms = MIN(ms, ms1); ms1 = cktime(&ppp->ccp.fsm.timer); ms = MIN(ms, ms1); } if (ppp->phase == NET_PHASE) { ms1 = cktime(&ppp->lcp.echo_timer); ms = MIN(ms, ms1); } } /* Watch lines that are not under the module. * Allow only one packet at a time so that * none bypass the module. */ if (ppp->dv.devfd >= 0) { if (ppp->in_stop != 1) { (void)do_strioctl(ppp, SIOC_PPP_1_RX, 0, "ioctl(_1_RX)"); ppp->in_stop = 1; } FD_SET(ppp->dv.devfd, &in); nfds = MAX(nfds,ppp->dv.devfd); } } /* Awaken for the expiration of the inactivity timer * if we are going to sleep at all. * Do not worry about the inactivity timer if the * bundle was started by the peer. Extra lines * are handled separately. */ if (sactime != 0 && oldest != 0) { c_dly = oldest->dv.active; } else { c_dly = TIME_NEVER; } if (c_dly == TIME_NEVER) { ppp_status.ps_atime = TIME_NEVER; } else { ppp_status.ps_atime = (c_dly-clk.tv_sec); if (ms != 0) { ms1 = ppp_status.ps_atime*1000; ms = (ms1 > 0) ? MIN(ms, ms1) : 0; } } timer.tv_sec = ms/1000; timer.tv_usec = (ms%1000)*1000; /* A CALLEE might not have finished IPCP chatting and * received its IP address and so might not yet have * the PPP module or rendezvous socket set up. */ if (rendnode >= 0 && add_pid != 0) { ck_rend("link to %s stolen"); FD_SET(rendnode, &in); nfds = MAX(nfds,rendnode); } if (modfd >= 0) { FD_SET(modfd, &in); nfds = MAX(nfds,modfd); } err = in; if (status_poke_fd >= 0) { FD_SET(status_poke_fd, &in); nfds = MAX(nfds,status_poke_fd); } nfds = select(nfds+1, &in,0,&err, &timer); if (nfds < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) bad_errno("select()",""); nfds = 0; } update_acct(0); if (banlen != 0 && nfds == 0) { /* get frame saved from banner */ FD_SET(ppp0.dv.devfd, &in); nfds = 1; } else if (nfds == 0) { /* If things are quiet, maybe shut down. * Stop incomplete calls. * If we originated this call, shut it all * down. Otherwise, shut down our part * of it. */ if (clk.tv_sec > c_dly) { unfork("activity timer expired"); devs_off((callmode != CALLEE || ppp0.conf.mp_callee) ? 0 : 1, "activity timer expired"); } } /* See what the kernel has to say from the PPP driver. */ if (modfd >= 0 && FD_ISSET(modfd,&in) && rcv_msg(modfd)) { switch (ibuf.type) { case BEEP_DEAD: if (!rcv_msg_ppp) { log_debug(1,"", "odd %s from index %d", beep_name, ibuf.dev_index); } else { deadline(rcv_msg_ppp); } break; case BEEP_CP_BAD: /* Problem with compression. * Blip CCP if active. */ ppp = mp_ppp ? mp_ppp : rcv_msg_ppp; if (!ppp) { log_debug(1,"", "odd %s from index %d", beep_name, ibuf.dev_index); } else { ppp->ccp.cnts.bad_ccp++; ccp_blip(ppp,0); } break; case BEEP_ACTIVE: act_devs(0,1); update_acct(1); ccp_blip_age(rcv_msg_ppp); break; case BEEP_WEAK: act_devs(1,1); update_acct(1); ccp_blip_age(rcv_msg_ppp); break; case BEEP_FRAME: if (!rcv_msg_ppp) { log_debug(1,"", "odd %s from index %d", beep_name, ibuf.dev_index); } else { ipkt(rcv_msg_ppp); } break; case BEEP_FCS: case BEEP_ABORT: case BEEP_TINY: case BEEP_BABBLE: case BEEP_MP: case BEEP_MP_ERROR: break; default: log_complain(rcv_msg_ppp ? rcv_msg_ppp->link_name : "", "wild %s from index %d", beep_name, ibuf.dev_index); break; } } /* Deal with non-IP input or other PPP events. */ for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0') continue; if (ppp->dv.devfd >= 0) { if (FD_ISSET(ppp->dv.devfd, &err)) { log_debug(1, ppp->link_name, "%s off/error", ppp->dv.line); hangup(ppp); continue; } if (FD_ISSET(ppp->dv.devfd, &in)) { if (ppp->in_stop != 0) ppp->in_stop = 2; if (rcv_msg(ppp->dv.devfd)) { switch (ibuf.type) { case BEEP_FRAME: ipkt(ppp); break; case BEEP_DEAD: deadline(ppp); continue; } } } } if (cktime(&ppp->lcp.fsm.timer) == TIME_NOW) lcp_event(ppp,FSM_TO_P); if (cktime(&ppp->auth.timer) == TIME_NOW) auth_time(ppp); if (mp_ppp == 0) { if (cktime(&ppp->ipcp.fsm.timer) == TIME_NOW) ipcp_event(ppp,FSM_TO_P); if (cktime(&ppp->ccp.fsm.timer) == TIME_NOW) ccp_event(ppp,FSM_TO_P); } if (ppp->phase == NET_PHASE && cktime(&ppp->lcp.echo_timer)==TIME_NOW) lcp_send_echo(ppp); } if (mp_ppp != 0) { if (cktime(&mp_ppp->ipcp.fsm.timer) == TIME_NOW) ipcp_event(mp_ppp,FSM_TO_P); if (cktime(&mp_ppp->ccp.fsm.timer) == TIME_NOW) ccp_event(mp_ppp,FSM_TO_P); } /* If we are the child, join the resident daemon * only when all of the banner has been processed * and at least one sync HDLC frame has gone * through the framer. */ if (add_pid == 0 && banlen == 0 && !reconfigure && (ppp0.dv.sync == SYNC_OFF || seen_framer)) { (void)make_rend(1); do_rend(&ppp0); } if (rendnode >= 0 && FD_ISSET(rendnode,&in)) (void)rendezvous(1); /* Add or delete extra lines. This is separate * from the inactivity timer on the whole bundle. * add_time delays adding the first line, * effectively helping to implement demand dialing. * Otherwise, we would restore the line immediately * after turning it off. */ if (numdevs < outdevs && (numdevs < mindevs || TIME_NOW == cktime(&busy_time))) { if (TIME_NOW == cktime(&add_time) && !fork_addline(ppp0.conf.mp_callee)) goto restart; } else if (numdevs > mindevs && TIME_NOW == cktime(&idle_time)) { /* Turn off most recent new device. */ devs_off(2,"inactivity timeout"); } /* update status for pppstat */ if (status_poke_fd >= 0 && FD_ISSET(status_poke_fd,&in)) update_status(); } /* all lines are off */ update_acct(1); if (!demand_dial && !camping) { /* Must be simple, one-shot CALLEE or CALLER * that handled one call. */ cleanup(0); } shut: /* Here there are 0 active lines. * * Come here after all lines have shut down, when * a quiet caller has not yet started a line, or after * a simple caller has failed to make a connection. * * A camping caller that has not yet made the connection * might not know the IP addresses and so the rendezvous * socket might not be ready. */ if (add_pid == 0) { /* must be a helpful child that failed. */ cleanup(1); } if (rendnode >= 0 && (demand_dial || camping || add_pid > 0)) { if (connmode == CALLER) connmode = Q_CALLER; for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.devfd >= 0 || ppp->dv.dev_index != -1) hangup(ppp); } /* Flush pending output, * unless we have a child trying to connect. */ if (add_pid < 0 && 0 > ioctl(modfd, TCFLSH, 2)) log_errno("ioctl(modfd,TCFLSH,2)",""); do { ppp0.dv.acct.sconn = get_clk(); /* Check that the FIFO has not been removed. * Quit if some other process now owns * the link. */ ck_rend("link to %s stolen"); ck_acct(&ppp0.dv,0); FD_ZERO(&in); FD_SET(rendnode, &in); nfds = rendnode; ms = cktime(&add_time); if (ms > 0) { /* If the modem is still hot, * let it cool. */ ppp_status.ps_atime = ms/1000; if (ms/1000 > HEARTBEAT) ms = HEARTBEAT*1000; } else { /* If not demand dialing, redial now. */ if (!demand_dial && !fork_addline(1)) goto restart; /* else, wait until the kernel * says something is happening */ FD_SET(modfd, &in); nfds = MAX(nfds,modfd); ppp_status.ps_atime = TIME_NEVER; ms = HEARTBEAT*1000; } timer.tv_sec = ms/1000; timer.tv_usec = (ms%1000)*1000; if (status_poke_fd >= 0) { FD_SET(status_poke_fd, &in); nfds = MAX(nfds,status_poke_fd); } nfds = select(nfds+1,&in,0,0, &timer); if (nfds < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) bad_errno("select()",""); nfds = 0; } ppp0.dv.acct.sconn = get_clk(); if (nfds > 0) { /* see what kernel has to say */ if (FD_ISSET(modfd,&in) && rcv_msg(modfd)) { if (ibuf.type == BEEP_ACTIVE || ibuf.type == BEEP_WEAK) { if (!fork_addline(1)) goto restart; } else { log_complain("", "odd \"%s\"", beep_name); } } if (FD_ISSET(rendnode,&in) && rendezvous(1)) { break; } } /* update status for pppstat */ if (status_poke_fd >= 0 && FD_ISSET(status_poke_fd, &in)) update_status(); } while (demand_dial || camping || add_pid > 0); } if (!demand_dial && !camping && add_pid <= 0 && numdevs == 0) { /* must be a one-shot CALLEE or CALLER that failed. */ cleanup(0); } } } static void usage(void) { kludge_stderr(); (void)fputs("usage: [-d] [-f cfile] [-r remote]\n", stderr); exit(1); } u_long newmagic(void) { register u_long m; struct tms tms; for (;;) { m = random() ^ times(&tms); if ((m & 0xff) != 0) return m; } } /* find the newest and oldest links */ static void newest_oldest(void) { struct ppp *ppp; int new_age, old_age; newest = 0; oldest = 0; callmode = UNKNOWN_CALL; new_age = 0; old_age = 0; all_sync = 1; for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0') continue; if (!ppp->dv.sync) all_sync = 0; if ( ppp->age >= new_age) { new_age = ppp->age; newest = ppp; } if (ppp->age <= old_age || old_age == 0) { old_age = ppp->age; oldest = ppp; callmode = ppp->dv.callmode; } } } static void fix_name(struct ppp *ppp) { bzero(ppp->link_name, sizeof(ppp->link_name)); if (maxdevs > 1 && add_pid != 0 && rendnode >= 0) { int i = ppp->link_num; (void)sprintf(ppp->link_name," %d", i); (void)sprintf(ppp->lcp.fsm.name, " LCP%d", i); (void)sprintf(ppp->auth.name, " AUTH%d", i); if (ppp == mp_ppp) { (void)strcpy(ppp->ipcp.fsm.name, " IPCP"); (void)strcpy(ppp->ccp.fsm.name, " CCP"); } else { (void)sprintf(ppp->ipcp.fsm.name," IPCP%d", i); (void)sprintf(ppp->ccp.fsm.name, " CCP%d", i); } } else { (void)strcpy(ppp->lcp.fsm.name, " LCP"); (void)strcpy(ppp->auth.name, " AUTH"); (void)strcpy(ppp->ipcp.fsm.name," IPCP"); (void)strcpy(ppp->ccp.fsm.name, " CCP"); } } static void fix_names(void) { struct ppp *ppp; for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) fix_name(ppp); } static void clear_ppp(struct ppp *ppp) { int save_num; struct dev *save_next; if (ppp == &ppp0) { ppp->link_num = 1; } else { save_next = ppp->dv.next; save_num = ppp->link_num; bzero(ppp, sizeof(*ppp)); ppp->dv.next = save_next; ppp->link_num = save_num; ppp->dv.acct = ppp0.dv.acct; ppp->dv.acct.call_start = 0; } ppp->mypid = getpid(); ppp->dv.line[0] = '\0'; ppp->dv.devfd = -1; ppp->dv.devfd_save = -1; ppp->dv.dev_index = -1; ppp->dv.dev_sbuf.st_ino = 0; ppp->dv.rendpid = -1; ppp->dv.active = TIME_NEVER; ppp->lcp.fsm.timer.tv_sec = TIME_NEVER; ppp->ipcp.fsm.timer.tv_sec = TIME_NEVER; ppp->ccp.fsm.timer.tv_sec = TIME_NEVER; ppp->utmp_id[0] = '\0'; ppp->utmp_has_host = 0; ppp->utmp_type = NO_UTMP; ppp->bps = ppp0.conf_bps; } static void init_ppp(struct ppp *ppp) { clear_ppp(ppp); fix_name(ppp); ppp->age = clk.tv_sec; lcp_init(ppp); auth_init(ppp); ipcp_init(ppp); ccp_init(ppp); } /* account for the connect-time */ static void update_acct(int reset_idle) /* 1=now busy */ { struct ppp *ppp; int total_secs, idle_secs; total_secs = get_clk() - ppp0.dv.acct.sconn; if (idle) { idle_secs = total_secs; } else if (total_secs >= BEEP/HZ) { /* If enough time has elapsed for the regular notifications * from the kernel, then the link must be idle. * The link is considered busy for BEEP/HZ seconds after the * kernel beeps. */ idle_secs = total_secs-BEEP/HZ; idle = 1; } else if (reset_idle) { /* The kernel has said something, so the preceding time * can be counted. */ idle_secs = idle ? total_secs : 0; } else { /* Fewer than BEEP/HZ seconds have elapsed since the * kernel said the link was busy, so nothing can be * counted yet. */ return; } if (reset_idle) idle = 0; /* count link-seconds, accumulating the connect time for * all of the links. */ for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0') continue; if (ppp->dv.callmode != CALLEE) { ppp0.dv.acct.orig_conn += total_secs; ppp0.dv.acct.orig_idle += idle_secs; } else { ppp0.dv.acct.ans_conn += total_secs; ppp0.dv.acct.ans_idle += idle_secs; } } ppp0.dv.acct.sconn = clk.tv_sec; ck_acct(&ppp0.dv, 0); } /* Try get get a frame from the among the banner * Do not bother with address or protocol compression, because * this code should only see initial LCP frames. */ static int /* <0 if failed */ get_banframe(void) { u_char c, *cp; u_short fcs; char esc; if (banlen == 0) return 0; ibuf.dev_index = ppp0.dv.dev_index; cp = &ibuf.frame; *cp++ = PPP_FLAG; fcs = PPP_FCS_INIT; esc = 0; while (banlen != 0) { if (cp >= &ibuf.un.info[PPP_MAX_MTU-2]) { ibuf.type = BEEP_BABBLE; break; } banlen--; c = banbuf[banoff++]; if (esc) { /* do not bother after an aborted frame */ if (c == PPP_FLAG) { log_debug(6,"", "abort: discard %d salvaged bytes", banlen+cp-&ibuf.frame); banlen = 0; return 0; } c ^= PPP_ESC_BIT; esc = 0; } else if (c == PPP_FLAG) { /* ignore null frames */ if (cp == &ibuf.addr) continue; /* skip bad frames */ if (cp < ibuf.un.info || fcs != PPP_FCS_GOOD) { log_debug(6,"", "bad FCS: discard %d salvaged bytes", cp-&ibuf.frame); cp = &ibuf.frame; continue; } /* got a good one */ ibuf.type = BEEP_FRAME; log_debug(3, "","salvage input packet from banner"); return (cp-(u_char*)&ibuf)-2; /* length less FCS */ } else if (c == PPP_ESC) { esc = 1; continue; } fcs = PPP_FCS(fcs,c); *cp++ = c; } if (cp != &ibuf.frame) log_debug(6,"","missing end flag: discard %d salvaged bytes", cp-&ibuf.frame); return 0; } /* update status for pppstat */ static void update_status(void) { struct ppp *ppp; struct ppp_info *info_p; struct ps_dev *psl; struct strioctl strioctl; struct stat st; char buf[32]; int fd; /* flush the pipe that poked us */ if (0 > read(status_poke_fd,buf,sizeof(buf))) { log_errno("read(status_poke_fd)",""); (void)close(status_poke_fd); status_poke_fd = -1; return; } /* skip it if the file is recent */ get_clk(); if (ppp_status.ps_cur_date == cur_date.tv_sec && cktime(&ppp_status_time) != TIME_NOW) return; fsm_settimer(&ppp_status_time, 750); /* punt if the module is not open */ if (modfd < 0) return; /* get the data from the kernel */ info_p = &ppp_status.ps_pi; strioctl.ic_cmd = SIOC_PPP_INFO; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(info_p); strioctl.ic_dp = (char*)&info_p; if (0 > ioctl(modfd, I_STR, &strioctl)) { log_errno("_INFO",""); ppp_status_time.tv_sec += 60; return; } if (ppp_status.ps_pi.pi_version != PI_VERSION) { log_complain("","wrong _PPP_INFO version %d instead of %d", ppp_status.ps_pi.pi_version, PI_VERSION); ppp_status_time.tv_sec += 60; return; } if (ppp_status.ps_pi.pi_len != sizeof(ppp_status.ps_pi)) { log_complain("","wrong _PPP_INFO size %d instead of %d", ppp_status.ps_pi.pi_len, sizeof(ppp_status.ps_pi)); ppp_status_time.tv_sec += 60; return; } ppp_status.ps_cur_date = cur_date.tv_sec; ppp_status.ps_pid = ppp0.mypid; ppp_status.ps_add_pid = add_pid; ppp_status.ps_debug = debug; ppp_status.ps_maxdevs = maxdevs; ppp_status.ps_outdevs = outdevs; ppp_status.ps_mindevs = mindevs; ppp_status.ps_numdevs = numdevs; ppp_status.ps_idle_time = cktime(&idle_time)/1000; ppp_status.ps_busy_time = cktime(&busy_time)/1000; ppp_status.ps_beepok_time = cktime(&beepok_time)/1000; ppp_status.ps_add_time = cktime(&add_time)/1000; ppp_status.ps_add_backoff = add_backoff; ppp_status.ps_avail_bps = avail_bps_ok ? avail_bps : 0; ppp_status.ps_prev_avail_bps = prev_avail_bps_ok ? prev_avail_bps : 0; bcopy(&ppp0.dv.acct, &ppp_status.ps_acct, sizeof(ppp_status.ps_acct)); for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0' || ppp->dv.dev_index < 0) continue; psl = &ppp_status.ps_devs[ppp->link_num-1]; psl->callee = (ppp->dv.callmode == CALLEE); psl->ps_active = clk.tv_sec - ppp->age; psl->bps = ppp->bps; psl->lcp_echo_rtt = ppp->lcp.echo_rtt; psl->lcp_neg_mtru = (mp_ppp ? mp_ppp : ppp)->lcp.neg_mtru; psl->lcp_neg_mrru = (mp_ppp ? mp_ppp : ppp)->lcp.neg_mrru; psl->lcp_neg_mtu = ppp->lcp.neg_mtu; psl->lcp_neg_mru = ppp->lcp.neg_mru; psl->bad_ccp = ppp->ccp.cnts.bad_ccp; psl->ccp_rreq_sent = ppp->ccp.cnts.rreq_sent; psl->ccp_rreq_rcvd = ppp->ccp.cnts.rreq_rcvd; psl->ccp_rack_rcvd = ppp->ccp.cnts.rack_rcvd; bcopy(&ppp->lcp.ident_rcvd, &psl->ident, (psl->ident_len = ppp->lcp.ident_rcvd_len)); } /* prevent some fun and games */ if (0 > lstat(status_path, &st)) { if (errno != ENOENT) { log_errno("open() ", status_path); ppp_status_time.tv_sec += 60; return; } } else if (st.st_uid != 0 && 0 > unlink(status_path)) { log_errno("unlink() ", status_path); ppp_status_time.tv_sec += 60; return; } /* re-open the file every time in case it has disappeared */ fd = open(status_path, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (fd < 0) { log_errno("open() ", status_path); ppp_status_time.tv_sec += 60; return; } if (sizeof(ppp_status) != write(fd,&ppp_status, sizeof(ppp_status))) log_errno("write() ", status_path); (void)close(fd); } int hack; /* get an input packet */ static int /* 0=bad */ rcv_msg(int fd) { int sn, ticks, i; float load_bps, bps; struct ppp *ppp; int flags; struct strbuf imsg, cmsg; char *nstr; char dbgmsg[100]; char cbuf[32]; char istr[sizeof("index=xxx ")+8]; static char *acts[] = { "rx lo/tx lo", /* 0 */ "rx med/tx lo", /* 1 BEEP_ST_RX_ACT */ "rx hi/tx lo", /* 2 BEEP_ST_RX_BUSY */ "rx ???/tx lo", /* 3 ? */ "rx hi/tx med", /* 4 BEEP_ST_TX_ACT */ "rx med/tx med", /* 5 BEEP_ST_RX_ACT|BEEP_ST_TX_ACT */ "rx hi/tx med", /* 6 BEEP_ST_RX_BUSY|BEEP_ST_TX_ACT */ "rx ???/tx med", /* 7 ?|BEEP_ST_TX_ACT */ "rx lo/tx hi", /* 8 BEEP_ST_TX_BUSY */ "rx med/tx hi", /* 9 BEEP_ST_RX_ACT|BEEP_ST_TX_BUSY */ "rx hi/tx hi", /* a BEEP_ST_RX_BUSY|BEEP_ST_TX_busy */ "rx ???/tx hi", /* b ?|BEEP_ST_TX_busy */ "rx lo/tx ???", /* c ? */ "rx med/tx ???", /* d BEEP_ST_RX_ACT */ "rx hi/tx ???", /* e BEEP_ST_RX_BUSY */ "???" /* f ? */ }; bzero(&ibuf,sizeof(ibuf)); /* Read from the file descriptor or from the frame saved * from the banner or an assembled MP packet. */ if (fd != ppp0.dv.devfd || 0 == (imsg.len = get_banframe())) { imsg.buf = (char*)&ibuf; imsg.maxlen = sizeof(ibuf); cmsg.buf = cbuf; cmsg.maxlen = sizeof(cbuf); bzero(cbuf,sizeof(cbuf)); flags = 0; i = getmsg(fd,&cmsg,&imsg,&flags); if (i < 0) { if (errno != EINTR && errno != EAGAIN) log_errno("getmsg()",""); return 0; } if (cmsg.len > 0) { if (imsg.len+cmsg.len >= sizeof(ibuf)) { log_complain("","discarding buffer with %d" " control and %d data bytes", cmsg.len, imsg.len); return 0; } (void)sprintf(dbgmsg, "discard buffer with %d control and" " %d data bytes", cmsg.len, imsg.len); bcopy(cmsg.buf,&imsg.buf[imsg.len], cmsg.len); log_pkt(1, "", dbgmsg,0, cmsg.len+imsg.len, (u_char*)imsg.buf); return 0; } if (i != 0) { log_complain("","getmsg() returned %d for %d bytes", i, imsg.len); return 0; } if (imsg.len < PPP_BUF_HEAD_FRAME) { (void)sprintf(dbgmsg, "getmsg() returned 0 for %d bytes", imsg.len); log_pkt(0,"",dbgmsg,0,imsg.len,(u_char*)imsg.buf); return 0; } } rcv_msg_ts = clk; ibuf_info_len = imsg.len - PPP_BUF_MIN; /* Recognize HDLC frames that arrive on a sync link before the * PPP framing STREAMS module was pushed onto the stack. * * Do that only until the first valid buffer is received. */ if (!seen_framer && imsg.buf == (char*)&ibuf) { if (imsg.buf[0] == PPP_ADDR && imsg.buf[1] == PPP_CNTL && ppp0.dv.sync == SYNC_ON) { for (i = imsg.len-1; i >= 0; i--) imsg.buf[i+PPP_BUF_HEAD] = imsg.buf[i]; ibuf.type = BEEP_FRAME; ibuf.dev_index = -1; ibuf.bits = 0; ibuf.frame = 0; ibuf_info_len += PPP_BUF_HEAD; log_debug(3, "","salvage early HDLC frame"); } else { seen_framer = 1; } } /* Find the corresponding MP or BF&I MP link */ for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0') continue; /* Messages with bad link numbers are either MP packets * or are from streams not under the mux. * They cannot be non-MP packets from links under the mux. */ if (ibuf.dev_index == -1 && ppp->dv.dev_index == -1 && ibuf.bits == 0) continue; if (ppp->dv.dev_index == ibuf.dev_index) break; } rcv_msg_ppp = ppp; istr[0] = '\0'; nstr = ""; if (ppp0.dv.next != 0 || mindevs > 1 || (ppp == 0 || ibuf.dev_index != -1)) { /* Forget the label if we can be certain only one link * is involved. * * Generate the "index=XXX" string if possible. * Use it only if there is not a better label from the * main structure. */ if (ppp != 0) nstr = ppp->link_name; if (nstr[0] == '\0' && ibuf.dev_index != -1) (void)sprintf(istr,"index=%d ", ibuf.dev_index); } if (ibuf.type <= NUM_BEEPS) { beep_name = beeps[ibuf.type-1]; } else { (void)sprintf(bad_beep, "%#x", ibuf.type); beep_name = bad_beep; } switch (ibuf.type) { default: (void)sprintf(dbgmsg, "read %#x odd bytes:", imsg.len); log_pkt(1, nstr, dbgmsg,0, imsg.len, (u_char*)imsg.buf); break; case BEEP_ACTIVE: case BEEP_WEAK: ppp_status.ps_beep_st = ibuf.un.beep.st; nstr = (ibuf.un.beep.st < (sizeof(acts)/sizeof(acts[0])) ? acts[ibuf.un.beep.st] : "???"); /* if this beep is too soon after a change, then ignore it. */ get_clk(); if (TIME_NOW != cktime(&beepok_time)) { beep_tstamp = ibuf.un.beep.tstamp; beep_ibytes = ibuf.un.beep.raw_ibytes; beep_obytes = ibuf.un.beep.raw_obytes; log_beep_act("ignore ", beep_name, istr, nstr, &ibuf.un.beep); break; } /* compute link speed */ if (beep_tstamp == 0) { ppp_status.ps_cur_ibps = 0; ppp_status.ps_cur_obps = 0; load_bps = 0; } else if ((ticks = (ibuf.un.beep.tstamp-beep_tstamp)) != 0) { ppp_status.ps_cur_ibps = ((ibuf.un.beep.raw_ibytes - beep_ibytes) * HZ * 8.0 / ticks); ppp_status.ps_cur_obps = ((ibuf.un.beep.raw_obytes - beep_obytes) * HZ * 8.0 / ticks); load_bps = MAX(ppp_status.ps_cur_ibps, ppp_status.ps_cur_obps); } beep_tstamp = ibuf.un.beep.tstamp; beep_ibytes = ibuf.un.beep.raw_ibytes; beep_obytes = ibuf.un.beep.raw_obytes; /* "busy" means at least one instant there was more output * on the interface queue than could be sent down * the STREAMS. * "idle" means that the interface queue drained completely. * Each message means the business, idleness, or whatever * has prevailed for the preceding "BEEP" duration. * * If we think we know the speed of the current link, * do not drop it if the recent traffic rate would not * fit on the bundle without the link. * * The ISDN driver does not tell the truth about the incoming * load. Under 100% saturation by TCP traffic, it often does * not say a link is saturated, although it is supposed to * say the link is saturated at or above 90%. So just guess * if we have the the right load and the ISDN driver said * something. */ if (0 != (ibuf.un.beep.st & (BEEP_ST_RX_BUSY|BEEP_ST_TX_BUSY)) || load_bps >= hi_bps || (all_sync && !avail_bps_ok && (ibuf.un.beep.st & BEEP_ST_RX_ACT) && load_bps >= 0.9*56.0*numdevs)) { idle_time.tv_sec = clk.tv_sec+idle_delay; if (busy_time.tv_sec > clk.tv_sec+busy_delay) busy_time.tv_sec = clk.tv_sec+busy_delay; /* Attribute the difference between the current and * previous maximum rates (when the bundle is * saturated) to the bandwidth of the newest link. */ if (avail_bps_ok < 2) { if (!avail_bps_ok || avail_bps < load_bps) { avail_bps = load_bps; avail_bps_ok = 1; get_hi_lo_bps(); } if (prev_avail_bps_ok && newest != 0 && newest->conf_bps == 0) { bps = avail_bps - prev_avail_bps; /* make estimate more real */ if (newest->dv.sync) { if (bps < 60000) bps = 56000; else if (bps < 66000) bps = 64000; else bps = trunc(bps/8000.0)*8000.0; avail_bps = prev_avail_bps + bps; get_hi_lo_bps(); } newest->bps = bps; } } } else { /* Ensure that the clock is ticking to turn off the * link if the kernel stops saying there is traffic. * * Ensure the clock does not run out and turn off * the link if there is more load than capacity. */ if (idle_time.tv_sec > clk.tv_sec+idle_delay || load_bps >= lo_bps) idle_time.tv_sec = clk.tv_sec+idle_delay; busy_time.tv_sec = TIME_NEVER; } log_beep_act("", beep_name, istr, nstr, &ibuf.un.beep); break; case BEEP_DEAD: case BEEP_CP_BAD: (void)sprintf(dbgmsg, "%s: %s", beep_name, istr); log_ipkt(2, nstr, dbgmsg); break; case BEEP_FRAME: /* Deal with MP frames received before the device * is under the STREAMS mux. This can happen if * the peer gets ahead of us. */ if (fd == ppp0.dv.devfd && ibuf.proto == PPP_MP) { (void)sprintf(dbgmsg, "read %#x MP bytes:%s", ibuf_info_len, istr); log_ipkt(6, nstr, dbgmsg); if (ibuf.bits != 0) { log_ipkt(log_mp_ipkt_str(BEEP_MP_ERROR, istr, dbgmsg), nstr, dbgmsg); return 0; } log_ipkt(log_mp_ipkt_str(BEEP_MP, istr, dbgmsg), nstr, dbgmsg); if (mp_recv_ssn) { sn = ibuf.un.mp_s.sn; i = sizeof(struct mp_s); } else { sn = ibuf.un.mp_l.sn; i = sizeof(struct mp_s); } if (ibuf.un.mp_s.mp_bits & MP_B_BIT) { if (mp_sn != -2) log_debug(1,"", "missing MP fragment" " after %d", mp_sn); mp_len = 0; bzero(&mp_buf,sizeof(mp_buf)); mp_buf.type = BEEP_FRAME; mp_sn = sn; } else if (mp_sn+1 != sn) { log_debug(1,"", "missing MP fragment before %d", sn); mp_sn = -2; return 0; } else { mp_sn = sn; } /* expand compressed protocol field */ while (mp_len == 0 && !(mp_buf.proto & 1) && ibuf_info_len > i) { mp_buf.proto <<= 8; mp_buf.proto |= ibuf.un.info[i++]; } ibuf_info_len -= i; if (ibuf_info_len < 0 || ibuf_info_len + mp_len >= PPP_MAX_MTU) { log_debug(1,"", "bad early MP packet %d bytes long", ibuf_info_len + mp_len); mp_sn = -2; return 0; } bcopy(&ibuf.un.info[i],&mp_buf.un.info[mp_len], ibuf_info_len); mp_len += ibuf_info_len; if (!(ibuf.un.mp_s.mp_bits & MP_E_BIT)) return 0; mp_buf.bits = MP_B_BIT|MP_E_BIT; ibuf_info_len = mp_len; bcopy(&mp_buf,&ibuf,sizeof(ibuf)); mp_len = 0; mp_sn = -2; /* forget MP null frames */ if (ibuf_info_len == 0) { (void)sprintf(dbgmsg, "ignore MP null:%s", istr); return 0; } (void)sprintf(dbgmsg, "assemble %#x bytes: %sproto=%#04x", ibuf_info_len, istr, ibuf.proto); } else { (void)sprintf(dbgmsg, "read %#x bytes: %sproto=%#04x", ibuf_info_len, istr, ibuf.proto); } log_ipkt(3, nstr, dbgmsg); break; case BEEP_FCS: case BEEP_ABORT: case BEEP_TINY: case BEEP_BABBLE: (void)sprintf(dbgmsg, "read %#x bytes: %s %sproto=%#04x", imsg.len-PPP_BUF_HEAD-4, beep_name, istr, ibuf.proto); log_ipkt(2, nstr, dbgmsg); break; case BEEP_MP: case BEEP_MP_ERROR: log_ipkt(log_mp_ipkt_str(ibuf.type, istr, dbgmsg), nstr, dbgmsg); break; } return 1; } /* Do not change the number of lines for awhile. */ static void no_beep(int delta) /* 1=added line, -1=lost line, * 0=no change */ { /* No more discretionary lines for a while, so reset the idle * and busy timers, and ignore the kernel for a while. */ beepok_time.tv_sec = clk.tv_sec+BEEP/HZ; idle_time.tv_sec = ((numdevs == 0) ? TIME_NEVER : clk.tv_sec+MAX(idle_delay,BEEP/HZ*2)); busy_time.tv_sec = TIME_NEVER; if (delta > 0) { /* Reset overall failure backoff after success. */ add_time.tv_sec = TIME_NOW; add_backoff = BACKOFF0; } else if (delta < 0) { /* Do not add a line soon after losing one. */ add_time.tv_sec = clk.tv_sec + add_backoff; } } /* send a PPP packet */ void ppp_send(struct ppp *ppp, struct ppp_buf *buf, int info_len) /* length of the INFO field */ { #define SEND_LOG_LVL 3 #define ADDBYTE(c) {if (PPP_ASYNC_MAP((c), ppp->lcp.tx_accm)) { \ *wptr++ = PPP_ESC; *wptr++ = (c) ^ PPP_ESC_BIT; \ } else { \ *wptr++ = (c); \ } \ } struct ppp_buf wbuf; struct strbuf omsg; u_short fcs; u_char c, *wptr; int i; char *mpstr, dbgmsg[50]; if (ppp->dv.devfd >= 0) { if (ppp->dv.sync == SYNC_DEFAULT) log_complain(ppp->lcp.fsm.name, "sync undecided before output"); wptr = &wbuf.frame; /* Start every async frame with a probably redundant * framing byte. */ if (ppp->dv.sync == SYNC_OFF) *wptr++ = PPP_FLAG; fcs = PPP_FCS_INIT; ADDBYTE(PPP_ADDR); fcs = PPP_FCS(fcs,PPP_ADDR); ADDBYTE(PPP_CNTL); fcs = PPP_FCS(fcs,PPP_CNTL); /* We cannot send MP packets until the link is bundled */ if (buf->bits != 0) log_complain("","sending bogus MP bits %#x",buf->bits); /* start sending the frame */ c = buf->proto>>8; ADDBYTE(c); fcs = PPP_FCS(fcs,c); c = buf->proto; ADDBYTE(c); fcs = PPP_FCS(fcs,c); for (i = 0; i < info_len; i++) { ADDBYTE(buf->un.info[i]); fcs = PPP_FCS(fcs,buf->un.info[i]); } /* end the frame */ if (ppp->dv.sync == SYNC_OFF) { fcs ^= PPP_FCS_INIT; ADDBYTE(fcs & 0xff); ADDBYTE(fcs >> 8); *wptr++ = PPP_FLAG; } omsg.buf = (char*)&wbuf.frame; omsg.len = wptr-&wbuf.frame; if (debug >= SEND_LOG_LVL) { (void)sprintf(dbgmsg, "write %#x bytes: proto=%#04x", info_len, buf->proto); log_pkt(SEND_LOG_LVL, ppp->link_name, dbgmsg,0, info_len, buf->un.info); (void)sprintf(dbgmsg, " %#x raw bytes:",omsg.len); log_pkt(SEND_LOG_LVL+1, ppp->link_name, dbgmsg,0, omsg.len, (u_char*)omsg.buf); } if (0 > putmsg(ppp->dv.devfd, 0,&omsg,0)) log_errno("putmsg()",""); } else { wbuf.type = BEEP_FRAME; wbuf.dev_index = ppp->dv.dev_index; if (buf->bits != 0) { mpstr = "(MP)"; wbuf.bits = MP_B_BIT|MP_E_BIT; } else { mpstr = ""; wbuf.bits = 0; } wbuf.frame = PPP_FLAG; wbuf.addr = PPP_ADDR; wbuf.cntl = PPP_CNTL; bcopy(&buf->proto, &wbuf.proto, info_len+sizeof(wbuf.proto)); omsg.buf = (char*)&wbuf; omsg.len = &wbuf.un.info[info_len]-(u_char*)&wbuf; if (debug >= SEND_LOG_LVL) { (void)sprintf(dbgmsg, "send %#x bytes: index=%d proto=%#04x%s", info_len, wbuf.dev_index, buf->proto, mpstr); log_pkt(SEND_LOG_LVL, ppp->link_name, dbgmsg,0, info_len, buf->un.info); } if (0 > putmsg(modfd, 0,&omsg,0)) log_errno("putmsg()",""); } #undef ADDBYTE #undef SEND_LOG_LVL } /* process an incoming packet. * We already know that the buffer is a PPP packet and not a message * from the driver. * * The driver uncompresses the address, control, and protocol fields * on input, and strips the flag, address, control, and FCS fields */ static void ipkt(struct ppp *ppp) { static struct { u_short act; u_short proto; char *msg; } *tp, t[] = { {2, PPP_IP, "stray IP packet"}, {2, PPP_VJC_COMP,"stray IP packet"}, {2, PPP_VJC_UNCOMP, "stray IP packet"}, {2, PPP_MP, "stray MP packet"}, {2, PPP_CP, "stray compressed packet"}, {1, PPP_CP_MULTI,"compression below multi-link"}, {1, PPP_CCP_MULTI,"compression below multi-link"}, {1, PPP_LQR, "LQR packet"}, {1, PPP_BCP, "Bridging Control Protocol (BCP)"}, {1, PPP_BP, "Bridging Protocol (BP)"}, {1, PPP_BACP, "Bandwidth Allocation Control Protocol (BACP)"}, {1, PPP_BAP, "Bandwidth Allocation Protocol (BAP)"}, {1, PPP_IPXCP, "IPX Control Protocol"}, {1, PPP_IPXP, "IPX Protocol"}, {1, 0x3f, "NETBIOS Framing"}, {1, 0x803f, "NETBIOS Framing Control Protocol"}, {1, 0x29, "Appletalk"}, {1, 0x8029, "Appletalk Control Protocol"}, {1, 0x53, "Encryption"}, {1, 0x8053, "Encryption Control Protocol"}, {1, 0x55, "Individual Link Encryption"}, {1, 0x8055, "Individual Link Encryption Control Protocol"}, {1, 0, "unrecognized protocol"} }; switch (ibuf.proto) { case PPP_LCP: lcp_ipkt(ppp); break; case PPP_PAP: pap_ipkt(ppp); break; case PPP_CHAP: chap_ipkt(ppp); break; case PPP_IPCP: ipcp_ipkt(ppp); break; case PPP_CCP: ccp_ipkt(mp_ppp ? mp_ppp : ppp); break; default: for (tp = t; tp->proto != 0 && tp->proto != ibuf.proto; tp++) continue; switch (tp->act) { case 1: log_debug(tp->act, ppp->link_name, "Protocol-Rejecting %s %#x", tp->msg, ibuf.proto); lcp_pj(ppp, ppp->lcp.fsm.name); break; case 2: log_ipkt(2, ppp->link_name, tp->msg); break; } } } /* log the INFO field of a packet */ static void log_pkt(int lvl, char* name, char *msg1, char *msg2, int buf_len, u_char *buf) { # define PAT_SIZE 200 char str[PAT_SIZE+3+4+2]; int pos, b, i, cmode, cmode_ok, c; if (debug < lvl) return; (void)strcpy(str, msg1); if (msg2) (void)strcat(str, msg2); pos = strlen(str); /* See if there is an ASCII string in the packet. * Unless there is at least one string of 4 characters, do not * decode any of the packet as ASCII. */ i = 0; cmode_ok = 0; b = 0; while (b < buf_len) { c = buf[b++]; if (isalnum(c) || c == ' ') { if (++i >= 4) { cmode_ok = 1; break; } } else { i = 0; } } b = 0; cmode = 0; while (b < buf_len) { if (pos >= PAT_SIZE) { (void)strcpy(&str[pos], cmode ? "\" ..." : " ..."); cmode = 0; break; } c = buf[b++]; /* It is too bad that ctype has been "improved" so * that isprint() no longer gives reliable answers. * * Display the current character as printable if it * is and if previous characters were or if at least * two more printable characters follow it. */ if (cmode_ok && (c >= ' ' && c < 0x7f) && (cmode || (b+2 < buf_len && (buf[b] >= ' ' && buf[b] < 0x7f) && (buf[b+1] >= ' ' && buf[b+1] < 0x7f)))) { i = sprintf(&str[pos], cmode ? "%c" : " \"%c", c); cmode = 1; } else { i = sprintf(&str[pos], cmode ? "\" %02x" : " %02x", c); cmode = 0; } if (i < 0) break; pos += i; } if (cmode) (void)strcat(str,"\""); log_debug(lvl, name, "%s", str); } /* log the most recent input packet */ void log_ipkt(int lvl, char* name, char *msg) { char *bstr; switch (ibuf.bits) { case 0: bstr = 0; break; case MP_B_BIT: bstr = "(MP_B)"; break; case MP_E_BIT: bstr = "(MP_E)"; break; case MP_B_BIT|MP_E_BIT: bstr = "(MP)"; break; default: bstr = "(MP?)"; /* (in case RFC 1717 changes) */ break; } log_pkt(lvl, name, msg,bstr, ibuf_info_len, ibuf.un.info); } /* log an activity indication */ static void log_beep_act(char *act, char *beep_name, char *istr, char *nstr, struct beep *bp) { log_debug(2,"", "%s%s: %s%-4s %d %d", act, beep_name, istr, nstr, bp->raw_ibytes, bp->raw_obytes); } static int log_mp_ipkt_str(int type, char *istr, char *dbgmsg) { (void)sprintf(dbgmsg, "%sMP %s%s-%#x", istr, (type == BEEP_MP) ? "" : "Error ", ((ibuf.un.mp_s.mp_bits == MP_B_BIT) ? "B-" : (ibuf.un.mp_s.mp_bits == MP_E_BIT) ? ".E" : (ibuf.un.mp_s.mp_bits != 0) ? "BE" : ".."), (mp_recv_ssn) ? ibuf.un.mp_s.sn : ibuf.un.mp_l.sn); ibuf.bits &= ~(MP_B_BIT|MP_E_BIT); if (type == BEEP_MP) { if ((!mp_recv_ssn && ibuf.un.mp_l.rsvd != 0) || ibuf.un.mp_s.rsvd != 0) return 1; return 5; } else { return 2; } } /* do a STREAMS ioctl */ int /* <0 if error, and message logged */ do_strioctl(struct ppp *ppp, int cmd, struct ppp_arg *arg, char *msg) { struct strioctl strioctl; struct ppp_arg arg0; int res; int fd; strioctl.ic_cmd = cmd; strioctl.ic_timout = 0; if (!arg) { arg = &arg0; bzero(&arg0,sizeof(arg0)); } strioctl.ic_dp = (char*)arg; strioctl.ic_len = sizeof(*arg); if (!ppp) { /* do something to the entire multilink bundle */ arg->dev_index = (__uint32_t)-1; fd = modfd; } else if (ppp->dv.devfd >= 0) { /* do something to a link not yet part of the bundle */ arg->dev_index = (__uint32_t)-1; fd = ppp->dv.devfd; } else { /* do something to a link that is part of the bundle */ arg->dev_index = ppp->dv.dev_index; fd = modfd; } arg->version = 0; res = 1; if (0 > (res = ioctl(fd, I_STR, &strioctl))) { log_errno(msg,""); log_complain("","%s--fd=%d dev_index=%d", msg, fd, arg->dev_index); } else if (arg->version != IF_PPP_VERSION) { log_complain("","%s--wrong kernel module version %d, not %d", msg, arg->version, IF_PPP_VERSION); res = -2; } else { log_debug(7,"","%s--success, fd=%d dev_index=%d", msg, fd, arg->dev_index); } return res; } /* do a boolean STREAMS ioctl */ int /* <0 if error, and message logged */ do_strioctl_ok(struct ppp *ppp, int cmd, u_char ok, char *msg) { struct ppp_arg arg; bzero(&arg,sizeof(arg)); arg.v.ok = ok; return do_strioctl(ppp, cmd, &arg, msg); } /* see about the file and pipe for `pppstat` */ static void ck_status_poke(void) { struct stat st1, st2; if (status_poke_path[0] == '\0') return; /* too early */ if (0 > lstat(status_poke_path, &st1)) { if (errno == ENOENT) { if (status_poke_fd >= 0) log_complain("","%s disappeared", status_poke_path); } else { log_errno("stat() ", status_poke_path); (void)close(status_poke_fd); status_poke_fd = -1; return; } st1.st_mode = 0; } else if (!S_ISFIFO(st1.st_mode)) { (void)unlink(status_poke_path); st1.st_mode = 0; } if (!S_ISFIFO(st1.st_mode)) { if (status_poke_fd >= 0) { (void)close(status_poke_fd); status_poke_fd = -1; } if (0 > mknod(status_poke_path, S_IFIFO, 0)) { log_errno("mknod() ", status_poke_path); return; } if (0 > chmod(status_poke_path, (S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH))) { log_errno("chmod() ", status_poke_path); return; } } if (status_poke_fd >= 0 && (0 > fstat(status_poke_fd, &st2) || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)) { (void)close(status_poke_fd); status_poke_fd = -1; } if (status_poke_fd < 0) { status_poke_fd = open(status_poke_path,O_RDWR|O_NDELAY,0); if (status_poke_fd < 0) { log_errno("open() ", status_poke_path); return; } strcpy(ppp_status.ps_rem_sysname, rem_sysname); ppp_status.ps_lochost = lochost.sin_addr; ppp_status.ps_remhost = remhost.sin_addr; update_status(); } } /* see about the rendezvous point */ static void ck_rend(char* msg_pat) { if (make_rend(1)) { log_complain("", msg_pat, remote); unmade_rend(); cleanup(1); } ck_status_poke(); } /* Poke the existing daemon (if there is one) and wait for it to kill us. */ void do_rend(struct ppp *ppp) { ppp->lochost.sin_addr.s_addr = (def_lochost ? 0 : lochost.sin_addr.s_addr); ppp->remhost.sin_addr.s_addr = (def_remhost ? 0 : remhost.sin_addr.s_addr); (void)strcpy(ppp->loc_epdis, loc_epdis); (void)strcpy(ppp->rem_epdis, rem_epdis); unmade_rend(); /* do not remove the node if we die */ log_debug(1,"","pass device"); no_signals(killed); if (0 > write(rendnode, &ppp0, sizeof(ppp0))) bad_errno("write(caller poke) ",ppp->dv.line); /* quit immediately if only reporting failure */ if (ppp->nowait == 2) exit(0); log_debug(4,"","waiting to be killed"); /* After the existing daemon has had time to grab the devices, * close them. They main device must not be closed too soon or * the STREAMS stack will be messed up. */ sginap(5*HZ); if (ppp->nowait != 0) { log_complain("","no signal received"); exit(0); } die(ppp); } /* set MTU in the kernel. */ static int /* 0=failed */ set_mtu(struct ppp *ppp) { struct ppp_arg arg; bzero(&arg,sizeof(arg)); kern_ip_mtu = (ppp->conf.ip_mtu < PPP_MIN_MTU ? PPP_DEF_MRU : ppp->conf.ip_mtu); if (kern_ip_mtu > ppp->lcp.neg_mtru) kern_ip_mtu = ppp->lcp.neg_mtru; arg.v.mtu.mtru = ppp->lcp.neg_mtru; arg.v.mtu.ip_mtu = kern_ip_mtu; if (qmax >= 0) { arg.v.mtu.qmax = qmax; } else { /* Pick a queue length that will avoid dropping packets * if the user picks a tiny MTU. Make it big enough * to handle one full stream of data transmitted * and ACKs for one full stream of data received. */ arg.v.mtu.qmax = 1+((DEF_WINDOW*3/2) /(kern_ip_mtu - sizeof(struct ip))); if (arg.v.mtu.qmax < IFQ_MAXLEN) arg.v.mtu.qmax = IFQ_MAXLEN; } if (bigxmit == -1) { arg.v.mtu.bigxmit = kern_ip_mtu/4; if (arg.v.mtu.bigxmit < PPP_MIN_MTU) arg.v.mtu.bigxmit = PPP_MIN_MTU; } else { arg.v.mtu.bigxmit = bigxmit; } arg.v.mtu.telnettos = telnettos; if (0 > do_strioctl(0, SIOC_PPP_MTU, &arg, "_MTU")) return 0; return 1; } /* Configure the IP part of the link. */ static int /* 0=failed */ make_ip(struct ppp *ppp) { static char remoteaddr[sizeof("REMOTEADDR=111.222.333.444")+1]; struct stat sbuf; char *p, *p1; int i; # define RCMDV "route -n " # define RCMD "route -n -q " # define SIZE_RCMD sizeof(RCMD "111.222.333.444") /* worst case */ char *rcmd; /* Poke the existing daemon and wait for it to kill us * if it exists. */ (void)add_rend_name("",remhost_str); if (make_rend(1) || add_pid == 0) { if (reconfigure) { log_complain("","premature make_ip()"); return 0; } do_rend(ppp); } /* set up the interface */ if (modfd < 0) { modfd = open("/dev/if_ppp", O_RDWR | O_NONBLOCK); if (0 > modfd) bad_errno("open() ","/dev/if_ppp)"); /* get the unit number */ if (0 > fstat(modfd, &sbuf)) { log_errno("fstat(unit#)",""); return 0; } ppp->dv.unit = minor(sbuf.st_rdev); sprintf(ifname, "ppp%d", ppp->dv.unit); (void)sprintf(status_path, RENDDIR_STATUS_PAT, ppp0.dv.unit); (void)sprintf(status_poke_path, RENDDIR_STATUS_POKE_PAT, ppp0.dv.unit); ck_status_poke(); proxy_arp(); set_ip(); def_lochost = 0; /* addresses cannot change now */ def_remhost = 0; if (kern_ip_mtu <= 0 && !set_mtu(ppp)) return 0; if (noicmp) (void)do_strioctl(0, SIOC_PPP_NOICMP, 0, "_NOICMP"); if (afilter != 0) { struct strioctl strioctl; strioctl.ic_cmd = SIOC_PPP_AFILTER; strioctl.ic_timout = 0; strioctl.ic_len = sizeof(afilter); strioctl.ic_dp = (char*)&afilter; if (0 > ioctl(modfd, I_STR, &strioctl)) log_errno("_AFILTER",""); } /* put the remote hostname and string into the environment * for the scripts and route commands. */ sprintf(remoteaddr, "REMOTEADDR=%s", remhost_str); (void)putenv(remoteaddr); /* run optional initial script */ if (start_cmd != 0) call_system("", "start_cmd: ", start_cmd, 1); /* Install a default route if asked. * Decide each time whether to be verbose in case * debugging has been turned on or off. */ if (add_route != 0) { if (add_route[0] == '\0' || (add_route[0] == '-' && add_route[1] == '\0')) { free(add_route); #ifdef PPP_IRIX_53 add_route=strdup("add default $REMOTEADDR 1"); #else add_route = strdup("add default $REMOTEADDR"); #endif } /* build `route delete ...` command */ if (conf_del_route && (del_route || !strncmp(add_route,"add ",4))) { if (!del_route) { /* find first 2 or 3 words after * "add" in add-route command. */ p1 = &add_route[4]; while (*p1 == ' ') p1++; p = p1; if (!strncmp(p,"net ",4) || !strncmp(p,"host ",5)) { while (*p != ' ') p++; while (*p == ' ') p++; } while (*p != ' ' && *p != '\0') p++; while (*p == ' ') p++; while (*p != ' ' && *p != '\0') p++; if (del_route) free(del_route); i = sizeof("delete ") +strlen(p1) +1; del_route = malloc(i); strcpy(del_route, "delete "); strncat(del_route, p1, p-p1); del_route[i-1] = '\0'; } p = malloc(SIZE_RCMD + strlen(del_route)); strcpy(p, debug ? RCMDV : RCMD); strcat(p, del_route); free(del_route); del_route = p; } /* build `route add ...` command * Preface it with the `route delete` command to * deal with any previous stray routes. */ if (del_route != 0) { rcmd = malloc(strlen(del_route)+2 +SIZE_RCMD +strlen(add_route)+1); sprintf(rcmd, "%s; %s%s", del_route, debug ? RCMDV : RCMD, add_route); } else { rcmd = malloc(SIZE_RCMD+strlen(add_route)+1); sprintf(rcmd, "%s%s", debug ? RCMDV : RCMD, add_route); } /* execute the `route add` command */ call_system("", "add_route: ", rcmd, 1); } #if defined(PPP_IRIX_53) || defined(PPP_IRIX_62) /* try to get the routes restored faster */ if (debug) { kludge_stderr(); (void)system("set -x; killall -v -ALRM routed 1>&2"); (void)system("set -x; killall -v -USR1 gated 1>&2"); } else { (void)system("killall -ALRM routed 1>&2"); (void)system("killall -USR1 gated 1>&2"); } #endif } return 1; } /* Turn MP on or off in the kernel. */ int /* >=0 on success */ set_mp(struct ppp *ppp) { struct ppp *ppp1; struct ppp_arg arg; /* Do nothing if MP could not yet be on. * If we have not happy with LCP, we cannot know if MP is * going to be used or not. However, we may have previously * been completed the Establish phase and only recently received * a new LCP Configure-Request or Authenication packet. */ if (ppp->phase <= AUTH_PHASE && !mp_known) return 0; mp_on = ppp->lcp.neg_mp; if (!mp_known) { log_debug(4,ppp->link_name, mp_on ? "MP known on" : "MP known off"); mp_known = 1; } if (mp_on) { if (mp_ppp != ppp) { if (mp_ppp != 0) { log_debug(6,ppp->link_name, "switch mp_ppp from %s", mp_ppp->link_name); ppp->ccp = mp_ppp->ccp; ppp->ipcp = mp_ppp->ipcp; } mp_ppp = ppp; fix_names(); } } else { if (mp_ppp != 0) { mp_ppp = 0; mp_ncp_bits = 0; fix_names(); } } /* We can only note MP if the link is not yet part of the bundle. */ if (ppp->dv.devfd >= 0) { log_debug(6,ppp->link_name, "postpone setting MP since devfd=%d dev_index=%d", ppp->dv.devfd, ppp->dv.dev_index); return 0; } bzero(&arg,sizeof(arg)); if (ppp->lcp.neg_mp) { mp_ncp_bits = MP_B_BIT|MP_E_BIT; arg.v.mp.on = 1; mp_send_ssn = ppp->lcp.neg_send_ssn; mp_recv_ssn = ppp->lcp.neg_recv_ssn; arg.v.mp.min_frag = (ppp->conf.frag_size != 0 ? ppp->conf.frag_size : PPP_MIN_MTU); if (arg.v.mp.min_frag > PPP_MIN_MTU) arg.v.mp.min_frag = PPP_MIN_MTU; arg.v.mp.max_frag = ppp->lcp.neg_mtu; if (mp_send_ssn) { arg.v.mp.max_frag -= sizeof(struct mp_s); arg.v.mp.min_frag -= sizeof(struct mp_s); } else { arg.v.mp.max_frag -= sizeof(struct mp_l); arg.v.mp.min_frag -= sizeof(struct mp_l); } arg.v.mp.reasm_window = ((ppp->conf.reasm_window > 0) ? ppp->conf.reasm_window : 32); arg.v.mp.send_ssn = mp_send_ssn; arg.v.mp.recv_ssn = mp_recv_ssn; /* We must always use MP headers if MTRU > MTU. * So find the smallest MTU and compare that to the MTRU. */ if (!(arg.v.mp.must_use_mp = ppp->conf.mp_headers)) { for (ppp1 = &ppp0; ppp1 != 0; ppp1 = (struct ppp*)ppp1->dv.next) { if (ppp1->dv.line[0] == '\0') continue; if (ppp1->lcp.neg_mtru > ppp1->lcp.neg_mtu) { arg.v.mp.must_use_mp = 1; break; } } } arg.v.mp.mp_nulls = ppp->conf.mp_nulls; if (debug >=4) arg.v.mp.debug = 2; else if (debug >= 2) arg.v.mp.debug = 1; else arg.v.mp.debug = 0; } else { arg.v.mp.max_frag = PPP_MAX_MTU; } if (!bcmp(&arg.v.mp, &ppp->lcp.arg_mp_set, sizeof(ppp->lcp.arg_mp_set))) return 0; log_debug(6,ppp->link_name, mp_on?"turn on MP: headers %s, mp.debug=%d" : "turn off MP", arg.v.mp.must_use_mp ? "required" : "optional", arg.v.mp.debug); if (0 > do_strioctl(ppp, SIOC_PPP_MP, &arg, "_MP")) return -1; bcopy(&arg.v.mp, &ppp->lcp.arg_mp_set, sizeof(ppp->lcp.arg_mp_set)); return 0; } /* Connect this line to the others */ int /* >=0 on success */ link_mux(struct ppp *l_ppp) { struct ppp *ppp = 0; struct ppp_arg arg; int i; /* If the IP addresses are not yet known, then we cannot link * the line to the STREAMS mux. */ if (lochost.sin_addr.s_addr == 0 || def_lochost) { log_debug(6,l_ppp->link_name, "postpone I_LINK: local IP address=%s" " and def_lochost=%d", ip2str(lochost.sin_addr.s_addr), def_lochost); return set_mp(l_ppp); } if (remhost.sin_addr.s_addr == 0 || def_remhost) { log_debug(6,l_ppp->link_name, "postpone I_LINK: remote IP address=%s" " and def_remhost=%d", ip2str(remhost.sin_addr.s_addr), def_remhost); return set_mp(l_ppp); } /* ensure that the IP interface (and so mux/driver) exists */ if (!make_ip(l_ppp)) return -1; /* Link the device STREAMS stack to the driver or mux. */ if (l_ppp->dv.devfd >= 0) { /* We cannot put the link under the STREAMS mux until the RX * ACCM is known, since switching the RX ACCM requires * stepping input one packet at time, and since that facility * is available only to links not under the mux. It does not * work for links under the mux, because IP packets do not go * to the daemon, so the daemon cannot know when to trigger * another input packet. */ if (l_ppp->phase < AUTH_PHASE) { log_debug(6,l_ppp->link_name, "postpone I_LINK: since in %s Phase", phase_name(l_ppp->phase)); return set_mp(l_ppp); } i = ioctl(modfd,I_LINK,l_ppp->dv.devfd); if (i < 0) { log_errno("I_LINK",""); return -1; } l_ppp->dv.dev_index = i; l_ppp->dv.devfd_save = l_ppp->dv.devfd; l_ppp->dv.devfd = -1; log_debug(6,l_ppp->link_name,"I_LINK to MUX with index %d", l_ppp->dv.dev_index); /* ensure the mux knows about the tx ACCM, MTU, etc. */ if (0 > lcp_set(l_ppp,1)) return -1; } /* Turn MP on or off in the kernel. * A side effect of turning on MP is enabling IP input for the * individual link, and disabling/turning on the compression gates * for the individual link in the PPP framer, since the module is in * charge with MP. * Note that this can change mp_ppp; */ if (0 > set_mp(l_ppp)) return -1; ppp = mp_ppp ? mp_ppp : l_ppp; /* Turn on input in the framer if we just now put the link * under the mux. This is redundant if MP is on. */ if (l_ppp->in_stop != 0) { log_debug(6,l_ppp->link_name, "SIOC_PPP_RX_OK=1 since in_stop=%d", l_ppp->in_stop); if (0 > do_strioctl_ok(l_ppp, SIOC_PPP_RX_OK, 1, "link_mux ioctl(mod, RX_OK on)")) return -1; l_ppp->in_stop = 0; } /* That is all for now if it is not yet time to turn on IP. */ if (l_ppp->phase != NET_PHASE || ppp->phase != NET_PHASE) return 1; /* Start IP header compression mechanisms for this link. * This is necessary if we just created the mux module * (so it does not know about VJ compression) or if * IPCP renegotiated. * * If VJ header compression is turned off, then use the * machinery only to count active TCP connections. */ bzero(&arg,sizeof(arg)); if (!ppp->ipcp.tx_comp) { arg.v.vj.tx_slots = DEF_VJ_SLOT; arg.v.vj.rx_slots = MIN_VJ_SLOT; } else { arg.v.vj.tx_comp = ppp->ipcp.tx_comp; arg.v.vj.tx_compslot = ppp->ipcp.tx_compslot; arg.v.vj.tx_slots = ppp->ipcp.tx_slots; arg.v.vj.rx_slots = ppp->ipcp.rx_slots; } arg.v.vj.link_num = l_ppp->link_num-1; if (0 > do_strioctl(l_ppp, SIOC_PPP_VJ, &arg, "_VJ")) return -1; /* let the bundle use the link */ return do_strioctl_ok(l_ppp, SIOC_PPP_TX_OK, 1, "ioctl(link_mux() TX_OK on)"); } /* reject a rendezvous */ static void rend_rej(struct ppp *ppp, char *pat, ...) { char pbuf[200]; va_list args; va_start(args, pat); vsprintf(pbuf, pat, args); va_end(args); log_complain(ppp->link_name, "reject %s: %s", ppp->dv.line, pbuf); ppp->dv.active = TIME_NEVER; set_tr_msg(&ppp->lcp.fsm, "%.*s", TR_MSG_MAX-1, pbuf); lcp_event(ppp,FSM_CLOSE); } char * epdis_msg(char *type, char *new_epdis, char *old_epdis) { static char buf[60+2*sizeof(loc_epdis)+1]; if (new_epdis[0] == '\0') { sprintf(buf, "no %s End Point Discrimitor instead of %s", type, old_epdis); } else { sprintf(buf, "%s End Point Discrimitor %s instead of %s", type, new_epdis, old_epdis); } return buf; } /* see what the other daemon wants */ int /* 1=took his device */ rendezvous(int mode) /* 0=kill him. 1=take his device */ { struct ppp *ppp, *ppp2; struct ppp nppp; int newfd; FILE *lf; pid_t ckpid; int i; /* We are probably about to add a second link to a multilink bundle. * This new link might last longer than the previous link(s). * * If this daemon was originally started by an incoming phone * call, then it would be bad if when that original line died, * `init` continued to wait for the original process to exit. * The original process would remain to control the second line, * and so `init` would never realize the line is free. * * So start a child to be the main process, and leave the * original process sitting around undead. As long as the original * process exists, `init` will know the line is busy and not * create a new getty that would mess things up. When the phone * line is shut down, the original, now undead main process is * killed, which releases `init` to restart `getty` on the line. */ if (ppp0.dv.line[0] != '\0' && ppp0.nowait == 0 && ppp0.dv.rendpid < 0 && ppp0.dv.callmode == CALLEE && numdevs == 1 && maxdevs > 1) { switch (ckpid = fork()) { case -1: log_errno("demote fork() ",""); break; case 0: ppp0.dv.rendpid = ppp0.mypid; ppp0.mypid = ppp_status.ps_pid = getpid(); if (ppp0.dv.lockfile[0] != '\0') { lf = fopen(ppp0.dv.lockfile,"r+"); if (!lf) { log_errno("fopen(demote) lockfile", ppp0.dv.lockfile); } else { (void)fprintf(lf, LOCKPID_PAT, ppp0.mypid); (void)fclose(lf); } } break; default: /* the parent is treated like a helping child */ log_debug(2,"", "demote PID %d to watch %s and promote %d", ppp0.mypid, ppp0.dv.line, ckpid); add_pid = 0; no_signals(killed); die(&ppp0); break; } } /* Read more than the largest possible structure in case the * binary changes. */ i = read(rendnode,&nppp,sizeof(nppp)); if (i != sizeof(nppp)) { if (i < 0) log_errno("read(rendnode) rendezvous", ""); else log_complain("","bogus %d byte rendezvous message",i); /* flush junk from the FIFO */ while (0 < read(rendnode,&nppp,sizeof(nppp))) continue; return 0; } if (nppp.version != ppp0.version) { log_complain("","incompatible structure version %d", nppp.version); /* flush junk from the FIFO */ while (0 < read(rendnode,&nppp,sizeof(nppp))) continue; return 0; } /* combine accounting */ ppp0.dv.acct.calls += nppp.dv.acct.calls; ppp0.dv.acct.attempts += nppp.dv.acct.attempts; ppp0.dv.acct.failures += nppp.dv.acct.failures; ppp0.dv.acct.answered += nppp.dv.acct.answered; ppp0.dv.acct.setup += nppp.dv.acct.setup; if (nppp.dv.acct.min_setup != 0) { if (ppp0.dv.acct.min_setup > nppp.dv.acct.min_setup || ppp0.dv.acct.min_setup == 0) ppp0.dv.acct.min_setup = nppp.dv.acct.min_setup; if (ppp0.dv.acct.max_setup < nppp.dv.acct.max_setup || ppp0.dv.acct.max_setup == 0) ppp0.dv.acct.max_setup = nppp.dv.acct.max_setup; } /* Kill the process if it was only reporting failure, */ if (nppp.dv.lockfile[0] == '\0') { if (add_pid == nppp.mypid && modfd >= 0 && numdevs == 0 && 0 > ioctl(modfd, TCFLSH, 2)) log_errno("ioctl(modfd,TCFLSH,2)",""); (void)kill(nppp.mypid, SIGINT); log_debug(1,"","accept failed call info from pid %d", nppp.mypid); return 0; } lf = fopen(nppp.dv.lockfile,"r+"); if (!lf) { log_errno("fopen(rendezvous) lockfile ", nppp.dv.lockfile); (void)kill(nppp.mypid,SIGINT); return 0; } if (1 != fscanf(lf, "%ld", &ckpid)) { log_complain("","bogus rendezvous lock file: %s", nppp.dv.lockfile); (void)kill(nppp.mypid,SIGINT); (void)fclose(lf); return 0; } if (nppp.mypid != ckpid || ckpid <= 0) { log_complain("","wrong PID %ld instead of %ld in lockfile %s", ckpid, nppp.mypid, nppp.dv.lockfile); (void)kill(nppp.mypid,SIGINT); if (ckpid > 0 && ckpid != ppp0.mypid) (void)kill(ckpid,SIGINT); (void)fclose(lf); return 0; } if (!mode) { /* should never happen with PPP */ ppp = 0; } else { /* find a free structure */ ppp2 = &ppp0; for (;;) { ppp = ppp2; /* Use the first free structure, provided * the configuration values are the same. * Assume they are the same if the initial * control file line number is the same. */ if (ppp->dv.line[0] == '\0' && (ppp->conf_lnum == nppp.conf_lnum || ppp != &ppp0)) break; ppp2 = (struct ppp*)ppp->dv.next; /* create a new structure if everything is busy. */ if (!ppp2) { ppp2 = malloc(sizeof(*ppp2)); bzero(ppp2,sizeof(*ppp2)); ppp2->link_num = ppp->link_num+1; ppp->dv.next = (struct dev*)ppp2; } } } if (!ppp) { (void)kill(ckpid, SIGINT); (void)unlink(nppp.dv.lockfile); (void)fclose(lf); log_debug(1,"","refuse to rendezvous with pid %ld for %s", ckpid, nppp.dv.line); return 0; } /* Steal his device. */ newfd = open(nppp.dv.line,O_RDWR|O_NDELAY,0); if (newfd < 0) { log_errno("failed to steal ", nppp.dv.line); (void)kill(ckpid, SIGINT); return 0; } sethup(newfd); /* steal the lock */ (void)rewind(lf); (void)fprintf(lf, LOCKPID_PAT, getpid()); stlock(nppp.dv.lockfile); (void)fclose(lf); /* use some of the state from the parent */ init_ppp(ppp); nppp.dv.next = ppp->dv.next; nppp.dv.rendpid = ckpid; nppp.mypid = ppp->mypid; nppp.in_stop = 2; nppp.dv.devfd = newfd; nppp.dv.devfd_save = -1; nppp.dv.dev_index = -1; nppp.dv.acct = ppp0.dv.acct; nppp.dv.acct.sconn = clk.tv_sec; nppp.age = ppp->age; nppp.auth.recv_names.nxt = 0; nppp.link_num = ppp->link_num; bzero(&nppp.lcp.arg_lcp_set,sizeof(nppp.lcp.arg_lcp_set)); bzero(&nppp.lcp.arg_mp_set,sizeof(nppp.lcp.arg_mp_set)); /* use the rest of the state from the child */ bcopy(&nppp,ppp,sizeof(*ppp)); fix_name(ppp); /* restore names after bcopy() */ numdevs_inc(ppp); /* the other process should have handled any premature HDLC frames. */ seen_framer = 1; log_debug(1,ppp->link_name,"took %s from process %d", ppp->dv.line, ckpid); dologin(ppp); if (ppp->nowait != 0) { /* If we do not need to keep getty from grabbing the device * (because we originated the call), then get rid of the * process. */ if (ppp->nowait != 2 && 0 > kill(ckpid, SIGTERM) && (errno != ESRCH || debug >= 1)) log_errno("kill(ckpid)",""); ppp->dv.rendpid = -1; } if (numdevs > maxdevs) { ppp->dv.active = TIME_NEVER; if (maxdevs == 0) { log_debug(1,ppp->link_name, "operator shutdown"); set_tr_msg(&ppp->lcp.fsm, "operator shutdown"); } else { log_complain(ppp->link_name, "more than maxdevs=%d" " links/devices with %s", maxdevs, ppp->dv.line); set_tr_msg(&ppp->lcp.fsm, "more than maxdevs=%d" " links/devices with %.40s", maxdevs, ppp->dv.line); } lcp_event(ppp,FSM_CLOSE); return 1; /* act as if it worked */ } if (kern_ip_mtu > ppp->lcp.neg_mtru) { rend_rej(ppp, "incompatible negotiated MTRU %d instead of %d", ppp->lcp.neg_mtru, kern_ip_mtu); return 1; } if (ppp->lochost.sin_addr.s_addr != 0) { if (ppp->lochost.sin_addr.s_addr != lochost.sin_addr.s_addr) { rend_rej(ppp, "bad local IP address %s instead of %s", ip2str(ppp->lochost.sin_addr.s_addr), lochost_str); return 1; } bzero(&ppp->lochost, sizeof(ppp->lochost)); } if (ppp->remhost.sin_addr.s_addr != 0) { if (ppp->remhost.sin_addr.s_addr != remhost.sin_addr.s_addr) { rend_rej(ppp,"bad remote IP address %s instead of %s", ip2str(ppp->remhost.sin_addr.s_addr), remhost_str); return 1; } bzero(&ppp->remhost, sizeof(ppp->remhost)); } if (mp_known) { if (mp_on != ppp->lcp.neg_mp) { rend_rej(ppp, "incompatible MP parameters;" " mp_on=%d neg_mp=%d", mp_on, ppp->lcp.neg_mp); return 1; } if (mp_on) { if (ppp->lcp.neg_recv_ssn != mp_recv_ssn) { rend_rej(ppp, "incompatible MP parameters;" " mp_recv_ssn=%d neg_recv_ssn=%d", mp_recv_ssn, ppp->lcp.neg_recv_ssn); return 1; } if (ppp->lcp.neg_send_ssn != mp_send_ssn) { rend_rej(ppp, "incompatible MP parameters;" " mp_send_ssn=%d neg_send_ssn=%d", mp_send_ssn, ppp->lcp.neg_send_ssn); return 1; } if (ppp->ccp.fsm.state >= FSM_REQ_SENT_6) { rend_rej(ppp, "CCP started prematurely"); return 1; } if (ppp->ipcp.fsm.state >= FSM_REQ_SENT_6) { rend_rej(ppp, "IPCP started prematurely"); return 1; } } } if (strcmp(rem_epdis, ppp->rem_epdis)) { /* Allow the peer to change its discriminator * only when starting the first link in a bundle. */ if (rem_epdis[0] != '\0') { char *msg = epdis_msg("remote", ppp->rem_epdis, rem_epdis); if ((add_pid > 0 && add_pid != ckpid) || numdevs > 1) { rend_rej(ppp, "inconsistent %s", msg); if (ppp->rem_epdis[0] != '\0') rm_rend("ep-", ppp->rem_epdis); return 1; } log_debug(1,"","change to %s", msg); rm_rend("ep-", rem_epdis); } strcpy(rem_epdis, ppp->rem_epdis); if (rem_epdis[0] != '\0') (void)add_rend_name("ep-", rem_epdis); } if (strcmp(loc_epdis, ppp->loc_epdis)) { /* If we might have switched endpoint discriminators, * then do it. */ if (loc_epdis[0] != '\0') { char *msg = epdis_msg("local", ppp->loc_epdis, loc_epdis); if ((add_pid > 0 && add_pid != ckpid) || numdevs > 0) { rend_rej(ppp, "inconsistent %s", msg); return 1; } log_debug(1,"","change to %s", msg); } strcpy(loc_epdis, ppp->loc_epdis); } if (ppp->rem_sysname[0] != '\0') { if (rem_sysname[0] == '\0') { strcpy(rem_sysname,ppp->rem_sysname); } else if (strcmp(ppp->rem_sysname, rem_sysname)) { static complained = 0; if (rem_epdis[0] == '\0' && !complained) { complained = 1; log_complain(ppp->link_name, "use REM_SYSNAME=%s or %s" " to resolve multiple remote" " names", ppp->rem_sysname, rem_sysname); } } if (!add_rend_name("",ppp->rem_sysname)) { rend_rej(ppp, "%s makes %d rendezvous names", ppp->rem_sysname, num_rends); return 1; } } if (link_mux(ppp) < 0) { rend_rej(ppp, "failed to LINK to mux"); return 1; } /* continue or finish authentication if it had not finished */ if (ppp->phase == AUTH_PHASE) auth_done(ppp); if (ppp->phase == NET_PHASE) { ipcp_go(ppp); if (!ccp_go(ppp)) { /* let data start moving */ /* give up on failure */ ipcp_event(ppp,FSM_CLOSE); set_tr_msg(&ppp->lcp.fsm, "system failure"); lcp_event(ppp,FSM_CLOSE); return 1; } } /* No more discretionary lines for a while after adding one. */ no_beep(1); return 1; } /* Add a line by calling the other machine. * * This always works on the first slot, on ppp0, because it is working on * the first line or because it has been fork()ed to start another line. */ static int /* 0=failed */ addline(void) { int i, j, k, l; int seenflag; fd_set in; struct timeval timer; char c; /* get rid of old PPP structures in case this is a forked child */ while (ppp0.dv.next != 0) { struct ppp *oppp = (struct ppp*)ppp0.dv.next; ppp0.dv.next = oppp->dv.next; free(oppp); } init_ppp(&ppp0); mp_sn = -2; mp_len = 0; mp_buf.proto = 0; seen_framer = 0; ppp0.dv.callmode = CALLER; j = 0; for (;;) { if (j >= num_uucp_nams) { if (num_uucp_nams == 0) { log_complain("","no UUCP name known"); return 0; } j = 0; } (void)strncpy(ppp0.dv.uucp_nam, uucp_names[j], sizeof(ppp0.dv.uucp_nam)); i = get_conn(&ppp0.dv,0); /* Do not add or delete discretionary lines for a while. */ no_beep(0); if (i == 2) /* let caller rendezvous */ return 0; if (i == 1) /* success */ break; /* dialing failed */ if (++j >= num_uucp_nams) { log_complain("", "giving up for now"); if (modfd >= 0) { /* flush attention getters */ while (rcv_msg(modfd)) continue; } return 0; } } numdevs_inc(&ppp0); if (!set_tty_modes(&ppp0.dv)) { rel_dev(&ppp0.dv); return 0; } /* Read the banner from the remote machine. Look for any * packets mixed up in it. */ if (ppp0.dv.sync == SYNC_OFF) { k = 0; seenflag = 0; for (;;) { if (!seenflag) { banlen = 0; banoff = 0; (void)get_clk(); rcv_msg_ts = clk; } timer.tv_sec = 0; timer.tv_usec = 1000000/20; FD_ZERO(&in); FD_SET(ppp0.dv.devfd, &in); i = select(ppp0.dv.devfd+1, &in,0,0, &timer); if (i <= 0) { if (i == 0) break; if (errno == EINTR || errno == EAGAIN) continue; log_errno("banner select()",""); break; } j = banoff+banlen; i = read(ppp0.dv.devfd, &banbuf[j], sizeof(banbuf)-j); if (i == 0) { log_complain("", "EOF while reading banner"); break; } if (i < 0) { log_errno("read(banner)",""); break; } if (Debug && k == 0) { kludge_stderr(); (void)fputs("banner: ",stderr); } l = i+j; while (j < l) { c = banbuf[j]; if (c == PPP_FLAG) { /* note possible start of frame */ if (!seenflag) { seenflag = 1; banoff = j+1; banlen = -banoff; } } c = toascii(c); if (Debug) { if (iscntrl((c))) { c += '@'; (void)fprintf(stderr,"^%c",c); } else { (void)fprintf(stderr,"%c",c); } } j++; k++; } if (seenflag) { banlen += i; if (banlen >= BANSIZE) { banlen = BANSIZE; break; } } } if (Debug && k != 0) { putc('\n',stderr); log_debug(1,"","saving %d bytes to salvage", banlen); } } if (0 >= rdy_tty_dev(&ppp0.dv) || 0 > reset_accm(&ppp0,1)) { rel_dev(&ppp0.dv); return 0; } return 1; } /* Start a child to add a line. * This can only be used when the IP address of the peer is known. */ static int /* 0=are the child, 1=the parent */ fork_addline(int force) { pid_t pid; /* only one child at a time */ if (add_pid >= 0) return 1; /* no children until there is a place to rendezvous. */ if (rendnode <= 0) return 1; /* If we are not sure if we have the right to add a link to the * bundle, add the link only if we created it the oldest link in * the bundle and so the bundle itself. */ if (!force && callmode == CALLEE) return 1; ck_rend("link to %s stolen"); /* Children cannot negotiate our address, because they * might agree to the wrong ones. */ if (lochost.sin_addr.s_addr == 0 || def_lochost || remhost.sin_addr.s_addr == 0 || def_remhost) return 1; /* Children cannot negotiate MP parameters until the parent is * sure of them. So do not create a child to create a second * line while the MP-state of a first line is unknown. */ if (!mp_known && numdevs != 0) return 1; (void)sighold(SIGCHLD); pid = fork(); add_pid = pid; (void)sigrelse(SIGCHLD); switch (pid) { case 0: /* this is the child */ log_debug(1,"","add line #%d", numdevs+1); ppp0.nowait = 1; modfd = -1; status_poke_fd = -1; unmade_rend(); connmode = RE_CALLER; del_route = 0; conf_del_route = 0; conf_proxy_arp = 0; numdevs = 0; clr_acct(&ppp0.dv); if (!addline()) do_rend(&ppp0); /* tell the parent of failure */ return 0; case -1: log_errno("fork()",""); break; } /* This is the parent. */ /* As soon as we know we are going to use multilink, * switch the names in the log. */ fix_names(); /* Do not try too soon again if this attempt fails */ add_time.tv_sec = clk.tv_sec + add_backoff; if (add_backoff < BACKOFF_MAX) add_backoff *= 2; return 1; } /* stop looking for another line */ static void unfork(char *msg) { pid_t pid; if ((pid = add_pid) > 0) { log_debug(1,"", "abort dialing by process %d: %s", pid, msg); (void)kill(pid,SIGTERM); } } /* go to sleep forever */ static void die(struct ppp *ppp) { /* It is worth closing everything in case there is no existing * daemon or the daemon crashes, to minimize keeping the device * turned on. * * Do not simply exit, because if getty is being used, init will start * another getty which will mess up the STREAMS stack. */ rendnode = -1; modfd = -1; status_poke_fd = -1; ppp->dv.devfd = -1; ppp->dv.devfd_save = -1; closefds(); ppp->utmp_type = NO_UTMP; /* sleep here forever */ for (;;) (void)pause(); } /* clear things after the last device is turned off */ static void last_dev(void) { numdevs = 0; oldest = 0; newest = 0; beep_tstamp = 0; prev_avail_bps = 0; prev_avail_bps_ok = 2; avail_bps = 0; avail_bps_ok = 2; mp_ppp = 0; if (ppp0.conf.mp != 0) mp_known = 0; mp_ncp_bits = 0; } /* (re)compute the available bandwidth in the bundle */ static void get_hi_lo_bps(void) { /* lo_bps = prev_avail_bps if known * = avail_bps * (numdevs-1)/numdevs avail_bps known * = avail_bps - newest->bps */ lo_bps = 0; if (prev_avail_bps_ok) { lo_bps = prev_avail_bps; } else if (avail_bps_ok && newest && newest->bps != 0) { lo_bps = avail_bps - newest->bps; } /* hi_bps <= 0.9*sum of sum of links, if known */ hi_bps = (avail_bps_ok) ? (0.9*avail_bps) : MAXFLOAT; } /* housekeeping when adding a new device */ static void numdevs_inc(struct ppp *ppp) { numdevs++; newest_oldest(); if (lactime != 0) ppp->dv.active = clk.tv_sec + lactime; beep_tstamp = 0; prev_avail_bps_ok = avail_bps_ok; prev_avail_bps = avail_bps; if (ppp->bps == 0) { avail_bps_ok = 0; } else { avail_bps += ppp->bps; if (avail_bps_ok == 2 && ppp->conf_bps == 0) avail_bps_ok = 1; } get_hi_lo_bps(); } /* hang up a phone */ void hangup(struct ppp *ppp) { struct ppp *ppp1; if (ppp->dv.devfd >= 0 || ppp->dv.dev_index != -1) { numdevs--; /* No more discretionary lines for a while after losing one. * Delay adding even lines required by mindevs */ no_beep(-1); beep_tstamp = 0; if (numdevs == 0) { last_dev(); } else if (numdevs == 1) { prev_avail_bps = 0; prev_avail_bps_ok = 2; } else if (ppp->bps != 0) { prev_avail_bps -= ppp->bps; } else { prev_avail_bps_ok = 0; } /* recompute available bandwidth */ avail_bps_ok = 2; avail_bps = 0; for (ppp1 = &ppp0; ppp1 != 0; ppp1 = (struct ppp*)ppp1->dv.next) { if (ppp1->dv.line[0] == '\0' || ppp1 == ppp) continue; if (mp_on && mp_ppp == ppp) { ppp1->ccp = mp_ppp->ccp; ppp1->ipcp = mp_ppp->ipcp; mp_ppp = ppp1; } if (ppp1->bps == 0) { avail_bps = 0; avail_bps_ok = 0; } else if (avail_bps_ok != 0) { avail_bps += ppp1->bps; if (ppp1->conf_bps == 0) avail_bps_ok = 1; } } get_hi_lo_bps(); } rel_dev(&ppp->dv); clear_ppp(ppp); newest_oldest(); } /* The kernel says a line is dead. */ static void deadline(struct ppp *ppp) { log_debug(1,ppp->link_name,"%s off", ppp->dv.line); hangup(ppp); } /* see if helper has finished */ static void reap_child(void) { struct ppp *ppp; int st; pid_t pid; for (;;) { pid = waitpid(-1, &st, WNOHANG); if (pid <= 0) { if (pid < 0 && errno != ECHILD) log_errno("waitpid()",""); break; } if (WIFSIGNALED(st) && WTERMSIG(st) != SIGTERM && WTERMSIG(st) != SIGINT) { log_complain("","pid %d killed by signal %d", pid, WTERMSIG(st)); } if (pid == stderrpid) { stderrpid = -1; stderrfd = -1; continue; } /* notice when the slave disappears */ if (pid == add_pid) { add_pid = -1; continue; } for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.rendpid == pid) { ppp->dv.rendpid = -1; break; } } } (void)signal(SIGCHLD, reap_child); } /* Turn off one device */ static void dev_off(struct ppp *ppp, char *msg) { log_debug(1,ppp->link_name,"%s; %s", msg, ppp->dv.line); ppp->dv.active = TIME_NEVER; ppp->dv.acct.call_start = 0; set_tr_msg(&ppp->lcp.fsm, "%.40s", msg); lcp_event(ppp,FSM_CLOSE); } /* Turn off some devices */ static void devs_off(int new, /* 0=all, 1=ours, 2=newest of ours */ char *msg) { struct ppp *ppp, *tgt; int age; age = 0; tgt = 0; for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.line[0] == '\0') continue; if (new == 0) { /* close all links i the bundle */ dev_off(ppp, msg); } else if (ppp->dv.callmode != CALLEE) { /* Close the newest link we created. */ if (new == 1) { dev_off(ppp, msg); } else if (ppp->age >= age) { age = ppp->age; tgt = ppp; } } } if (tgt != 0) dev_off(tgt, msg); /* No more discretionary lines for a while. */ no_beep(0); } /* set MP debugging */ static void set_mpdebug(void) { struct ppp *ppp; for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->dv.devfd >= 0 && ppp->dv.dev_index != -1) { (void)set_mp(mp_ppp); return; } } } /* change debugging */ static void moredebug() { int soc; struct ifreq ifr; ++debug; SET_Debug(); kludge_stderr(); /* tell the kernel to change debugging */ if (debug > 1 &&ifname[0] != '\0') { soc = socket(AF_INET, SOCK_DGRAM, 0); if (soc < 0) log_errno("moredebug socket()",""); (void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (0 > ioctl(soc, SIOCGIFFLAGS, (char*)&ifr)) log_errno("moredebug SIOCGIFFLAGS",""); ifr.ifr_flags |= IFF_DEBUG; (void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (0 > ioctl(soc, SIOCSIFFLAGS, (char*)&ifr)) log_errno("moredebug SIOCSIFFLAGS",""); } if (mp_ppp != 0) set_mpdebug(); log_complain("", "debug=%d", debug); /* reset call backoff timer */ add_time.tv_sec = TIME_NOW; (void)signal(SIGUSR1, moredebug); } static void lessdebug() { int soc; struct ifreq ifr; if (--debug < 1) debug = 0; SET_Debug(); kludge_stderr(); /* tell the kernel to change debugging */ if (debug <= 1 && ifname[0] != '\0') { soc = socket(AF_INET, SOCK_DGRAM, 0); if (soc < 0) log_errno("lessdebug socket()",""); (void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (0 > ioctl(soc, SIOCGIFFLAGS, (char*)&ifr)) log_errno("lessdebug SIOCGIFFLAGS",""); ifr.ifr_flags &= ~IFF_DEBUG; (void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (0 > ioctl(soc, SIOCSIFFLAGS, (char*)&ifr)) log_errno("lessdebug SIOCSIFFLAGS",""); } if (mp_ppp != 0) set_mpdebug(); if (debug == 0) ck_acct(&ppp0.dv, 1); /* dump connect info */ log_complain("", "debug=%d", debug); /* reset call backoff timer */ add_time.tv_sec = TIME_NOW; (void)signal(SIGUSR2, lessdebug); } /* Shut down after SIGINT or SIGTERM */ static void stopint(int sig) { struct ppp *ppp; int numact; if (sig == SIGPIPE) stderrfd = -1; log_debug(interact ? 0 : 1, "", "received signal %d", sig); /* do this only on the first signal */ (void)signal(SIGINT, cleanup); (void)signal(SIGTERM,cleanup); (void)signal(SIGPIPE,cleanup); /* if this is a child, then pass call info to parent */ if (add_pid == 0) { ppp0.nowait = 2; do_rend(&ppp0); } /* do not start any new connections */ maxdevs = 0; outdevs = 0; mindevs = 0; idle_time.tv_sec = TIME_NEVER; busy_time.tv_sec = TIME_NEVER; beepok_time.tv_sec = TIME_NEVER; /* count the active PPP state machines */ numact = 0; for (ppp = &ppp0; ppp != 0; ppp = (struct ppp*)ppp->dv.next) { if (ppp->lcp.fsm.state >= FSM_REQ_SENT_6) numact++; } unfork("operator shutdown"); devs_off(0, "operator shutdown"); if (numact == 0 ) /* if nothing active */ cleanup(sig); /* then quit now */ demand_dial = 0; sactime = 0; /* quit when things are over */ camping = 0; } /* * Record login in wtmp file. */ #define UTYPE struct utmpx #include #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a)) static void clr_ut(struct ppp *ppp, UTYPE *ut, int type) { char *p; bzero(ut,sizeof(*ut)); ut->ut_type = type; ut->ut_pid = getpid(); ut->ut_xtime = time(0); if (ppp->utmp_id[0] == '\0') { (void)sprintf(ut->ut_id, "p%02X%X", ppp->dv.unit, ppp->link_num); } else { bcopy(ppp->utmp_id,ut->ut_id,sizeof(ut->ut_id)); } p = strrchr(ppp->dv.lockfile, '.'); strncpy(ut->ut_line, p ? (p+1) : ppp->dv.nodename, sizeof(ut->ut_line)-1); strncpy(ut->ut_name, ppp->utmp_name, sizeof(ut->ut_name)-1); if (remhost.sin_addr.s_addr != 0 && !def_remhost) strncpy(ut->ut_host,remhost_str, sizeof(ut->ut_host)-1); } void dologin(struct ppp *ppp) { UTYPE *ufound, utmp; /* Do not fiddle with the files more than necessary. * If the utmp entry even has the hostname, then stop now. * If it does not and if we know it, then update it. * Add the record even if we do not know the host name * to log efforts to log in. */ if (ppp->utmp_has_host || (ppp->utmp_type != NO_UTMP && (remhost.sin_addr.s_addr == 0 || def_remhost))) return; if (ppp->utmp_name[0] == '\0') strncpy(ppp->utmp_name, (ppp->auth.recvd_name[0] != '\0' ? ppp->auth.recvd_name : remote), sizeof(ppp->utmp_name)); clr_ut(ppp,&utmp,USER_PROCESS); /* Look for an existing slot for us. */ setutxent(); ufound = getutxline(&utmp); if (!ufound) { setutxent(); ufound = getutxid(&utmp); } if (ufound != 0) { /* update the existing slot. */ SCPYN(ppp->utmp_id, ufound->ut_id); SCPYN(utmp.ut_id, ufound->ut_id); if (ufound->ut_type == USER_PROCESS) { if (ufound->ut_name[0] != '\0') { SCPYN(ppp->utmp_name, ufound->ut_name); SCPYN(utmp.ut_name, ufound->ut_name); } if (ufound->ut_host[0] != '\0') { ppp->utmp_has_host = 1; (void)strcpy(utmp.ut_host, ufound->ut_host); } } if (no_utmp && ppp->utmp_type == NO_UTMP) { endutxent(); return; } if (ppp->utmp_type == NO_UTMP || ufound->ut_pid != utmp.ut_pid || (!ppp->utmp_has_host && utmp.ut_host[0] != '\0')) { if (ppp->utmp_type == NO_UTMP) ppp->utmp_type = PPP_UTMP; if (utmp.ut_host[0] != '\0') ppp->utmp_has_host = 1; pututxline(&utmp); log_debug(6,"","updated utmp: ut_host=\"%s\" type=%d", utmp.ut_host,ppp->utmp_type); } else { log_debug(6,"","do not update utmp:" " remhost=%s def=%d ut_host=\"%s\"", ip2str(remhost.sin_addr.s_addr), def_remhost, ufound->ut_host); } } else if (no_utmp) { ppp->utmp_type = NO_UTMP; endutxent(); return; } else { /* create a new entry */ ppp->utmp_type = PPP_UTMP; setutxent(); (void)pututxline(&utmp); } /* * updwtmpx attempts to update both wtmp and wtmpx, * which must be kept in sync for 'last' to produce * any helpful remote-host info. */ updwtmpx(WTMPX_FILE, &utmp); endutxent(); } /* Record logout in wtmp file */ void dologout(struct dev *dp) { struct ppp *ppp = (struct ppp*)dp; UTYPE *ufound, utmp; /* Do it only if we are in charge of the utmp entry. */ if (ppp->utmp_type != PPP_UTMP) return; clr_ut(ppp,&utmp,DEAD_PROCESS); setutxent(); ufound = getutxid(&utmp); if (!ufound) return; utmp = *ufound; utmp.ut_type = DEAD_PROCESS; utmp.ut_user[0] = '\0'; utmp.ut_xtime = time(0); (void)pututxline(&utmp); endutxent(); updwtmpx(WTMPX_FILE, &utmp); ppp->utmp_type = NO_UTMP; }