diff --git a/usr.sbin/ppp/bundle.c b/usr.sbin/ppp/bundle.c index 40eebfc291c1..a054b132fbfe 100644 --- a/usr.sbin/ppp/bundle.c +++ b/usr.sbin/ppp/bundle.c @@ -1,1213 +1,1237 @@ /*- * Copyright (c) 1998 Brian Somers * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bundle.c,v 1.1.2.64 1998/04/28 01:25:04 brian Exp $ + * $Id: bundle.c,v 1.1.2.65 1998/04/30 23:53:21 brian Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include "command.h" #include "mbuf.h" #include "log.h" #include "id.h" #include "defs.h" #include "timer.h" #include "fsm.h" #include "iplist.h" #include "lqr.h" #include "hdlc.h" #include "throughput.h" #include "slcompress.h" #include "ipcp.h" #include "filter.h" #include "descriptor.h" #include "route.h" #include "lcp.h" #include "ccp.h" #include "link.h" #include "mp.h" #include "bundle.h" #include "async.h" #include "physical.h" #include "modem.h" #include "auth.h" #include "lcpproto.h" #include "chap.h" #include "tun.h" #include "prompt.h" #include "chat.h" #include "datalink.h" #include "ip.h" static const char *PhaseNames[] = { "Dead", "Establish", "Authenticate", "Network", "Terminate" }; const char * bundle_PhaseName(struct bundle *bundle) { return bundle->phase <= PHASE_TERMINATE ? PhaseNames[bundle->phase] : "unknown"; } void bundle_NewPhase(struct bundle *bundle, u_int new) { if (new == bundle->phase) return; if (new <= PHASE_TERMINATE) LogPrintf(LogPHASE, "bundle: %s\n", PhaseNames[new]); switch (new) { case PHASE_DEAD: bundle->phase = new; break; case PHASE_ESTABLISH: bundle->phase = new; break; case PHASE_AUTHENTICATE: bundle->phase = new; bundle_DisplayPrompt(bundle); break; case PHASE_NETWORK: ipcp_Setup(&bundle->ncp.ipcp); FsmUp(&bundle->ncp.ipcp.fsm); FsmOpen(&bundle->ncp.ipcp.fsm); bundle->phase = new; bundle_DisplayPrompt(bundle); break; case PHASE_TERMINATE: bundle->phase = new; mp_Down(&bundle->ncp.mp); bundle_DisplayPrompt(bundle); break; } } static int bundle_CleanInterface(const struct bundle *bundle) { int s; struct ifreq ifrq; struct ifaliasreq ifra; s = ID0socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { LogPrintf(LogERROR, "bundle_CleanInterface: socket(): %s\n", strerror(errno)); return (-1); } strncpy(ifrq.ifr_name, bundle->ifname, sizeof ifrq.ifr_name - 1); ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0'; while (ID0ioctl(s, SIOCGIFADDR, &ifrq) == 0) { memset(&ifra.ifra_mask, '\0', sizeof ifra.ifra_mask); strncpy(ifra.ifra_name, bundle->ifname, sizeof ifra.ifra_name - 1); ifra.ifra_name[sizeof ifra.ifra_name - 1] = '\0'; ifra.ifra_addr = ifrq.ifr_addr; if (ID0ioctl(s, SIOCGIFDSTADDR, &ifrq) < 0) { if (ifra.ifra_addr.sa_family == AF_INET) LogPrintf(LogERROR, "bundle_CleanInterface: Can't get dst for %s on %s !\n", inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr), bundle->ifname); return 0; } ifra.ifra_broadaddr = ifrq.ifr_dstaddr; if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) { if (ifra.ifra_addr.sa_family == AF_INET) LogPrintf(LogERROR, "bundle_CleanInterface: Can't delete %s address on %s !\n", inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr), bundle->ifname); return 0; } } return 1; } static void bundle_LayerStart(void *v, struct fsm *fp) { /* The given FSM is about to start up ! */ } static void bundle_Notify(struct bundle *bundle, char c) { if (bundle->notify.fd != -1) { if (write(bundle->notify.fd, &c, 1) == 1) LogPrintf(LogPHASE, "Parent notified of success.\n"); else LogPrintf(LogPHASE, "Failed to notify parent of success.\n"); close(bundle->notify.fd); bundle->notify.fd = -1; } } static void bundle_LayerUp(void *v, struct fsm *fp) { /* * The given fsm is now up * If it's an LCP set our mtu (if we're multilink, add up the link * speeds and set the MRRU). * If it's an NCP, tell our -background parent to go away. * If it's the first NCP, start the idle timer. */ struct bundle *bundle = (struct bundle *)v; if (fp->proto == PROTO_LCP) { if (bundle->ncp.mp.active) { int speed; struct datalink *dl; for (dl = bundle->links, speed = 0; dl; dl = dl->next) speed += modem_Speed(dl->physical); if (speed) tun_configure(bundle, bundle->ncp.mp.peer_mrru, speed); } else tun_configure(bundle, fsm2lcp(fp)->his_mru, modem_Speed(link2physical(fp->link))); } else if (fp->proto == PROTO_IPCP) { bundle_StartIdleTimer(bundle); bundle_Notify(bundle, EX_NORMAL); } } static void bundle_LayerDown(void *v, struct fsm *fp) { /* * The given FSM has been told to come down. * If it's our last NCP, stop the idle timer. * If it's our last NCP *OR* LCP, enter TERMINATE phase. * If it's an LCP and we're in multilink mode, adjust our tun speed. */ struct bundle *bundle = (struct bundle *)v; if (fp->proto == PROTO_IPCP) { bundle_StopIdleTimer(bundle); } else if (fp->proto == PROTO_LCP) { int speed, others_active; struct datalink *dl; others_active = 0; for (dl = bundle->links, speed = 0; dl; dl = dl->next) if (fp != &dl->physical->link.lcp.fsm && dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) { speed += modem_Speed(dl->physical); others_active++; } if (bundle->ncp.mp.active && speed) tun_configure(bundle, bundle->ncp.mp.link.lcp.his_mru, speed); if (!others_active) bundle_NewPhase(bundle, PHASE_TERMINATE); } } static void bundle_LayerFinish(void *v, struct fsm *fp) { /* The given fsm is now down (fp cannot be NULL) * * If it's the last LCP, FsmDown all NCPs * If it's the last NCP, FsmClose all LCPs */ struct bundle *bundle = (struct bundle *)v; struct datalink *dl; if (fp->proto == PROTO_IPCP) { if (bundle_Phase(bundle) != PHASE_DEAD) bundle_NewPhase(bundle, PHASE_TERMINATE); for (dl = bundle->links; dl; dl = dl->next) datalink_Close(dl, 0); FsmDown(fp); FsmClose(fp); } else if (fp->proto == PROTO_LCP) { int others_active; others_active = 0; for (dl = bundle->links; dl; dl = dl->next) if (fp != &dl->physical->link.lcp.fsm && dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) others_active++; if (!others_active) { FsmDown(&bundle->ncp.ipcp.fsm); FsmClose(&bundle->ncp.ipcp.fsm); /* ST_INITIAL please */ } } } int bundle_LinkIsUp(const struct bundle *bundle) { return bundle->ncp.ipcp.fsm.state == ST_OPENED; } void bundle_Close(struct bundle *bundle, const char *name, int staydown) { /* * Please close the given datalink. * If name == NULL or name is the last datalink, FsmClose all NCPs * (except our MP) * If it isn't the last datalink, just Close that datalink. */ struct datalink *dl, *this_dl; int others_active; if (bundle->phase == PHASE_TERMINATE || bundle->phase == PHASE_DEAD) return; others_active = 0; this_dl = NULL; for (dl = bundle->links; dl; dl = dl->next) { if (name && !strcasecmp(name, dl->name)) this_dl = dl; if (name == NULL || this_dl == dl) { if (staydown) datalink_StayDown(dl); } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) others_active++; } if (name && this_dl == NULL) { LogPrintf(LogWARN, "%s: Invalid datalink name\n", name); return; } if (!others_active) { if (bundle->ncp.ipcp.fsm.state > ST_CLOSED || bundle->ncp.ipcp.fsm.state == ST_STARTING) FsmClose(&bundle->ncp.ipcp.fsm); else { if (bundle->ncp.ipcp.fsm.state > ST_INITIAL) { FsmClose(&bundle->ncp.ipcp.fsm); FsmDown(&bundle->ncp.ipcp.fsm); } for (dl = bundle->links; dl; dl = dl->next) datalink_Close(dl, staydown); } } else if (this_dl && this_dl->state != DATALINK_CLOSED && this_dl->state != DATALINK_HANGUP) datalink_Close(this_dl, staydown); } static int bundle_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) { struct bundle *bundle = descriptor2bundle(d); struct datalink *dl; struct descriptor *desc; int result; result = 0; for (dl = bundle->links; dl; dl = dl->next) result += descriptor_UpdateSet(&dl->desc, r, w, e, n); for (desc = bundle->desc.next; desc; desc = desc->next) result += descriptor_UpdateSet(desc, r, w, e, n); return result; } static int bundle_IsSet(struct descriptor *d, const fd_set *fdset) { struct bundle *bundle = descriptor2bundle(d); struct datalink *dl; struct descriptor *desc; for (dl = bundle->links; dl; dl = dl->next) if (descriptor_IsSet(&dl->desc, fdset)) return 1; for (desc = bundle->desc.next; desc; desc = desc->next) if (descriptor_IsSet(desc, fdset)) return 1; return 0; } static void bundle_DescriptorRead(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct datalink *dl; struct descriptor *desc; for (dl = bundle->links; dl; dl = dl->next) if (descriptor_IsSet(&dl->desc, fdset)) descriptor_Read(&dl->desc, bundle, fdset); for (desc = bundle->desc.next; desc; desc = desc->next) if (descriptor_IsSet(desc, fdset)) descriptor_Read(desc, bundle, fdset); } static void bundle_DescriptorWrite(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct datalink *dl; struct descriptor *desc; for (dl = bundle->links; dl; dl = dl->next) if (descriptor_IsSet(&dl->desc, fdset)) descriptor_Write(&dl->desc, bundle, fdset); for (desc = bundle->desc.next; desc; desc = desc->next) if (descriptor_IsSet(desc, fdset)) descriptor_Write(desc, bundle, fdset); } struct bundle * bundle_Create(const char *prefix, struct prompt *prompt, int type) { int s, enoentcount, err; struct ifreq ifrq; static struct bundle bundle; /* there can be only one */ if (bundle.ifname != NULL) { /* Already allocated ! */ LogPrintf(LogERROR, "bundle_Create: There's only one BUNDLE !\n"); return NULL; } err = ENOENT; enoentcount = 0; for (bundle.unit = 0; ; bundle.unit++) { snprintf(bundle.dev, sizeof bundle.dev, "%s%d", prefix, bundle.unit); bundle.tun_fd = ID0open(bundle.dev, O_RDWR); if (bundle.tun_fd >= 0) break; else if (errno == ENXIO) { err = errno; break; } else if (errno == ENOENT) { if (++enoentcount > 2) break; } else err = errno; } if (bundle.tun_fd < 0) { LogPrintf(LogWARN, "No available tunnel devices found (%s).\n", strerror(err)); return NULL; } LogSetTun(bundle.unit); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { LogPrintf(LogERROR, "bundle_Create: socket(): %s\n", strerror(errno)); close(bundle.tun_fd); return NULL; } bundle.ifname = strrchr(bundle.dev, '/'); if (bundle.ifname == NULL) bundle.ifname = bundle.dev; else bundle.ifname++; /* * Now, bring up the interface. */ memset(&ifrq, '\0', sizeof ifrq); strncpy(ifrq.ifr_name, bundle.ifname, sizeof ifrq.ifr_name - 1); ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0'; if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCGIFFLAGS): %s\n", strerror(errno)); close(s); close(bundle.tun_fd); bundle.ifname = NULL; return NULL; } ifrq.ifr_flags |= IFF_UP; if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) { LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCSIFFLAGS): %s\n", strerror(errno)); close(s); close(bundle.tun_fd); bundle.ifname = NULL; return NULL; } close(s); if ((bundle.ifIndex = GetIfIndex(bundle.ifname)) < 0) { LogPrintf(LogERROR, "OpenTunnel: Can't find ifindex.\n"); close(bundle.tun_fd); bundle.ifname = NULL; return NULL; } prompt_Printf(prompt, "Using interface: %s\n", bundle.ifname); LogPrintf(LogPHASE, "Using interface: %s\n", bundle.ifname); bundle.routing_seq = 0; bundle.phase = PHASE_DEAD; bundle.CleaningUp = 0; bundle.fsm.LayerStart = bundle_LayerStart; bundle.fsm.LayerUp = bundle_LayerUp; bundle.fsm.LayerDown = bundle_LayerDown; bundle.fsm.LayerFinish = bundle_LayerFinish; bundle.fsm.object = &bundle; bundle.cfg.idle_timeout = NCP_IDLE_TIMEOUT; *bundle.cfg.auth.name = '\0'; *bundle.cfg.auth.key = '\0'; bundle.cfg.opt = OPT_IDCHECK | OPT_LOOPBACK | OPT_THROUGHPUT | OPT_UTMP; *bundle.cfg.label = '\0'; bundle.cfg.mtu = DEF_MTU; bundle.phys_type = type; bundle.links = datalink_Create("deflink", &bundle, type); if (bundle.links == NULL) { LogPrintf(LogERROR, "Cannot create data link: %s\n", strerror(errno)); close(bundle.tun_fd); bundle.ifname = NULL; return NULL; } bundle.desc.type = BUNDLE_DESCRIPTOR; bundle.desc.next = NULL; bundle.desc.UpdateSet = bundle_UpdateSet; bundle.desc.IsSet = bundle_IsSet; bundle.desc.Read = bundle_DescriptorRead; bundle.desc.Write = bundle_DescriptorWrite; mp_Init(&bundle.ncp.mp, &bundle); /* Send over the first physical link by default */ ipcp_Init(&bundle.ncp.ipcp, &bundle, &bundle.links->physical->link, &bundle.fsm); memset(&bundle.filter, '\0', sizeof bundle.filter); bundle.filter.in.fragok = bundle.filter.in.logok = 1; bundle.filter.in.name = "IN"; bundle.filter.out.fragok = bundle.filter.out.logok = 1; bundle.filter.out.name = "OUT"; bundle.filter.dial.name = "DIAL"; bundle.filter.dial.logok = 1; bundle.filter.alive.name = "ALIVE"; bundle.filter.alive.logok = 1; memset(&bundle.idle.timer, '\0', sizeof bundle.idle.timer); bundle.idle.done = 0; bundle.notify.fd = -1; /* Clean out any leftover crud */ bundle_CleanInterface(&bundle); if (prompt) { /* Retrospectively introduce ourselves to the prompt */ prompt->bundle = &bundle; bundle_RegisterDescriptor(&bundle, &prompt->desc); } return &bundle; } static void bundle_DownInterface(struct bundle *bundle) { struct ifreq ifrq; int s; DeleteIfRoutes(bundle, 1); s = ID0socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { LogPrintf(LogERROR, "bundle_DownInterface: socket: %s\n", strerror(errno)); return; } memset(&ifrq, '\0', sizeof ifrq); strncpy(ifrq.ifr_name, bundle->ifname, sizeof ifrq.ifr_name - 1); ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0'; if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { LogPrintf(LogERROR, "bundle_DownInterface: ioctl(SIOCGIFFLAGS): %s\n", strerror(errno)); close(s); return; } ifrq.ifr_flags &= ~IFF_UP; if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) { LogPrintf(LogERROR, "bundle_DownInterface: ioctl(SIOCSIFFLAGS): %s\n", strerror(errno)); close(s); return; } close(s); } void bundle_Destroy(struct bundle *bundle) { struct datalink *dl; struct descriptor *desc, *ndesc; if (bundle->phys_type & PHYS_DEMAND) { IpcpCleanInterface(&bundle->ncp.ipcp); bundle_DownInterface(bundle); } dl = bundle->links; while (dl) dl = datalink_Destroy(dl); bundle_Notify(bundle, EX_ERRDEAD); desc = bundle->desc.next; while (desc) { ndesc = desc->next; if (desc->type == PROMPT_DESCRIPTOR) prompt_Destroy((struct prompt *)desc, 1); else LogPrintf(LogERROR, "bundle_Destroy: Don't know how to delete descriptor" " type %d\n", desc->type); desc = ndesc; } bundle->desc.next = NULL; bundle->ifname = NULL; } struct rtmsg { struct rt_msghdr m_rtm; char m_space[64]; }; void bundle_SetRoute(struct bundle *bundle, int cmd, struct in_addr dst, struct in_addr gateway, struct in_addr mask, int bang) { struct rtmsg rtmes; int s, nb, wb; char *cp; const char *cmdstr; struct sockaddr_in rtdata; if (bang) cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!"); else cmdstr = (cmd == RTM_ADD ? "Add" : "Delete"); s = ID0socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) { LogPrintf(LogERROR, "bundle_SetRoute: socket(): %s\n", strerror(errno)); return; } memset(&rtmes, '\0', sizeof rtmes); rtmes.m_rtm.rtm_version = RTM_VERSION; rtmes.m_rtm.rtm_type = cmd; rtmes.m_rtm.rtm_addrs = RTA_DST; rtmes.m_rtm.rtm_seq = ++bundle->routing_seq; rtmes.m_rtm.rtm_pid = getpid(); rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; memset(&rtdata, '\0', sizeof rtdata); rtdata.sin_len = sizeof rtdata; rtdata.sin_family = AF_INET; rtdata.sin_port = 0; rtdata.sin_addr = dst; cp = rtmes.m_space; memcpy(cp, &rtdata, rtdata.sin_len); cp += rtdata.sin_len; if (cmd == RTM_ADD) { if (gateway.s_addr == INADDR_ANY) { /* Add a route through the interface */ struct sockaddr_dl dl; const char *iname; int ilen; iname = Index2Nam(bundle->ifIndex); ilen = strlen(iname); dl.sdl_len = sizeof dl - sizeof dl.sdl_data + ilen; dl.sdl_family = AF_LINK; dl.sdl_index = bundle->ifIndex; dl.sdl_type = 0; dl.sdl_nlen = ilen; dl.sdl_alen = 0; dl.sdl_slen = 0; strncpy(dl.sdl_data, iname, sizeof dl.sdl_data); memcpy(cp, &dl, dl.sdl_len); cp += dl.sdl_len; rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY; } else { rtdata.sin_addr = gateway; memcpy(cp, &rtdata, rtdata.sin_len); cp += rtdata.sin_len; rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY; } } if (dst.s_addr == INADDR_ANY) mask.s_addr = INADDR_ANY; if (cmd == RTM_ADD || dst.s_addr == INADDR_ANY) { rtdata.sin_addr = mask; memcpy(cp, &rtdata, rtdata.sin_len); cp += rtdata.sin_len; rtmes.m_rtm.rtm_addrs |= RTA_NETMASK; } nb = cp - (char *) &rtmes; rtmes.m_rtm.rtm_msglen = nb; wb = ID0write(s, &rtmes, nb); if (wb < 0) { LogPrintf(LogTCPIP, "bundle_SetRoute failure:\n"); LogPrintf(LogTCPIP, "bundle_SetRoute: Cmd = %s\n", cmdstr); LogPrintf(LogTCPIP, "bundle_SetRoute: Dst = %s\n", inet_ntoa(dst)); LogPrintf(LogTCPIP, "bundle_SetRoute: Gateway = %s\n", inet_ntoa(gateway)); LogPrintf(LogTCPIP, "bundle_SetRoute: Mask = %s\n", inet_ntoa(mask)); failed: if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST || (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) { if (!bang) LogPrintf(LogWARN, "Add route failed: %s already exists\n", inet_ntoa(dst)); else { rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE; if ((wb = ID0write(s, &rtmes, nb)) < 0) goto failed; } } else if (cmd == RTM_DELETE && (rtmes.m_rtm.rtm_errno == ESRCH || (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) { if (!bang) LogPrintf(LogWARN, "Del route failed: %s: Non-existent\n", inet_ntoa(dst)); } else if (rtmes.m_rtm.rtm_errno == 0) LogPrintf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr, inet_ntoa(dst), strerror(errno)); else LogPrintf(LogWARN, "%s route failed: %s: %s\n", cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno)); } LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n", wb, cmdstr, (unsigned)dst.s_addr, (unsigned)gateway.s_addr); close(s); } void bundle_LinkClosed(struct bundle *bundle, struct datalink *dl) { /* * Our datalink has closed. * UpdateSet() will remove 1OFF and STDIN links. * If it's the last data link, enter phase DEAD. */ struct datalink *odl; int other_links; other_links = 0; for (odl = bundle->links; odl; odl = odl->next) if (odl != dl && odl->state != DATALINK_CLOSED) other_links++; if (!other_links) { if (dl->physical->type != PHYS_DEMAND) bundle_DownInterface(bundle); if (bundle->ncp.ipcp.fsm.state > ST_CLOSED || bundle->ncp.ipcp.fsm.state == ST_STARTING) { FsmDown(&bundle->ncp.ipcp.fsm); FsmClose(&bundle->ncp.ipcp.fsm); /* ST_INITIAL please */ } bundle_NewPhase(bundle, PHASE_DEAD); bundle_DisplayPrompt(bundle); } } void bundle_Open(struct bundle *bundle, const char *name, int mask) { /* * Please open the given datalink, or all if name == NULL */ struct datalink *dl; for (dl = bundle->links; dl; dl = dl->next) if (name == NULL || !strcasecmp(dl->name, name)) { if (mask & dl->physical->type) datalink_Up(dl, 1, 1); if (name != NULL) break; } } struct datalink * bundle2datalink(struct bundle *bundle, const char *name) { struct datalink *dl; if (name != NULL) { for (dl = bundle->links; dl; dl = dl->next) if (!strcasecmp(dl->name, name)) return dl; } else if (bundle->links && !bundle->links->next) return bundle->links; return NULL; } int bundle_FillQueues(struct bundle *bundle) { int total; if (bundle->ncp.mp.active) { total = mp_FillQueues(bundle); } else { total = link_QueueLen(&bundle->links->physical->link); if (total == 0 && bundle->links->physical->out == NULL) total = IpFlushPacket(&bundle->links->physical->link, bundle); } return total + ip_QueueLen(); } int bundle_ShowLinks(struct cmdargs const *arg) { struct datalink *dl; for (dl = arg->bundle->links; dl; dl = dl->next) prompt_Printf(arg->prompt, "Name: %s [%s]\n", dl->name, datalink_State(dl)); return 0; } static const char * optval(struct bundle *bundle, int bit) { return (bundle->cfg.opt & bit) ? "enabled" : "disabled"; } int bundle_ShowStatus(struct cmdargs const *arg) { int remaining; prompt_Printf(arg->prompt, "Phase %s\n", bundle_PhaseName(arg->bundle)); prompt_Printf(arg->prompt, " Interface: %s\n", arg->bundle->dev); prompt_Printf(arg->prompt, "\nDefaults:\n"); prompt_Printf(arg->prompt, " Label: %s\n", arg->bundle->cfg.label); prompt_Printf(arg->prompt, " Auth name: %s\n", arg->bundle->cfg.auth.name); prompt_Printf(arg->prompt, " Idle Timer: "); if (arg->bundle->cfg.idle_timeout) { prompt_Printf(arg->prompt, "%ds", arg->bundle->cfg.idle_timeout); remaining = bundle_RemainingIdleTime(arg->bundle); if (remaining != -1) prompt_Printf(arg->prompt, " (%ds remaining)", remaining); prompt_Printf(arg->prompt, "\n"); } else prompt_Printf(arg->prompt, "disabled\n"); prompt_Printf(arg->prompt, " MTU: "); if (arg->bundle->cfg.mtu) prompt_Printf(arg->prompt, "%d\n", arg->bundle->cfg.mtu); else prompt_Printf(arg->prompt, "unspecified\n"); prompt_Printf(arg->prompt, " ID check: %s\n", optval(arg->bundle, OPT_IDCHECK)); prompt_Printf(arg->prompt, " Loopback: %s\n", optval(arg->bundle, OPT_LOOPBACK)); prompt_Printf(arg->prompt, " PasswdAuth: %s\n", optval(arg->bundle, OPT_PASSWDAUTH)); prompt_Printf(arg->prompt, " Proxy: %s\n", optval(arg->bundle, OPT_PROXY)); prompt_Printf(arg->prompt, " Throughput: %s\n", optval(arg->bundle, OPT_THROUGHPUT)); prompt_Printf(arg->prompt, " Utmp: %s\n", optval(arg->bundle, OPT_UTMP)); return 0; } static void bundle_IdleTimeout(void *v) { struct bundle *bundle = (struct bundle *)v; bundle->idle.done = 0; LogPrintf(LogPHASE, "Idle timer expired.\n"); bundle_Close(bundle, NULL, 1); } /* * Start Idle timer. If timeout is reached, we call bundle_Close() to * close LCP and link. */ void bundle_StartIdleTimer(struct bundle *bundle) { if (!(bundle->phys_type & (PHYS_DEDICATED|PHYS_PERM))) { StopTimer(&bundle->idle.timer); if (bundle->cfg.idle_timeout) { bundle->idle.timer.func = bundle_IdleTimeout; bundle->idle.timer.name = "idle"; bundle->idle.timer.load = bundle->cfg.idle_timeout * SECTICKS; bundle->idle.timer.arg = bundle; StartTimer(&bundle->idle.timer); bundle->idle.done = time(NULL) + bundle->cfg.idle_timeout; } } } void bundle_SetIdleTimer(struct bundle *bundle, int value) { bundle->cfg.idle_timeout = value; if (bundle_LinkIsUp(bundle)) bundle_StartIdleTimer(bundle); } void bundle_StopIdleTimer(struct bundle *bundle) { StopTimer(&bundle->idle.timer); bundle->idle.done = 0; } int bundle_RemainingIdleTime(struct bundle *bundle) { if (bundle->idle.done) return bundle->idle.done - time(NULL); return -1; } int bundle_IsDead(struct bundle *bundle) { return !bundle->links || (bundle->phase == PHASE_DEAD && bundle->CleaningUp); } void bundle_RegisterDescriptor(struct bundle *bundle, struct descriptor *d) { d->next = bundle->desc.next; bundle->desc.next = d; } void bundle_UnRegisterDescriptor(struct bundle *bundle, struct descriptor *d) { struct descriptor **desc; for (desc = &bundle->desc.next; *desc; desc = &(*desc)->next) if (*desc == d) { *desc = d->next; break; } } void bundle_DelPromptDescriptors(struct bundle *bundle, struct server *s) { struct descriptor **desc; struct prompt *p; desc = &bundle->desc.next; while (*desc) { if ((*desc)->type == PROMPT_DESCRIPTOR) { p = (struct prompt *)*desc; if (p->owner == s) { prompt_Destroy(p, 1); desc = &bundle->desc.next; continue; } } desc = &(*desc)->next; } } void bundle_DisplayPrompt(struct bundle *bundle) { struct descriptor **desc; for (desc = &bundle->desc.next; *desc; desc = &(*desc)->next) if ((*desc)->type == PROMPT_DESCRIPTOR) prompt_Required((struct prompt *)*desc); } void bundle_WriteTermPrompt(struct bundle *bundle, struct datalink *dl, const char *data, int len) { struct descriptor *desc; struct prompt *p; for (desc = bundle->desc.next; desc; desc = desc->next) if (desc->type == PROMPT_DESCRIPTOR) { p = (struct prompt *)desc; if (prompt_IsTermMode(p, dl)) prompt_Printf(p, "%.*s", len, data); } } void bundle_SetTtyCommandMode(struct bundle *bundle, struct datalink *dl) { struct descriptor *desc; struct prompt *p; for (desc = bundle->desc.next; desc; desc = desc->next) if (desc->type == PROMPT_DESCRIPTOR) { p = (struct prompt *)desc; if (prompt_IsTermMode(p, dl)) prompt_TtyCommandMode(p); } } static void bundle_GenPhysType(struct bundle *bundle) { struct datalink *dl; bundle->phys_type = 0; for (dl = bundle->links; dl; dl = dl->next) bundle->phys_type |= dl->physical->type; } void bundle_DatalinkClone(struct bundle *bundle, struct datalink *dl, const char *name) { struct datalink *ndl = datalink_Clone(dl, name); ndl->next = dl->next; dl->next = ndl; bundle_GenPhysType(bundle); } void bundle_DatalinkRemove(struct bundle *bundle, struct datalink *dl) { struct datalink **dlp; if (dl->state == DATALINK_CLOSED) for (dlp = &bundle->links; *dlp; dlp = &(*dlp)->next) if (*dlp == dl) { *dlp = datalink_Destroy(dl); break; } bundle_GenPhysType(bundle); } void bundle_CleanDatalinks(struct bundle *bundle) { struct datalink **dlp = &bundle->links; while (*dlp) if ((*dlp)->state == DATALINK_CLOSED && (*dlp)->physical->type & (PHYS_DIRECT|PHYS_1OFF)) *dlp = datalink_Destroy(*dlp); else dlp = &(*dlp)->next; bundle_GenPhysType(bundle); } void bundle_SetLabel(struct bundle *bundle, const char *label) { if (label) strncpy(bundle->cfg.label, label, sizeof bundle->cfg.label - 1); else *bundle->cfg.label = '\0'; } const char * bundle_GetLabel(struct bundle *bundle) { return *bundle->cfg.label ? bundle->cfg.label : NULL; } void bundle_ReceiveDatalink(struct bundle *bundle, int fd) { struct datalink *dl, *ndl; u_char *buf; int get, got, vlen; /* * We expect: * .---------.---------.--------------. * | ver len | Version | datalink len | * `---------'---------'--------------' * We then pass the rest of the stream to datalink. */ LogPrintf(LogPHASE, "Receiving datalink\n"); vlen = strlen(Version); get = sizeof(int) * 2 + vlen; buf = (u_char *)malloc(get); got = fullread(fd, buf, get); if (got != get) { LogPrintf(LogWARN, "Cannot receive datalink header" " (got %d bytes, not %d)\n", got, get); close(fd); free(buf); return; } if (*(int *)buf != vlen || *(int *)(buf + sizeof(int) + vlen) != sizeof *dl || memcmp(buf + sizeof(int), Version, vlen)) { LogPrintf(LogWARN, "Cannot receive datalink, incorrect version\n"); close(fd); free(buf); return; } free(buf); ndl = datalink_FromBinary(bundle, fd); if (ndl) { /* Make sure the name is unique ! */ do { for (dl = bundle->links; dl; dl = dl->next) if (!strcasecmp(ndl->name, dl->name)) { datalink_Rename(ndl); break; } } while (dl); ndl->next = bundle->links; bundle->links = ndl; bundle_GenPhysType(bundle); LogPrintf(LogPHASE, "%s: Created in %s state\n", ndl->name, datalink_State(ndl)); datalink_AuthOk(ndl); } } void -bundle_SendDatalink(struct datalink *dl, int fd) +bundle_SendDatalink(struct datalink *dl, int ppp_fd) { - int len, link_fd, err; + int len, link_fd, err, nfd, flags; struct datalink **pdl; struct bundle *bundle = dl->bundle; char procname[100]; /* * We send: * .---------.---------.--------------. * | ver len | Version | datalink len | * `---------'---------'--------------' * We then pass the rest of the stream to datalink. */ LogPrintf(LogPHASE, "Transmitting datalink %s\n", dl->name); /* First, un-hook the datalink */ for (pdl = &bundle->links; *pdl; pdl = &(*pdl)->next) if (*pdl == dl) { *pdl = dl->next; dl->next = NULL; break; } /* Write our bit of the data */ err = 0; len = strlen(Version); - if (write(fd, &len, sizeof len) != sizeof len) + if (write(ppp_fd, &len, sizeof len) != sizeof len) err++; - if (write(fd, Version, len) != len) + if (write(ppp_fd, Version, len) != len) err++; len = sizeof(struct datalink); - if (write(fd, &len, sizeof len) != sizeof len) + if (write(ppp_fd, &len, sizeof len) != sizeof len) err++; if (err) { LogPrintf(LogERROR, "Failed sending version\n"); - close(fd); - fd = -1; + close(ppp_fd); + ppp_fd = -1; } - link_fd = datalink_ToBinary(dl, fd); + link_fd = datalink_ToBinary(dl, ppp_fd); if (link_fd != -1) { switch (fork()) { case 0: - snprintf(procname, sizeof procname, "%s <-> %s", - dl->name, *dl->physical->name.base ? - dl->physical->name.base : "network"); + TermTimerService(); + + ppp_fd = fcntl(ppp_fd, F_DUPFD, 3); + link_fd = fcntl(link_fd, F_DUPFD, 3); + nfd = dup2(open(_PATH_DEVNULL, O_WRONLY), STDERR_FILENO); + setsid(); - dup2(link_fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); setuid(geteuid()); - /* signals are defaulted by the exec */ - execlp(PPPMPIPE, procname, NULL); - LogPrintf(LogERROR, "exec: %s: %s\n", PPPMPIPE, strerror(errno)); + + flags = fcntl(ppp_fd, F_GETFL, 0); + fcntl(ppp_fd, F_SETFL, flags & ~O_NONBLOCK); + flags = fcntl(link_fd, F_GETFL, 0); + fcntl(link_fd, F_SETFL, flags & ~O_NONBLOCK); + + switch (fork()) { + case 0: + dup2(ppp_fd, STDIN_FILENO); + dup2(link_fd, STDOUT_FILENO); + snprintf(procname, sizeof procname, "%s -> %s", + dl->name, *dl->physical->name.base ? + dl->physical->name.base : "network"); + execl(CATPROG, procname, NULL); + LogPrintf(LogERROR, "exec: %s: %s\n", CATPROG, strerror(errno)); + break; + case -1: + break; + default: + dup2(link_fd, STDIN_FILENO); + dup2(ppp_fd, STDOUT_FILENO); + snprintf(procname, sizeof procname, "%s <- %s", + dl->name, *dl->physical->name.base ? + dl->physical->name.base : "network"); + execl(CATPROG, procname, NULL); + LogPrintf(LogERROR, "exec: %s: %s\n", CATPROG, strerror(errno)); + break; + } exit(1); break; case -1: LogPrintf(LogERROR, "fork: %s\n", strerror(errno)); - /* Fall through */ - default: break; } } } diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c index 72ac81024fbf..7a8c0b13e24d 100644 --- a/usr.sbin/ppp/command.c +++ b/usr.sbin/ppp/command.c @@ -1,1843 +1,1844 @@ /* * PPP User command processing module * * Written by Toshiharu OHNO (tony-o@iij.ad.jp) * * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the Internet Initiative Japan, Inc. The name of the * IIJ may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: command.c,v 1.131.2.73 1998/04/27 01:40:38 brian Exp $ + * $Id: command.c,v 1.131.2.74 1998/04/30 23:53:29 brian Exp $ * */ #include #include #include #include #include #include #include #include #include #ifndef NOALIAS #include #endif #include #include #include #include #include #include #include #include #include #include #include "command.h" #include "mbuf.h" #include "log.h" #include "defs.h" #include "timer.h" #include "fsm.h" #include "lcp.h" #include "iplist.h" #include "throughput.h" #include "slcompress.h" #include "ipcp.h" #include "modem.h" #ifndef NOALIAS #include "alias_cmd.h" #endif #include "lqr.h" #include "hdlc.h" #include "loadalias.h" #include "systems.h" #include "filter.h" #include "descriptor.h" #include "main.h" #include "route.h" #include "ccp.h" #include "auth.h" #include "async.h" #include "link.h" #include "physical.h" #include "mp.h" #include "bundle.h" #include "server.h" #include "prompt.h" #include "chat.h" #include "chap.h" #include "datalink.h" /* ``set'' values */ #define VAR_AUTHKEY 0 #define VAR_DIAL 1 #define VAR_LOGIN 2 #define VAR_AUTHNAME 3 #define VAR_WINSIZE 4 #define VAR_DEVICE 5 #define VAR_ACCMAP 6 #define VAR_MRRU 7 #define VAR_MRU 8 #define VAR_MTU 9 #define VAR_OPENMODE 10 #define VAR_PHONE 11 #define VAR_HANGUP 12 #define VAR_ENC 13 #define VAR_IDLETIMEOUT 14 #define VAR_LQRPERIOD 15 #define VAR_LCPRETRY 16 #define VAR_CHAPRETRY 17 #define VAR_PAPRETRY 18 #define VAR_CCPRETRY 19 #define VAR_IPCPRETRY 20 #define VAR_DNS 21 #define VAR_NBNS 22 /* ``accept|deny|disable|enable'' masks */ #define NEG_HISMASK (1) #define NEG_MYMASK (2) /* ``accept|deny|disable|enable'' values */ #define NEG_ACFCOMP 40 #define NEG_CHAP 41 #define NEG_DEFLATE 42 #define NEG_LQR 43 #define NEG_PAP 44 #define NEG_PPPDDEFLATE 45 #define NEG_PRED1 46 #define NEG_PROTOCOMP 47 #define NEG_SHORTSEQ 48 #define NEG_VJCOMP 49 #define NEG_DNS 50 const char Version[] = "2.0-beta"; -const char VersionDate[] = "$Date: 1998/04/27 01:40:38 $"; +const char VersionDate[] = "$Date: 1998/04/30 23:53:29 $"; static int ShowCommand(struct cmdargs const *); static int TerminalCommand(struct cmdargs const *); static int QuitCommand(struct cmdargs const *); static int OpenCommand(struct cmdargs const *); static int CloseCommand(struct cmdargs const *); static int DownCommand(struct cmdargs const *); static int AllowCommand(struct cmdargs const *); static int SetCommand(struct cmdargs const *); static int LinkCommand(struct cmdargs const *); static int AddCommand(struct cmdargs const *); static int DeleteCommand(struct cmdargs const *); static int NegotiateCommand(struct cmdargs const *); #ifndef NOALIAS static int AliasCommand(struct cmdargs const *); static int AliasEnable(struct cmdargs const *); static int AliasOption(struct cmdargs const *); #endif static int HelpCommand(struct cmdargs const *arg) { struct cmdtab const *cmd; int n, cmax, dmax, cols; if (!arg->prompt) { LogPrintf(LogWARN, "help: Cannot help without a prompt\n"); return 0; } if (arg->argc > arg->argn) { for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++) if ((cmd->lauth & arg->prompt->auth) && ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) || (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) { prompt_Printf(arg->prompt, "%s\n", cmd->syntax); return 0; } return -1; } cmax = dmax = 0; for (cmd = arg->cmdtab; cmd->func; cmd++) if (cmd->name && (cmd->lauth & arg->prompt->auth)) { if ((n = strlen(cmd->name)) > cmax) cmax = n; if ((n = strlen(cmd->helpmes)) > dmax) dmax = n; } cols = 80 / (dmax + cmax + 3); n = 0; for (cmd = arg->cmdtab; cmd->func; cmd++) if (cmd->name && (cmd->lauth & arg->prompt->auth)) { prompt_Printf(arg->prompt, " %-*.*s: %-*.*s", cmax, cmax, cmd->name, dmax, dmax, cmd->helpmes); if (++n % cols == 0) prompt_Printf(arg->prompt, "\n"); } if (n % cols != 0) prompt_Printf(arg->prompt, "\n"); return 0; } static int CloneCommand(struct cmdargs const *arg) { int f; if (arg->argc == arg->argn) return -1; if (!arg->bundle->ncp.mp.cfg.mrru) { LogPrintf(LogWARN, "clone: Only available in multilink mode\n"); return 1; } for (f = arg->argn; f < arg->argc; f++) bundle_DatalinkClone(arg->bundle, arg->cx, arg->argv[f]); return 0; } static int RemoveCommand(struct cmdargs const *arg) { if (arg->argc != arg->argn) return -1; if (arg->cx->state != DATALINK_CLOSED) { LogPrintf(LogWARN, "remove: Cannot delete links that aren't closed\n"); return 2; } bundle_DatalinkRemove(arg->bundle, arg->cx); return 0; } int LoadCommand(struct cmdargs const *arg) { const char *name; if (arg->argc > arg->argn) name = arg->argv[arg->argn]; else name = "default"; if (!ValidSystem(name, arg->prompt, arg->bundle->phys_type)) { LogPrintf(LogERROR, "%s: Label not allowed\n", name); return 1; } else { /* * Set the label before & after so that `set enddisc' works and * we handle nested `load' commands. */ bundle_SetLabel(arg->bundle, arg->argc > arg->argn ? name : NULL); if (SelectSystem(arg->bundle, name, CONFFILE, arg->prompt) < 0) { bundle_SetLabel(arg->bundle, NULL); LogPrintf(LogWARN, "%s: label not found.\n", name); return -1; } bundle_SetLabel(arg->bundle, arg->argc > arg->argn ? name : NULL); } return 0; } int SaveCommand(struct cmdargs const *arg) { LogPrintf(LogWARN, "save command is not implemented (yet).\n"); return 1; } static int DialCommand(struct cmdargs const *arg) { int res; if ((arg->cx && !(arg->cx->physical->type & (PHYS_MANUAL|PHYS_DEMAND))) || (!arg->cx && (arg->bundle->phys_type & ~(PHYS_MANUAL|PHYS_DEMAND)))) { LogPrintf(LogWARN, "Manual dial is only available for auto and" " interactive links\n"); return 1; } if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0) return res; bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL); return 0; } static int ShellCommand(struct cmdargs const *arg, int bg) { const char *shell; pid_t shpid; int argc; char *argv[MAXARGS]; #ifdef SHELL_ONLY_INTERACTIVELY /* we're only allowed to shell when we run ppp interactively */ if (arg->prompt && arg->prompt->owner) { LogPrintf(LogWARN, "Can't start a shell from a network connection\n"); return 1; } #endif if (arg->argc == arg->argn) { if (!arg->prompt) { LogPrintf(LogWARN, "Can't start an interactive shell from" " a config file\n"); return 1; } else if (arg->prompt->owner) { LogPrintf(LogWARN, "Can't start an interactive shell from" " a socket connection\n"); return 1; } else if (bg) { LogPrintf(LogWARN, "Can only start an interactive shell in" " the foreground mode\n"); return 1; } } if ((shpid = fork()) == 0) { int i, fd; if ((shell = getenv("SHELL")) == 0) shell = _PATH_BSHELL; TermTimerService(); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGALRM, SIG_DFL); if (arg->prompt) fd = arg->prompt->fd_out; - else if ((fd = open("/dev/null", O_RDWR)) == -1) { - LogPrintf(LogALERT, "Failed to open /dev/null: %s\n", strerror(errno)); + else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { + LogPrintf(LogALERT, "Failed to open %s: %s\n", + _PATH_DEVNULL, strerror(errno)); exit(1); } for (i = 0; i < 3; i++) dup2(fd, i); setuid(geteuid()); if (arg->argc > arg->argn) { /* substitute pseudo args */ argv[0] = strdup(arg->argv[arg->argn]); for (argc = 1; argc < arg->argc - arg->argn; argc++) { if (strcasecmp(arg->argv[argc + arg->argn], "HISADDR") == 0) argv[argc] = strdup(inet_ntoa(arg->bundle->ncp.ipcp.peer_ip)); else if (strcasecmp(arg->argv[argc + arg->argn], "INTERFACE") == 0) argv[argc] = strdup(arg->bundle->ifname); else if (strcasecmp(arg->argv[argc + arg->argn], "MYADDR") == 0) argv[argc] = strdup(inet_ntoa(arg->bundle->ncp.ipcp.my_ip)); else argv[argc] = strdup(arg->argv[argc + arg->argn]); } argv[argc] = NULL; if (bg) { pid_t p; p = getpid(); if (daemon(1, 1) == -1) { LogPrintf(LogERROR, "%d: daemon: %s\n", p, strerror(errno)); exit(1); } } else if (arg->prompt) printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]); execvp(argv[0], argv); } else { if (arg->prompt) printf("ppp: Pausing until %s finishes\n", shell); prompt_TtyOldMode(arg->prompt); execl(shell, shell, NULL); } LogPrintf(LogWARN, "exec() of %s failed\n", arg->argc > arg->argn ? arg->argv[arg->argn] : shell); exit(255); } if (shpid == (pid_t) - 1) LogPrintf(LogERROR, "Fork failed: %s\n", strerror(errno)); else { int status; waitpid(shpid, &status, 0); } if (arg->prompt && !arg->prompt->owner) prompt_TtyCommandMode(arg->prompt); return 0; } static int BgShellCommand(struct cmdargs const *arg) { if (arg->argc == arg->argn) return -1; return ShellCommand(arg, 1); } static int FgShellCommand(struct cmdargs const *arg) { return ShellCommand(arg, 0); } static struct cmdtab const Commands[] = { {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, "accept option request", "accept option .."}, {"add", NULL, AddCommand, LOCAL_AUTH, "add route", "add dest mask gateway", NULL}, {NULL, "add!", AddCommand, LOCAL_AUTH, "add or change route", "add! dest mask gateway", (void *)1}, #ifndef NOALIAS {"alias", NULL, AliasCommand, LOCAL_AUTH, "alias control", "alias option [yes|no]"}, #endif {"allow", "auth", AllowCommand, LOCAL_AUTH, "Allow ppp access", "allow users|modes ...."}, {"bg", "!bg", BgShellCommand, LOCAL_AUTH, "Run a background command", "[!]bg command"}, {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX, "Clone a link", "clone newname..."}, {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Close an FSM", "close [lcp|ccp]"}, {"delete", NULL, DeleteCommand, LOCAL_AUTH, "delete route", "delete dest", NULL}, {NULL, "delete!", DeleteCommand, LOCAL_AUTH, "delete a route if it exists", "delete! dest", (void *)1}, {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Deny option request", "deny option .."}, {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Dial and login", "dial|call [remote]"}, {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Disable option", "disable option .."}, {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX, "Generate a down event", "down"}, {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Enable option", "enable option .."}, {"link", "datalink", LinkCommand, LOCAL_AUTH, "Link specific commands", "link name command ..."}, {"load", NULL, LoadCommand, LOCAL_AUTH, "Load settings", "load [remote]"}, {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Open an FSM", "open [lcp|ccp]"}, {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH, "Password for manipulation", "passwd LocalPassword"}, {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH, "Quit PPP program", "quit|bye [all]"}, {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX, "Remove a link", "remove"}, {"save", NULL, SaveCommand, LOCAL_AUTH, "Save settings", "save"}, {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Set parameters", "set[up] var value"}, {"shell", "!", FgShellCommand, LOCAL_AUTH, "Run a subshell", "shell|! [sh command]"}, {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT, "Show status and stats", "show var"}, {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX, "Enter terminal mode", "term"}, {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, "Display this message", "help|? [command]", Commands}, {NULL, NULL, NULL}, }; static int ShowEscape(struct cmdargs const *arg) { if (arg->cx->physical->async.cfg.EscMap[32]) { int code, bit; const char *sep = ""; for (code = 0; code < 32; code++) if (arg->cx->physical->async.cfg.EscMap[code]) for (bit = 0; bit < 8; bit++) if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) { prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit); sep = ", "; } prompt_Printf(arg->prompt, "\n"); } return 0; } static int ShowTimerList(struct cmdargs const *arg) { ShowTimers(0, arg->prompt); return 0; } static int ShowStopped(struct cmdargs const *arg) { prompt_Printf(arg->prompt, " Stopped Timer: LCP: "); if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load) prompt_Printf(arg->prompt, "Disabled"); else prompt_Printf(arg->prompt, "%ld secs", arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS); prompt_Printf(arg->prompt, ", CCP: "); if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load) prompt_Printf(arg->prompt, "Disabled"); else prompt_Printf(arg->prompt, "%ld secs", arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS); prompt_Printf(arg->prompt, "\n"); return 0; } static int ShowVersion(struct cmdargs const *arg) { prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, VersionDate); return 0; } static int ShowProtocolStats(struct cmdargs const *arg) { struct link *l = ChooseLink(arg); prompt_Printf(arg->prompt, "%s:\n", l->name); link_ReportProtocolStatus(l, arg->prompt); return 0; } static struct cmdtab const ShowCommands[] = { {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH, "Show bundle details", "show bundle"}, {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT, "Show CCP status", "show cpp"}, {"compress", NULL, ReportCompress, LOCAL_AUTH, "Show compression stats", "show compress"}, {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX, "Show escape characters", "show escape"}, {"filter", NULL, ShowFilter, LOCAL_AUTH, "Show packet filters", "show filter [in|out|dial|alive]"}, {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX, "Show HDLC errors", "show hdlc"}, {"ipcp", NULL, ReportIpcpStatus, LOCAL_AUTH, "Show IPCP status", "show ipcp"}, {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX, "Show LCP status", "show lcp"}, {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX, "Show (high-level) link info", "show link"}, {"links", NULL, bundle_ShowLinks, LOCAL_AUTH, "Show available link names", "show links"}, {"log", NULL, log_ShowLevel, LOCAL_AUTH, "Show log levels", "show log"}, {"mem", NULL, ShowMemMap, LOCAL_AUTH, "Show memory map", "show mem"}, {"modem", NULL, modem_ShowStatus, LOCAL_AUTH | LOCAL_CX, "Show (low-level) link info", "show modem"}, {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH, "Show multilink setup", "show mp"}, {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT, "Show protocol summary", "show proto"}, {"route", NULL, ShowRoute, LOCAL_AUTH, "Show routing table", "show route"}, {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX, "Show STOPPED timeout", "show stopped"}, {"timers", NULL, ShowTimerList, LOCAL_AUTH, "Show alarm timers", "show timers"}, {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH, "Show version string", "show version"}, {"who", NULL, log_ShowWho, LOCAL_AUTH, "Show client list", "show who"}, {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH, "Display this message", "show help|? [command]", ShowCommands}, {NULL, NULL, NULL}, }; static struct cmdtab const * FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch) { int nmatch; int len; struct cmdtab const *found; found = NULL; len = strlen(str); nmatch = 0; while (cmds->func) { if (cmds->name && strncasecmp(str, cmds->name, len) == 0) { if (cmds->name[len] == '\0') { *pmatch = 1; return cmds; } nmatch++; found = cmds; } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) { if (cmds->alias[len] == '\0') { *pmatch = 1; return cmds; } nmatch++; found = cmds; } cmds++; } *pmatch = nmatch; return found; } static const char * mkPrefix(int argc, char const *const *argv, char *tgt, int sz) { int f, tlen, len; tlen = 0; for (f = 0; f < argc && tlen < sz - 2; f++) { if (f) tgt[tlen++] = ' '; len = strlen(argv[f]); if (len > sz - tlen - 1) len = sz - tlen - 1; strncpy(tgt+tlen, argv[f], len); tlen += len; } tgt[tlen] = '\0'; return tgt; } static int FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn, char const *const *argv, struct prompt *prompt, struct datalink *cx) { struct cmdtab const *cmd; int val = 1; int nmatch; struct cmdargs arg; char prefix[100]; cmd = FindCommand(cmds, argv[argn], &nmatch); if (nmatch > 1) LogPrintf(LogWARN, "%s: Ambiguous command\n", mkPrefix(argn+1, argv, prefix, sizeof prefix)); else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) { if ((cmd->lauth & LOCAL_CX) && !cx) /* We've got no context, but we require it */ cx = bundle2datalink(bundle, NULL); if ((cmd->lauth & LOCAL_CX) && !cx) LogPrintf(LogWARN, "%s: No context (use the `link' command)\n", mkPrefix(argn+1, argv, prefix, sizeof prefix)); else { if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) { LogPrintf(LogWARN, "%s: Redundant context (%s) ignored\n", mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name); cx = NULL; } arg.cmdtab = cmds; arg.cmd = cmd; arg.argc = argc; arg.argn = argn+1; arg.argv = argv; arg.bundle = bundle; arg.cx = cx; arg.prompt = prompt; val = (*cmd->func) (&arg); } } else LogPrintf(LogWARN, "%s: Invalid command\n", mkPrefix(argn+1, argv, prefix, sizeof prefix)); if (val == -1) LogPrintf(LogWARN, "Usage: %s\n", cmd->syntax); else if (val) LogPrintf(LogWARN, "%s: Failed %d\n", mkPrefix(argn+1, argv, prefix, sizeof prefix), val); return val; } void InterpretCommand(char *buff, int nb, int *argc, char ***argv) { static char *vector[MAXARGS]; char *cp; if (nb > 0) { cp = buff + strcspn(buff, "\r\n"); if (cp) *cp = '\0'; *argc = MakeArgs(buff, vector, VECSIZE(vector)); *argv = vector; } else *argc = 0; } static int arghidden(int argc, char const *const *argv, int n) { /* Is arg n of the given command to be hidden from the log ? */ /* set authkey xxxxx */ /* set key xxxxx */ if (n == 2 && !strncasecmp(argv[0], "se", 2) && (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2))) return 1; /* passwd xxxxx */ if (n == 1 && !strncasecmp(argv[0], "p", 1)) return 1; return 0; } void RunCommand(struct bundle *bundle, int argc, char const *const *argv, struct prompt *prompt, const char *label) { if (argc > 0) { if (LogIsKept(LogCOMMAND)) { static char buf[LINE_LEN]; int f, n; *buf = '\0'; if (label) { strncpy(buf, label, sizeof buf - 3); buf[sizeof buf - 3] = '\0'; strcat(buf, ": "); } n = strlen(buf); for (f = 0; f < argc; f++) { if (n < sizeof buf - 1 && f) buf[n++] = ' '; if (arghidden(argc, argv, f)) strncpy(buf+n, "********", sizeof buf - n - 1); else strncpy(buf+n, argv[f], sizeof buf - n - 1); n += strlen(buf+n); } LogPrintf(LogCOMMAND, "%s\n", buf); } FindExec(bundle, Commands, argc, 0, argv, prompt, NULL); } } void DecodeCommand(struct bundle *bundle, char *buff, int nb, struct prompt *prompt, const char *label) { int argc; char **argv; InterpretCommand(buff, nb, &argc, &argv); RunCommand(bundle, argc, (char const *const *)argv, prompt, label); } static int ShowCommand(struct cmdargs const *arg) { if (!arg->prompt) LogPrintf(LogWARN, "show: Cannot show without a prompt\n"); else if (arg->argc > arg->argn) FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv, arg->prompt, arg->cx); else prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n"); return 0; } static int TerminalCommand(struct cmdargs const *arg) { if (!arg->prompt) { LogPrintf(LogWARN, "term: Need a prompt\n"); return 1; } if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) { prompt_Printf(arg->prompt, "LCP state is [%s]\n", State2Nam(arg->cx->physical->link.lcp.fsm.state)); return 1; } datalink_Up(arg->cx, 0, 0); prompt_TtyTermMode(arg->prompt, arg->cx); return 0; } static int QuitCommand(struct cmdargs const *arg) { if (!arg->prompt || prompt_IsController(arg->prompt) || (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") && (arg->prompt->auth & LOCAL_AUTH))) Cleanup(EX_NORMAL); if (arg->prompt) prompt_Destroy(arg->prompt, 1); return 0; } static int OpenCommand(struct cmdargs const *arg) { if (arg->argc == arg->argn || (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "lcp"))) bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL); else if (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "ccp")) { struct fsm *fp = &ChooseLink(arg)->ccp.fsm; if (fp->link->lcp.fsm.state != ST_OPENED) LogPrintf(LogWARN, "open: LCP must be open before opening CCP\n"); else if (fp->state != ST_OPENED) { FsmUp(fp); FsmOpen(fp); } } else return -1; return 0; } static int CloseCommand(struct cmdargs const *arg) { if (arg->argc == arg->argn || (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "lcp"))) bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, 1); else if (arg->argc == arg->argn+1 && !strcasecmp(arg->argv[arg->argn], "ccp")) { struct fsm *fp = &ChooseLink(arg)->ccp.fsm; if (fp->state == ST_OPENED) FsmClose(fp); } else return -1; return 0; } static int DownCommand(struct cmdargs const *arg) { datalink_Down(arg->cx, 1); return 0; } static int SetModemSpeed(struct cmdargs const *arg) { long speed; char *end; if (arg->argc > arg->argn && *arg->argv[arg->argn]) { if (arg->argc > arg->argn+1) { LogPrintf(LogWARN, "SetModemSpeed: Too many arguments"); return -1; } if (strcasecmp(arg->argv[arg->argn], "sync") == 0) { Physical_SetSync(arg->cx->physical); return 0; } end = NULL; speed = strtol(arg->argv[arg->argn], &end, 10); if (*end) { LogPrintf(LogWARN, "SetModemSpeed: Bad argument \"%s\"", arg->argv[arg->argn]); return -1; } if (Physical_SetSpeed(arg->cx->physical, speed)) return 0; LogPrintf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]); } else LogPrintf(LogWARN, "SetModemSpeed: No speed specified\n"); return -1; } static int SetStoppedTimeout(struct cmdargs const *arg) { struct link *l = &arg->cx->physical->link; l->lcp.fsm.StoppedTimer.load = 0; l->ccp.fsm.StoppedTimer.load = 0; if (arg->argc <= arg->argn+2) { if (arg->argc > arg->argn) { l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS; if (arg->argc > arg->argn+1) l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS; } return 0; } return -1; } #define ismask(x) \ (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3) static int SetServer(struct cmdargs const *arg) { int res = -1; if (arg->argc > arg->argn && arg->argc < arg->argn+4) { const char *port, *passwd, *mask; /* What's what ? */ port = arg->argv[arg->argn]; if (arg->argc == arg->argn + 2) { passwd = arg->argv[arg->argn+1]; mask = NULL; } else if (arg->argc == arg->argn + 3) { passwd = arg->argv[arg->argn+1]; mask = arg->argv[arg->argn+2]; if (!ismask(mask)) return -1; } else if (strcasecmp(port, "none") == 0) { if (ServerClose(arg->bundle)) LogPrintf(LogPHASE, "Disabled server port.\n"); return 0; } else return -1; strncpy(server.passwd, passwd, sizeof server.passwd - 1); server.passwd[sizeof server.passwd - 1] = '\0'; if (*port == '/') { mode_t imask; if (mask != NULL) { unsigned m; if (sscanf(mask, "%o", &m) == 1) imask = m; else return -1; } else imask = (mode_t)-1; res = ServerLocalOpen(arg->bundle, port, imask); } else { int iport; if (mask != NULL) return -1; if (strspn(port, "0123456789") != strlen(port)) { struct servent *s; if ((s = getservbyname(port, "tcp")) == NULL) { iport = 0; LogPrintf(LogWARN, "%s: Invalid port or service\n", port); } else iport = ntohs(s->s_port); } else iport = atoi(port); res = iport ? ServerTcpOpen(arg->bundle, iport) : -1; } } return res; } static int SetModemParity(struct cmdargs const *arg) { return arg->argc > arg->argn ? modem_SetParity(arg->cx->physical, arg->argv[arg->argn]) : -1; } static int SetEscape(struct cmdargs const *arg) { int code; int argc = arg->argc - arg->argn; char const *const *argv = arg->argv + arg->argn; for (code = 0; code < 33; code++) arg->cx->physical->async.cfg.EscMap[code] = 0; while (argc-- > 0) { sscanf(*argv++, "%x", &code); code &= 0xff; arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7)); arg->cx->physical->async.cfg.EscMap[32] = 1; } return 0; } static struct in_addr GetIpAddr(const char *cp) { struct hostent *hp; struct in_addr ipaddr; if (inet_aton(cp, &ipaddr) == 0) { hp = gethostbyname(cp); if (hp && hp->h_addrtype == AF_INET) memcpy(&ipaddr, hp->h_addr, hp->h_length); else ipaddr.s_addr = 0; } return (ipaddr); } static int SetInterfaceAddr(struct cmdargs const *arg) { struct ipcp *ipcp = &arg->bundle->ncp.ipcp; const char *hisaddr; hisaddr = NULL; ipcp->cfg.my_range.ipaddr.s_addr = INADDR_ANY; ipcp->cfg.peer_range.ipaddr.s_addr = INADDR_ANY; if (arg->argc > arg->argn + 4) return -1; ipcp->cfg.HaveTriggerAddress = 0; ipcp->cfg.netmask.s_addr = INADDR_ANY; iplist_reset(&ipcp->cfg.peer_list); if (arg->argc > arg->argn) { if (!ParseAddr(ipcp, arg->argc - arg->argn, arg->argv + arg->argn, &ipcp->cfg.my_range.ipaddr, &ipcp->cfg.my_range.mask, &ipcp->cfg.my_range.width)) return 1; if (arg->argc > arg->argn+1) { hisaddr = arg->argv[arg->argn+1]; if (arg->argc > arg->argn+2) { ipcp->cfg.netmask = GetIpAddr(arg->argv[arg->argn+2]); if (arg->argc > arg->argn+3) { ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]); ipcp->cfg.HaveTriggerAddress = 1; } } } } /* * For backwards compatibility, 0.0.0.0 means any address. */ if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) { ipcp->cfg.my_range.mask.s_addr = INADDR_ANY; ipcp->cfg.my_range.width = 0; } ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr; if (ipcp->cfg.peer_range.ipaddr.s_addr == INADDR_ANY) { ipcp->cfg.peer_range.mask.s_addr = INADDR_ANY; ipcp->cfg.peer_range.width = 0; } if (hisaddr && !UseHisaddr(arg->bundle, hisaddr, arg->bundle->phys_type & PHYS_DEMAND)) return 4; return 0; } static int SetVariable(struct cmdargs const *arg) { u_long ulong_val; const char *argp; int param = (int)arg->cmd->args; struct datalink *cx = arg->cx; /* AUTH_CX uses this */ const char *err = NULL; struct link *l = ChooseLink(arg); /* AUTH_CX_OPT uses this */ int dummyint; struct in_addr dummyaddr, *addr; if (arg->argc > arg->argn) argp = arg->argv[arg->argn]; else argp = ""; if ((arg->cmd->lauth & LOCAL_CX) && !cx) { LogPrintf(LogWARN, "set %s: No context (use the `link' command)\n", arg->cmd->name); return 1; } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) { LogPrintf(LogWARN, "set %s: Redundant context (%s) ignored\n", arg->cmd->name, cx->name); cx = NULL; } switch (param) { case VAR_AUTHKEY: if (bundle_Phase(arg->bundle) == PHASE_DEAD) { strncpy(arg->bundle->cfg.auth.key, argp, sizeof arg->bundle->cfg.auth.key - 1); arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0'; } else { err = "set authkey: Only available at phase DEAD\n"; LogPrintf(LogWARN, err); } break; case VAR_AUTHNAME: if (bundle_Phase(arg->bundle) == PHASE_DEAD) { strncpy(arg->bundle->cfg.auth.name, argp, sizeof arg->bundle->cfg.auth.name - 1); arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name - 1] = '\0'; } else { err = "set authname: Only available at phase DEAD\n"; LogPrintf(LogWARN, err); } break; case VAR_DIAL: strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1); cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0'; break; case VAR_LOGIN: strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1); cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0'; break; case VAR_WINSIZE: if (arg->argc > arg->argn) { l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]); if (l->ccp.cfg.deflate.out.winsize < 8 || l->ccp.cfg.deflate.out.winsize > 15) { LogPrintf(LogWARN, "%d: Invalid outgoing window size\n", l->ccp.cfg.deflate.out.winsize); l->ccp.cfg.deflate.out.winsize = 15; } if (arg->argc > arg->argn+1) { l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]); if (l->ccp.cfg.deflate.in.winsize < 8 || l->ccp.cfg.deflate.in.winsize > 15) { LogPrintf(LogWARN, "%d: Invalid incoming window size\n", l->ccp.cfg.deflate.in.winsize); l->ccp.cfg.deflate.in.winsize = 15; } } else l->ccp.cfg.deflate.in.winsize = 0; } else { err = "No window size specified\n"; LogPrintf(LogWARN, err); } break; case VAR_DEVICE: Physical_SetDeviceList(cx->physical, arg->argc - arg->argn, arg->argv + arg->argn); break; case VAR_ACCMAP: if (arg->argc > arg->argn) { sscanf(argp, "%lx", &ulong_val); cx->physical->link.lcp.cfg.accmap = ulong_val; } else { err = "No accmap specified\n"; LogPrintf(LogWARN, err); } break; case VAR_MRRU: if (bundle_Phase(arg->bundle) != PHASE_DEAD) LogPrintf(LogWARN, "mrru: Only changable at phase DEAD\n"); else { ulong_val = atol(argp); if (ulong_val < MIN_MRU) err = "Given MRRU value (%lu) is too small.\n"; else if (ulong_val > MAX_MRU) err = "Given MRRU value (%lu) is too big.\n"; else arg->bundle->ncp.mp.cfg.mrru = ulong_val; if (err) LogPrintf(LogWARN, err, ulong_val); } break; case VAR_MRU: ulong_val = atol(argp); if (ulong_val < MIN_MRU) err = "Given MRU value (%lu) is too small.\n"; else if (ulong_val > MAX_MRU) err = "Given MRU value (%lu) is too big.\n"; else l->lcp.cfg.mru = ulong_val; if (err) LogPrintf(LogWARN, err, ulong_val); break; case VAR_MTU: ulong_val = atol(argp); if (ulong_val == 0) arg->bundle->cfg.mtu = 0; else if (ulong_val < MIN_MTU) err = "Given MTU value (%lu) is too small.\n"; else if (ulong_val > MAX_MTU) err = "Given MTU value (%lu) is too big.\n"; else arg->bundle->cfg.mtu = ulong_val; if (err) LogPrintf(LogWARN, err, ulong_val); break; case VAR_OPENMODE: if (strcasecmp(argp, "active") == 0) cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ? atoi(arg->argv[arg->argn+1]) : 1; else if (strcasecmp(argp, "passive") == 0) cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE; else { err = "%s: Invalid openmode\n"; LogPrintf(LogWARN, err, argp); } break; case VAR_PHONE: strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1); cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0'; break; case VAR_HANGUP: strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1); cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0'; break; case VAR_IDLETIMEOUT: if (arg->argc > arg->argn+1) err = "Too many idle timeout values\n"; else if (arg->argc == arg->argn+1) bundle_SetIdleTimer(arg->bundle, atoi(argp)); if (err) LogPrintf(LogWARN, err); break; case VAR_LQRPERIOD: ulong_val = atol(argp); if (ulong_val <= 0) { err = "%s: Invalid lqr period\n"; LogPrintf(LogWARN, err, argp); } else l->lcp.cfg.lqrperiod = ulong_val; break; case VAR_LCPRETRY: ulong_val = atol(argp); if (ulong_val <= 0) { err = "%s: Invalid LCP FSM retry period\n"; LogPrintf(LogWARN, err, argp); } else cx->physical->link.lcp.cfg.fsmretry = ulong_val; break; case VAR_CHAPRETRY: ulong_val = atol(argp); if (ulong_val <= 0) { err = "%s: Invalid CHAP retry period\n"; LogPrintf(LogWARN, err, argp); } else cx->chap.auth.cfg.fsmretry = ulong_val; break; case VAR_PAPRETRY: ulong_val = atol(argp); if (ulong_val <= 0) { err = "%s: Invalid PAP retry period\n"; LogPrintf(LogWARN, err, argp); } else cx->pap.cfg.fsmretry = ulong_val; break; case VAR_CCPRETRY: ulong_val = atol(argp); if (ulong_val <= 0) { err = "%s: Invalid CCP FSM retry period\n"; LogPrintf(LogWARN, err, argp); } else l->ccp.cfg.fsmretry = ulong_val; break; case VAR_IPCPRETRY: ulong_val = atol(argp); if (ulong_val <= 0) { err = "%s: Invalid IPCP FSM retry period\n"; LogPrintf(LogWARN, err, argp); } else arg->bundle->ncp.ipcp.cfg.fsmretry = ulong_val; break; case VAR_NBNS: case VAR_DNS: if (param == VAR_DNS) addr = arg->bundle->ncp.ipcp.cfg.ns.dns; else addr = arg->bundle->ncp.ipcp.cfg.ns.nbns; addr[0].s_addr = addr[1].s_addr = INADDR_ANY; if (arg->argc > arg->argn) { ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn, addr, &dummyaddr, &dummyint); if (arg->argc > arg->argn+1) ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn + 1, addr + 1, &dummyaddr, &dummyint); if (addr[1].s_addr == INADDR_ANY) addr[1].s_addr = addr[0].s_addr; if (addr[0].s_addr == INADDR_ANY) addr[0].s_addr = addr[1].s_addr; } break; } return err ? 1 : 0; } static int SetCtsRts(struct cmdargs const *arg) { if (arg->argc == arg->argn+1) { if (strcmp(arg->argv[arg->argn], "on") == 0) Physical_SetRtsCts(arg->cx->physical, 1); else if (strcmp(arg->argv[arg->argn], "off") == 0) Physical_SetRtsCts(arg->cx->physical, 0); else return -1; return 0; } return -1; } static struct cmdtab const SetCommands[] = { {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP}, {"authkey", "key", SetVariable, LOCAL_AUTH, "Set authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY}, {"authname", NULL, SetVariable, LOCAL_AUTH, "Set authentication name", "set authname name", (const void *)VAR_AUTHNAME}, {"ccpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, "Set FSM retry period", "set ccpretry value", (const void *)VAR_CCPRETRY}, {"chapretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set CHAP retry period", "set chapretry value", (const void *)VAR_CHAPRETRY}, {"ctsrts", "crtscts", SetCtsRts, LOCAL_AUTH | LOCAL_CX, "Use hardware flow control", "set ctsrts [on|off]"}, {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, "Set deflate window sizes", "set deflate out-winsize in-winsize", (const void *) VAR_WINSIZE}, {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX, "Set modem device name", "set device|line device-name[,device-name]", (const void *) VAR_DEVICE}, {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set dialing script", "set dial chat-script", (const void *) VAR_DIAL}, {"dns", NULL, SetVariable, LOCAL_AUTH, "Set Domain Name Server", "set dns pri-addr [sec-addr]", (const void *)VAR_DNS}, {"encrypt", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Select CHAP encryption type", "set encrypt MSChap|MD5", (const void *)VAR_ENC}, {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH, "Set Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"}, {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX, "Set escape characters", "set escape hex-digit ..."}, {"filter", NULL, SetFilter, LOCAL_AUTH, "Set packet filters", "set filter alive|dial|in|out rule-no permit|deny " "[src_addr[/width]] [dst_addr[/width]] [tcp|udp|icmp [src [lt|eq|gt port]] " "[dst [lt|eq|gt port]] [estab] [syn] [finrst]]"}, {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set hangup script", "set hangup chat-script", (const void *) VAR_HANGUP}, {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "Set destination address", "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"}, {"ipcpretry", NULL, SetVariable, LOCAL_AUTH, "Set FSM retry period", "set ipcpretry value", (const void *)VAR_IPCPRETRY}, {"lcpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set FSM retry period", "set lcpretry value", (const void *)VAR_LCPRETRY}, {"log", NULL, log_SetLevel, LOCAL_AUTH, "Set log level", "set log [local] [+|-]value..."}, {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set login script", "set login chat-script", (const void *) VAR_LOGIN}, {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, "Set LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD}, {"mrru", NULL, SetVariable, LOCAL_AUTH, "Set MRRU value", "set mrru value", (const void *)VAR_MRRU}, {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, "Set MRU value", "set mru value", (const void *)VAR_MRU}, {"mtu", NULL, SetVariable, LOCAL_AUTH, "Set interface MTU value", "set mtu value", (const void *)VAR_MTU}, {"nbns", NULL, SetVariable, LOCAL_AUTH, "Set NetBIOS Name Server", "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS}, {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set open mode", "set openmode active|passive [secs]", (const void *)VAR_OPENMODE}, {"papretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set PAP retry period", "set papretry value", (const void *)VAR_PAPRETRY}, {"parity", NULL, SetModemParity, LOCAL_AUTH | LOCAL_CX, "Set modem parity", "set parity [odd|even|none]"}, {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Set telephone number(s)", "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE}, {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX, "Set Reconnect timeout", "set reconnect value ntries"}, {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX, "Set Redial timeout", "set redial value|random[.value|random] [attempts]"}, {"server", "socket", SetServer, LOCAL_AUTH, "Set server port", "set server|socket TcpPort|LocalName|none [mask]"}, {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX, "Set modem speed", "set speed value"}, {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX, "Set STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"}, {"timeout", NULL, SetVariable, LOCAL_AUTH, "Set Idle timeout", "set timeout idletime", (const void *)VAR_IDLETIMEOUT}, {"vj", NULL, SetInitVJ, LOCAL_AUTH, "Set vj values", "set vj slots|slotcomp [value]"}, {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX, "Set datalink weighting", "set weight n"}, {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, "Display this message", "set help|? [command]", SetCommands}, {NULL, NULL, NULL}, }; static int SetCommand(struct cmdargs const *arg) { if (arg->argc > arg->argn) FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv, arg->prompt, arg->cx); else if (arg->prompt) prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? ' for" " syntax help.\n"); else LogPrintf(LogWARN, "set command must have arguments\n"); return 0; } static int AddCommand(struct cmdargs const *arg) { struct in_addr dest, gateway, netmask; int gw; if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2) return -1; if (arg->argc == arg->argn+2) { if (strcasecmp(arg->argv[arg->argn], "default")) return -1; else { dest.s_addr = netmask.s_addr = INADDR_ANY; gw = 1; } } else { if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) dest = arg->bundle->ncp.ipcp.my_ip; else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) dest = arg->bundle->ncp.ipcp.peer_ip; else dest = GetIpAddr(arg->argv[arg->argn]); netmask = GetIpAddr(arg->argv[arg->argn+1]); gw = 2; } if (strcasecmp(arg->argv[arg->argn+gw], "HISADDR") == 0) gateway = arg->bundle->ncp.ipcp.peer_ip; else if (strcasecmp(arg->argv[arg->argn+gw], "INTERFACE") == 0) gateway.s_addr = INADDR_ANY; else gateway = GetIpAddr(arg->argv[arg->argn+gw]); bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask, arg->cmd->args ? 1 : 0); return 0; } static int DeleteCommand(struct cmdargs const *arg) { struct in_addr dest, none; if (arg->argc == arg->argn+1) { if(strcasecmp(arg->argv[arg->argn], "all") == 0) DeleteIfRoutes(arg->bundle, 0); else { if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) dest = arg->bundle->ncp.ipcp.my_ip; else if (strcasecmp(arg->argv[arg->argn], "default") == 0) dest.s_addr = INADDR_ANY; else dest = GetIpAddr(arg->argv[arg->argn]); none.s_addr = INADDR_ANY; bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none, arg->cmd->args ? 1 : 0); } } else return -1; return 0; } #ifndef NOALIAS static struct cmdtab const AliasCommands[] = { {"addr", NULL, AliasRedirectAddr, LOCAL_AUTH, "static address translation", "alias addr [addr_local addr_alias]"}, {"deny_incoming", NULL, AliasOption, LOCAL_AUTH, "stop incoming connections", "alias deny_incoming [yes|no]", (const void *) PKT_ALIAS_DENY_INCOMING}, {"enable", NULL, AliasEnable, LOCAL_AUTH, "enable IP aliasing", "alias enable [yes|no]"}, {"log", NULL, AliasOption, LOCAL_AUTH, "log aliasing link creation", "alias log [yes|no]", (const void *) PKT_ALIAS_LOG}, {"port", NULL, AliasRedirectPort, LOCAL_AUTH, "port redirection", "alias port [proto addr_local:port_local port_alias]"}, {"same_ports", NULL, AliasOption, LOCAL_AUTH, "try to leave port numbers unchanged", "alias same_ports [yes|no]", (const void *) PKT_ALIAS_SAME_PORTS}, {"unregistered_only", NULL, AliasOption, LOCAL_AUTH, "alias unregistered (private) IP address space only", "alias unregistered_only [yes|no]", (const void *) PKT_ALIAS_UNREGISTERED_ONLY}, {"use_sockets", NULL, AliasOption, LOCAL_AUTH, "allocate host sockets", "alias use_sockets [yes|no]", (const void *) PKT_ALIAS_USE_SOCKETS}, {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, "Display this message", "alias help|? [command]", AliasCommands}, {NULL, NULL, NULL}, }; static int AliasCommand(struct cmdargs const *arg) { if (arg->argc > arg->argn) FindExec(arg->bundle, AliasCommands, arg->argc, arg->argn, arg->argv, arg->prompt, arg->cx); else if (arg->prompt) prompt_Printf(arg->prompt, "Use `alias help' to get a list or `alias help" "