diff --git a/usr.sbin/ppp/bundle.c b/usr.sbin/ppp/bundle.c index d6beda3a1867..e57e29f8f11d 100644 --- a/usr.sbin/ppp/bundle.c +++ b/usr.sbin/ppp/bundle.c @@ -1,1354 +1,1377 @@ /*- * 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.81 1998/05/10 22:20:06 brian Exp $ + * $Id: bundle.c,v 1.1.2.82 1998/05/11 23:39:27 brian Exp $ */ #include #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 "loadalias.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" #define SCATTER_SEGMENTS 4 /* version, datalink, name, physical */ #define SOCKET_OVERHEAD 100 /* additional buffer space for large */ /* {recv,send}msg() calls */ 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) log_Printf(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); fsm_Up(&bundle->ncp.ipcp.fsm); fsm_Open(&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) { log_Printf(LogERROR, "bundle_CleanInterface: socket(): %s\n", strerror(errno)); return (-1); } strncpy(ifrq.ifr_name, bundle->ifp.Name, 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->ifp.Name, 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) log_Printf(LogERROR, "bundle_CleanInterface: Can't get dst for %s on %s !\n", inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr), bundle->ifp.Name); return 0; } ifra.ifra_broadaddr = ifrq.ifr_dstaddr; if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) { if (ifra.ifra_addr.sa_family == AF_INET) log_Printf(LogERROR, "bundle_CleanInterface: Can't delete %s address on %s !\n", inet_ntoa(((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr), bundle->ifp.Name); 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) log_Printf(LogPHASE, "Parent notified of success.\n"); else log_Printf(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) { struct datalink *dl; bundle->ifp.Speed = 0; for (dl = bundle->links; dl; dl = dl->next) if (dl->state == DATALINK_OPEN) bundle->ifp.Speed += modem_Speed(dl->physical); tun_configure(bundle, bundle->ncp.mp.peer_mrru); } else { bundle->ifp.Speed = modem_Speed(link2physical(fp->link)); tun_configure(bundle, fsm2lcp(fp)->his_mru); } } 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 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 && bundle->ncp.mp.active) { struct datalink *dl; bundle->ifp.Speed = 0; for (dl = bundle->links; dl; dl = dl->next) if (fp != &dl->physical->link.lcp.fsm && dl->state == DATALINK_OPEN) bundle->ifp.Speed += modem_Speed(dl->physical); if (bundle->ifp.Speed) /* Don't configure down to a speed of 0 */ tun_configure(bundle, bundle->ncp.mp.link.lcp.his_mru); } } 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, fsm_Down all NCPs * If it's the last NCP, fsm_Close 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); fsm_Down(fp); fsm_Close(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) { fsm_Down(&bundle->ncp.ipcp.fsm); fsm_Close(&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, fsm_Close 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) { log_Printf(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) fsm_Close(&bundle->ncp.ipcp.fsm); else { if (bundle->ncp.ipcp.fsm.state > ST_INITIAL) { fsm_Close(&bundle->ncp.ipcp.fsm); fsm_Down(&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); } void bundle_Down(struct bundle *bundle) { struct datalink *dl; for (dl = bundle->links; dl; dl = dl->next) datalink_Down(dl, 1); } 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); /* If there are aren't many packets queued, look for some more. */ if (r && bundle->links && bundle_FillQueues(bundle) < 20) { if (*n < bundle->dev.fd + 1) *n = bundle->dev.fd + 1; FD_SET(bundle->dev.fd, r); log_Printf(LogTIMER, "tun: fdset(r) %d\n", bundle->dev.fd); result++; } + /* + * This *MUST* be called after the datalink UpdateSet()s as it + * might be ``holding'' one of the datalinks and wants to be + * able to de-select() from the descriptor set + */ + descriptor_UpdateSet(&bundle->ncp.mp.server.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 FD_ISSET(bundle->dev.fd, fdset); } static void bundle_DescriptorRead(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct datalink *dl; struct descriptor *desc; + if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset)) + descriptor_Read(&bundle->ncp.mp.server.desc, bundle, fdset); + 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); if (FD_ISSET(bundle->dev.fd, fdset)) { struct tun_data tun; int n, pri; /* something to read from tun */ n = read(bundle->dev.fd, &tun, sizeof tun); if (n < 0) { log_Printf(LogERROR, "read from tun: %s\n", strerror(errno)); return; } n -= sizeof tun - sizeof tun.data; if (n <= 0) { log_Printf(LogERROR, "read from tun: Only %d bytes read\n", n); return; } if (!tun_check_header(tun, AF_INET)) return; if (((struct ip *)tun.data)->ip_dst.s_addr == bundle->ncp.ipcp.my_ip.s_addr) { /* we've been asked to send something addressed *to* us :( */ if (Enabled(bundle, OPT_LOOPBACK)) { pri = PacketCheck(bundle, tun.data, n, &bundle->filter.in); if (pri >= 0) { struct mbuf *bp; #ifndef NOALIAS if (alias_IsEnabled()) { (*PacketAlias.In)(tun.data, sizeof tun.data); n = ntohs(((struct ip *)tun.data)->ip_len); } #endif bp = mbuf_Alloc(n, MB_IPIN); memcpy(MBUF_CTOP(bp), tun.data, n); ip_Input(bundle, bp); log_Printf(LogDEBUG, "Looped back packet addressed to myself\n"); } return; } else log_Printf(LogDEBUG, "Oops - forwarding packet addressed to myself\n"); } /* * Process on-demand dialup. Output packets are queued within tunnel * device until IPCP is opened. */ if (bundle_Phase(bundle) == PHASE_DEAD) { /* * Note, we must be in AUTO mode :-/ otherwise our interface should * *not* be UP and we can't receive data */ if ((pri = PacketCheck(bundle, tun.data, n, &bundle->filter.dial)) >= 0) bundle_Open(bundle, NULL, PHYS_DEMAND); else /* * Drop the packet. If we were to queue it, we'd just end up with * a pile of timed-out data in our output queue by the time we get * around to actually dialing. We'd also prematurely reach the * threshold at which we stop select()ing to read() the tun * device - breaking auto-dial. */ return; } pri = PacketCheck(bundle, tun.data, n, &bundle->filter.out); if (pri >= 0) { #ifndef NOALIAS if (alias_IsEnabled()) { (*PacketAlias.Out)(tun.data, sizeof tun.data); n = ntohs(((struct ip *)tun.data)->ip_len); } #endif ip_Enqueue(pri, tun.data, n); } } } static void bundle_DescriptorWrite(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct datalink *dl; struct descriptor *desc; + /* This is not actually necessary as struct mpserver doesn't Write() */ + if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset)) + descriptor_Write(&bundle->ncp.mp.server.desc, bundle, fdset); + 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.ifp.Name != NULL) { /* Already allocated ! */ log_Printf(LogERROR, "bundle_Create: There's only one BUNDLE !\n"); return NULL; } err = ENOENT; enoentcount = 0; for (bundle.unit = 0; ; bundle.unit++) { snprintf(bundle.dev.Name, sizeof bundle.dev.Name, "%s%d", prefix, bundle.unit); bundle.dev.fd = ID0open(bundle.dev.Name, O_RDWR); if (bundle.dev.fd >= 0) break; else if (errno == ENXIO) { err = errno; break; } else if (errno == ENOENT) { if (++enoentcount > 2) break; } else err = errno; } if (bundle.dev.fd < 0) { log_Printf(LogWARN, "No available tunnel devices found (%s).\n", strerror(err)); return NULL; } log_SetTun(bundle.unit); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { log_Printf(LogERROR, "bundle_Create: socket(): %s\n", strerror(errno)); close(bundle.dev.fd); return NULL; } bundle.ifp.Name = strrchr(bundle.dev.Name, '/'); if (bundle.ifp.Name == NULL) bundle.ifp.Name = bundle.dev.Name; else bundle.ifp.Name++; /* * Now, bring up the interface. */ memset(&ifrq, '\0', sizeof ifrq); strncpy(ifrq.ifr_name, bundle.ifp.Name, sizeof ifrq.ifr_name - 1); ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0'; if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { log_Printf(LogERROR, "OpenTunnel: ioctl(SIOCGIFFLAGS): %s\n", strerror(errno)); close(s); close(bundle.dev.fd); bundle.ifp.Name = NULL; return NULL; } ifrq.ifr_flags |= IFF_UP; if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) { log_Printf(LogERROR, "OpenTunnel: ioctl(SIOCSIFFLAGS): %s\n", strerror(errno)); close(s); close(bundle.dev.fd); bundle.ifp.Name = NULL; return NULL; } close(s); if ((bundle.ifp.Index = GetIfIndex(bundle.ifp.Name)) < 0) { log_Printf(LogERROR, "OpenTunnel: Can't find interface index.\n"); close(bundle.dev.fd); bundle.ifp.Name = NULL; return NULL; } prompt_Printf(prompt, "Using interface: %s\n", bundle.ifp.Name); log_Printf(LogPHASE, "Using interface: %s\n", bundle.ifp.Name); bundle.ifp.Speed = 0; 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_SROUTES | 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) { log_Printf(LogERROR, "Cannot create data link: %s\n", strerror(errno)); close(bundle.dev.fd); bundle.ifp.Name = 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; route_IfDelete(bundle, 1); s = ID0socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { log_Printf(LogERROR, "bundle_DownInterface: socket: %s\n", strerror(errno)); return; } memset(&ifrq, '\0', sizeof ifrq); strncpy(ifrq.ifr_name, bundle->ifp.Name, sizeof ifrq.ifr_name - 1); ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0'; if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) { log_Printf(LogERROR, "bundle_DownInterface: ioctl(SIOCGIFFLAGS): %s\n", strerror(errno)); close(s); return; } ifrq.ifr_flags &= ~IFF_UP; if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) { log_Printf(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; - /* In case we're dropping out with an exception :-O */ + /* + * Clean up the interface. We don't need to mp_Down(), + * ipcp_CleanInterface() and bundle_DownInterface() unless we're getting + * out under exceptional conditions such as a descriptor exception. + */ mp_Down(&bundle->ncp.mp); - - if (bundle->phys_type & PHYS_DEMAND) { - ipcp_CleanInterface(&bundle->ncp.ipcp); - bundle_DownInterface(bundle); - } + ipcp_CleanInterface(&bundle->ncp.ipcp); + bundle_DownInterface(bundle); + /* Again, these are all DATALINK_CLOSED unless we're abending */ dl = bundle->links; while (dl) dl = datalink_Destroy(dl); + /* In case we never made PHASE_NETWORK */ bundle_Notify(bundle, EX_ERRDEAD); + /* Finally, destroy our prompts */ desc = bundle->desc.next; while (desc) { ndesc = desc->next; if (desc->type == PROMPT_DESCRIPTOR) prompt_Destroy((struct prompt *)desc, 1); else log_Printf(LogERROR, "bundle_Destroy: Don't know how to delete descriptor" " type %d\n", desc->type); desc = ndesc; } bundle->desc.next = NULL; bundle->ifp.Name = NULL; } struct rtmsg { struct rt_msghdr m_rtm; char m_space[64]; }; int 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; int result = 1; 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) { log_Printf(LogERROR, "bundle_SetRoute: socket(): %s\n", strerror(errno)); return result; } 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->ifp.Index); ilen = strlen(iname); dl.sdl_len = sizeof dl - sizeof dl.sdl_data + ilen; dl.sdl_family = AF_LINK; dl.sdl_index = bundle->ifp.Index; 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) { log_Printf(LogTCPIP, "bundle_SetRoute failure:\n"); log_Printf(LogTCPIP, "bundle_SetRoute: Cmd = %s\n", cmdstr); log_Printf(LogTCPIP, "bundle_SetRoute: Dst = %s\n", inet_ntoa(dst)); log_Printf(LogTCPIP, "bundle_SetRoute: Gateway = %s\n", inet_ntoa(gateway)); log_Printf(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) { log_Printf(LogWARN, "Add route failed: %s already exists\n", inet_ntoa(dst)); result = 0; /* Don't add to our dynamic list */ } 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) log_Printf(LogWARN, "Del route failed: %s: Non-existent\n", inet_ntoa(dst)); } else if (rtmes.m_rtm.rtm_errno == 0) log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr, inet_ntoa(dst), strerror(errno)); else log_Printf(LogWARN, "%s route failed: %s: %s\n", cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno)); } log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n", wb, cmdstr, (unsigned)dst.s_addr, (unsigned)gateway.s_addr); close(s); return result; } void bundle_LinkClosed(struct bundle *bundle, struct datalink *dl) { /* * Our datalink has closed. - * UpdateSet() will remove 1OFF and STDIN links. + * CleanDatalinks() (called from DoLoop()) will remove closed + * 1OFF and DIRECT links. * If it's the last data link, enter phase DEAD. + * + * NOTE: dl may not be in our list (bundle_SendDatalink()) ! */ 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) { fsm_Down(&bundle->ncp.ipcp.fsm); fsm_Close(&bundle->ncp.ipcp.fsm); /* ST_INITIAL please */ } + bundle_DownInterface(bundle); 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 { struct datalink *dl; int add; for (total = 0, dl = bundle->links; dl; dl = dl->next) if (dl->state == DATALINK_OPEN) { add = link_QueueLen(&dl->physical->link); if (add == 0 && dl->physical->out == NULL) add = ip_FlushPacket(&dl->physical->link, bundle); total += add; } } 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]", dl->name, datalink_State(dl)); if (dl->physical->link.throughput.rolling && dl->state == DATALINK_OPEN) prompt_Printf(arg->prompt, " (weight %d, %d bytes/sec)", dl->mp.weight, dl->physical->link.throughput.OctetsPerSecond); prompt_Printf(arg->prompt, "\n"); } 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, " Device: %s\n", arg->bundle->dev.Name); prompt_Printf(arg->prompt, " Interface: %s @ %lubps\n", arg->bundle->ifp.Name, arg->bundle->ifp.Speed); 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, " Sticky Routes: %s\n", optval(arg->bundle, OPT_SROUTES)); 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 Logging: %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; log_Printf(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))) { timer_Stop(&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; timer_Start(&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) { timer_Stop(&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; } int bundle_DatalinkClone(struct bundle *bundle, struct datalink *dl, const char *name) { struct datalink *ndl; ndl = bundle2datalink(bundle, name); if (!ndl) { ndl = datalink_Clone(dl, name); ndl->next = dl->next; dl->next = ndl; bundle_GenPhysType(bundle); return 1; } log_Printf(LogWARN, "Clone: %s: name already exists\n", ndl->name); return 0; } 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 s, struct sockaddr_un *sun) { char cmsgbuf[sizeof(struct cmsghdr) + sizeof(int)]; struct cmsghdr *cmsg = (struct cmsghdr *)cmsgbuf; struct msghdr msg; struct iovec iov[SCATTER_SEGMENTS]; struct datalink *dl; int niov, link_fd, expect, f; log_Printf(LogPHASE, "Receiving datalink\n"); /* Create our scatter/gather array */ niov = 1; iov[0].iov_len = strlen(Version) + 1; iov[0].iov_base = (char *)malloc(iov[0].iov_len); if (datalink2iov(NULL, iov, &niov, sizeof iov / sizeof *iov) == -1) return; for (f = expect = 0; f < niov; f++) expect += iov[f].iov_len; /* Set up our message */ cmsg->cmsg_len = sizeof cmsgbuf; cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memset(&msg, '\0', sizeof msg); msg.msg_name = (caddr_t)sun; msg.msg_namelen = sizeof *sun; msg.msg_iov = iov; msg.msg_iovlen = niov; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof cmsgbuf; log_Printf(LogDEBUG, "Expecting %d scatter/gather bytes\n", expect); f = expect + 100; setsockopt(s, SOL_SOCKET, SO_RCVBUF, &f, sizeof f); if ((f = recvmsg(s, &msg, MSG_WAITALL)) != expect) { if (f == -1) log_Printf(LogERROR, "Failed recvmsg: %s\n", strerror(errno)); else log_Printf(LogERROR, "Failed recvmsg: Got %d, not %d\n", f, expect); while (niov--) free(iov[niov].iov_base); return; } /* We've successfully received an open file descriptor through our socket */ link_fd = *(int *)CMSG_DATA(cmsg); write(s, "!",1 ); /* ACK */ if (strncmp(Version, iov[0].iov_base, iov[0].iov_len)) { log_Printf(LogWARN, "Cannot receive datalink, incorrect version" " (\"%.*s\", not \"%s\")\n", (int)iov[0].iov_len, iov[0].iov_base, Version); close(link_fd); while (niov--) free(iov[niov].iov_base); return; } niov = 1; dl = iov2datalink(bundle, iov, &niov, sizeof iov / sizeof *iov, link_fd); if (dl) { dl->next = bundle->links; bundle->links = dl; bundle_GenPhysType(bundle); datalink_AuthOk(dl); } else close(link_fd); free(iov[0].iov_base); } void bundle_SendDatalink(struct datalink *dl, int s, struct sockaddr_un *sun) { char cmsgbuf[sizeof(struct cmsghdr) + sizeof(int)], ack; struct cmsghdr *cmsg = (struct cmsghdr *)cmsgbuf; struct msghdr msg; struct iovec iov[SCATTER_SEGMENTS]; int niov, link_fd, f, expect; struct datalink **pdl; struct bundle *bundle = dl->bundle; log_Printf(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; } + bundle_GenPhysType(bundle); + bundle_LinkClosed(bundle, dl); + /* Build our scatter/gather array */ iov[0].iov_len = strlen(Version) + 1; iov[0].iov_base = strdup(Version); niov = 1; link_fd = datalink2iov(dl, iov, &niov, sizeof iov / sizeof *iov); if (link_fd != -1) { cmsg->cmsg_len = sizeof cmsgbuf; cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = link_fd; memset(&msg, '\0', sizeof msg); msg.msg_name = (caddr_t)sun; msg.msg_namelen = sizeof *sun; msg.msg_iov = iov; msg.msg_iovlen = niov; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof cmsgbuf; for (f = expect = 0; f < niov; f++) expect += iov[f].iov_len; log_Printf(LogDEBUG, "Sending %d bytes in scatter/gather array\n", expect); f = expect + SOCKET_OVERHEAD; setsockopt(s, SOL_SOCKET, SO_SNDBUF, &f, sizeof f); if (sendmsg(s, &msg, 0) == -1) log_Printf(LogERROR, "Failed sendmsg: %s\n", strerror(errno)); /* We must get the ACK before closing the descriptor ! */ read(s, &ack, 1); close(link_fd); } while (niov--) free(iov[niov].iov_base); } diff --git a/usr.sbin/ppp/datalink.c b/usr.sbin/ppp/datalink.c index 286aaec45f50..e946465c18f3 100644 --- a/usr.sbin/ppp/datalink.c +++ b/usr.sbin/ppp/datalink.c @@ -1,1072 +1,1075 @@ /*- * 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: datalink.c,v 1.1.2.59 1998/05/11 23:39:29 brian Exp $ + * $Id: datalink.c,v 1.1.2.60 1998/05/15 18:21:02 brian Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mbuf.h" #include "log.h" #include "defs.h" #include "timer.h" #include "fsm.h" #include "lcp.h" #include "descriptor.h" #include "lqr.h" #include "hdlc.h" #include "async.h" #include "throughput.h" #include "ccp.h" #include "link.h" #include "physical.h" #include "iplist.h" #include "slcompress.h" #include "ipcp.h" #include "filter.h" #include "mp.h" #include "bundle.h" #include "chat.h" #include "auth.h" #include "modem.h" #include "prompt.h" #include "lcpproto.h" #include "pap.h" #include "chap.h" #include "command.h" #include "datalink.h" static void datalink_LoginDone(struct datalink *); static void datalink_NewState(struct datalink *, int); static void datalink_OpenTimeout(void *v) { struct datalink *dl = (struct datalink *)v; timer_Stop(&dl->dial_timer); if (dl->state == DATALINK_OPENING) log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name); } static void datalink_StartDialTimer(struct datalink *dl, int Timeout) { timer_Stop(&dl->dial_timer); if (Timeout) { if (Timeout > 0) dl->dial_timer.load = Timeout * SECTICKS; else dl->dial_timer.load = (random() % DIAL_TIMEOUT) * SECTICKS; dl->dial_timer.func = datalink_OpenTimeout; dl->dial_timer.name = "dial"; dl->dial_timer.arg = dl; timer_Start(&dl->dial_timer); if (dl->state == DATALINK_OPENING) log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n", dl->name, Timeout); } } static void datalink_HangupDone(struct datalink *dl) { if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp && physical_GetFD(dl->physical) != -1) { /* Don't close our modem if the link is dedicated */ datalink_LoginDone(dl); return; } modem_Close(dl->physical); dl->phone.chosen = "N/A"; if (dl->bundle->CleaningUp || (dl->physical->type == PHYS_DIRECT) || ((!dl->dial_tries || (dl->dial_tries < 0 && !dl->reconnect_tries)) && !(dl->physical->type & (PHYS_PERM|PHYS_DEDICATED)))) { datalink_NewState(dl, DATALINK_CLOSED); dl->dial_tries = -1; dl->reconnect_tries = 0; bundle_LinkClosed(dl->bundle, dl); if (!dl->bundle->CleaningUp) datalink_StartDialTimer(dl, dl->cfg.dial.timeout); } else { datalink_NewState(dl, DATALINK_OPENING); if (dl->dial_tries < 0) { datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout); dl->dial_tries = dl->cfg.dial.max; dl->reconnect_tries--; } else { if (dl->phone.next == NULL) datalink_StartDialTimer(dl, dl->cfg.dial.timeout); else datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout); } } } static const char * datalink_ChoosePhoneNumber(struct datalink *dl) { char *phone; if (dl->phone.alt == NULL) { if (dl->phone.next == NULL) { strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1); dl->phone.list[sizeof dl->phone.list - 1] = '\0'; dl->phone.next = dl->phone.list; } dl->phone.alt = strsep(&dl->phone.next, ":"); } phone = strsep(&dl->phone.alt, "|"); dl->phone.chosen = *phone ? phone : "[NONE]"; if (*phone) log_Printf(LogPHASE, "Phone: %s\n", phone); return phone; } static void datalink_LoginDone(struct datalink *dl) { if (!dl->script.packetmode) { dl->dial_tries = -1; datalink_NewState(dl, DATALINK_READY); } else if (modem_Raw(dl->physical, dl->bundle) < 0) { dl->dial_tries = 0; log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n"); if (dl->script.run) { datalink_NewState(dl, DATALINK_HANGUP); modem_Offline(dl->physical); chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL); } else { if (dl->physical->type == PHYS_DEDICATED) /* force a redial timeout */ modem_Close(dl->physical); datalink_HangupDone(dl); } } else { dl->dial_tries = -1; hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp); async_Init(&dl->physical->async); lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ? 0 : dl->physical->link.lcp.cfg.openmode); ccp_Setup(&dl->physical->link.ccp); datalink_NewState(dl, DATALINK_LCP); fsm_Up(&dl->physical->link.lcp.fsm); fsm_Open(&dl->physical->link.lcp.fsm); } } static int datalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) { struct datalink *dl = descriptor2datalink(d); int result; result = 0; switch (dl->state) { case DATALINK_CLOSED: if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_1OFF)) && !bundle_IsDead(dl->bundle)) /* * Our first time in - DEDICATED never comes down, and STDIN & 1OFF * get deleted when they enter DATALINK_CLOSED. Go to * DATALINK_OPENING via datalink_Up() and fall through. */ datalink_Up(dl, 1, 1); else break; /* fall through */ case DATALINK_OPENING: if (dl->dial_timer.state != TIMER_RUNNING) { if (--dl->dial_tries < 0) dl->dial_tries = 0; if (modem_Open(dl->physical, dl->bundle) >= 0) { if (dl->script.run) { datalink_NewState(dl, DATALINK_DIAL); chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1, datalink_ChoosePhoneNumber(dl)); if (!(dl->physical->type & (PHYS_PERM|PHYS_DEDICATED)) && dl->cfg.dial.max) log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n", dl->name, dl->cfg.dial.max - dl->dial_tries, dl->cfg.dial.max); return datalink_UpdateSet(d, r, w, e, n); } else datalink_LoginDone(dl); } else { if (!(dl->physical->type & (PHYS_PERM|PHYS_DEDICATED)) && dl->cfg.dial.max) log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n", dl->cfg.dial.max - dl->dial_tries, dl->cfg.dial.max); else log_Printf(LogCHAT, "Failed to open modem\n"); if (dl->bundle->CleaningUp || (!(dl->physical->type & (PHYS_PERM|PHYS_DEDICATED)) && dl->cfg.dial.max && dl->dial_tries == 0)) { datalink_NewState(dl, DATALINK_CLOSED); dl->reconnect_tries = 0; dl->dial_tries = -1; bundle_LinkClosed(dl->bundle, dl); } if (!dl->bundle->CleaningUp) datalink_StartDialTimer(dl, dl->cfg.dial.timeout); } } break; case DATALINK_HANGUP: case DATALINK_DIAL: case DATALINK_LOGIN: result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n); switch (dl->chat.state) { case CHAT_DONE: /* script succeeded */ chat_Destroy(&dl->chat); switch(dl->state) { case DATALINK_HANGUP: datalink_HangupDone(dl); break; case DATALINK_DIAL: datalink_NewState(dl, DATALINK_LOGIN); chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL); return datalink_UpdateSet(d, r, w, e, n); case DATALINK_LOGIN: datalink_LoginDone(dl); break; } break; case CHAT_FAILED: /* Going down - script failed */ log_Printf(LogWARN, "Chat script failed\n"); chat_Destroy(&dl->chat); switch(dl->state) { case DATALINK_HANGUP: datalink_HangupDone(dl); break; case DATALINK_DIAL: case DATALINK_LOGIN: datalink_NewState(dl, DATALINK_HANGUP); modem_Offline(dl->physical); chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL); return datalink_UpdateSet(d, r, w, e, n); } break; } break; case DATALINK_READY: case DATALINK_LCP: case DATALINK_AUTH: case DATALINK_OPEN: - if (dl == dl->bundle->ncp.mp.server.send.dl) - /* Never read our descriptor if we're scheduled for transfer */ - r = NULL; result = descriptor_UpdateSet(&dl->physical->desc, r, w, e, n); break; } return result; } +int +datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e) +{ + return physical_RemoveFromSet(dl->physical, r, w, e); +} + static int datalink_IsSet(struct descriptor *d, const fd_set *fdset) { struct datalink *dl = descriptor2datalink(d); switch (dl->state) { case DATALINK_CLOSED: case DATALINK_OPENING: break; case DATALINK_HANGUP: case DATALINK_DIAL: case DATALINK_LOGIN: return descriptor_IsSet(&dl->chat.desc, fdset); case DATALINK_READY: case DATALINK_LCP: case DATALINK_AUTH: case DATALINK_OPEN: return descriptor_IsSet(&dl->physical->desc, fdset); } return 0; } static void datalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct datalink *dl = descriptor2datalink(d); switch (dl->state) { case DATALINK_CLOSED: case DATALINK_OPENING: break; case DATALINK_HANGUP: case DATALINK_DIAL: case DATALINK_LOGIN: descriptor_Read(&dl->chat.desc, bundle, fdset); break; case DATALINK_READY: case DATALINK_LCP: case DATALINK_AUTH: case DATALINK_OPEN: descriptor_Read(&dl->physical->desc, bundle, fdset); break; } } static void datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct datalink *dl = descriptor2datalink(d); switch (dl->state) { case DATALINK_CLOSED: case DATALINK_OPENING: break; case DATALINK_HANGUP: case DATALINK_DIAL: case DATALINK_LOGIN: descriptor_Write(&dl->chat.desc, bundle, fdset); break; case DATALINK_READY: case DATALINK_LCP: case DATALINK_AUTH: case DATALINK_OPEN: descriptor_Write(&dl->physical->desc, bundle, fdset); break; } } static void datalink_ComeDown(struct datalink *dl, int stay) { if (stay) { dl->dial_tries = -1; dl->reconnect_tries = 0; } if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) { modem_Offline(dl->physical); if (dl->script.run && dl->state != DATALINK_OPENING) { datalink_NewState(dl, DATALINK_HANGUP); chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL); } else datalink_HangupDone(dl); } } static void datalink_LayerStart(void *v, struct fsm *fp) { /* The given FSM is about to start up ! */ struct datalink *dl = (struct datalink *)v; if (fp->proto == PROTO_LCP) (*dl->parent->LayerStart)(dl->parent->object, fp); } static void datalink_LayerUp(void *v, struct fsm *fp) { /* The given fsm is now up */ struct datalink *dl = (struct datalink *)v; if (fp->proto == PROTO_LCP) { datalink_GotAuthname(dl, "", 0); dl->physical->link.lcp.auth_ineed = dl->physical->link.lcp.want_auth; dl->physical->link.lcp.auth_iwait = dl->physical->link.lcp.his_auth; if (dl->physical->link.lcp.his_auth || dl->physical->link.lcp.want_auth) { if (bundle_Phase(dl->bundle) == PHASE_ESTABLISH) bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE); log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name, Auth2Nam(dl->physical->link.lcp.his_auth), Auth2Nam(dl->physical->link.lcp.want_auth)); if (dl->physical->link.lcp.his_auth == PROTO_PAP) auth_StartChallenge(&dl->pap, dl->physical, pap_SendChallenge); if (dl->physical->link.lcp.want_auth == PROTO_CHAP) auth_StartChallenge(&dl->chap.auth, dl->physical, chap_SendChallenge); } else datalink_AuthOk(dl); } } void datalink_GotAuthname(struct datalink *dl, const char *name, int len) { if (len >= sizeof dl->peer.authname) len = sizeof dl->peer.authname - 1; strncpy(dl->peer.authname, name, len); dl->peer.authname[len] = '\0'; } void datalink_AuthOk(struct datalink *dl) { if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) { /* we've authenticated in multilink mode ! */ switch (mp_Up(&dl->bundle->ncp.mp, dl)) { case MP_LINKSENT: - /* We've handed the link off to another ppp ! */ + /* We've handed the link off to another ppp (well, we will soon) ! */ return; case MP_UP: /* First link in the bundle */ auth_Select(dl->bundle, dl->peer.authname, dl->physical); /* fall through */ case MP_ADDED: /* We're in multilink mode ! */ dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE; break; case MP_FAILED: datalink_AuthNotOk(dl); return; } } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) { log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name); datalink_AuthNotOk(dl); return; } else { dl->bundle->ncp.mp.peer = dl->peer; ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link); auth_Select(dl->bundle, dl->peer.authname, dl->physical); } fsm_Up(&dl->physical->link.ccp.fsm); fsm_Open(&dl->physical->link.ccp.fsm); datalink_NewState(dl, DATALINK_OPEN); bundle_NewPhase(dl->bundle, PHASE_NETWORK); (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm); } void datalink_AuthNotOk(struct datalink *dl) { datalink_NewState(dl, DATALINK_LCP); fsm_Close(&dl->physical->link.lcp.fsm); } static void datalink_LayerDown(void *v, struct fsm *fp) { /* The given FSM has been told to come down */ struct datalink *dl = (struct datalink *)v; if (fp->proto == PROTO_LCP) { switch (dl->state) { case DATALINK_OPEN: peerid_Init(&dl->peer); fsm_Down(&dl->physical->link.ccp.fsm); fsm_Close(&dl->physical->link.ccp.fsm); (*dl->parent->LayerDown)(dl->parent->object, fp); /* fall through */ case DATALINK_AUTH: timer_Stop(&dl->pap.authtimer); timer_Stop(&dl->chap.auth.authtimer); } datalink_NewState(dl, DATALINK_LCP); } } static void datalink_LayerFinish(void *v, struct fsm *fp) { /* The given fsm is now down */ struct datalink *dl = (struct datalink *)v; if (fp->proto == PROTO_LCP) { fsm_Down(fp); /* Bring us to INITIAL or STARTING */ (*dl->parent->LayerFinish)(dl->parent->object, fp); datalink_ComeDown(dl, 0); } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE) fsm_Open(fp); /* CCP goes to ST_STOPPED */ } struct datalink * datalink_Create(const char *name, struct bundle *bundle, int type) { struct datalink *dl; dl = (struct datalink *)malloc(sizeof(struct datalink)); if (dl == NULL) return dl; dl->desc.type = DATALINK_DESCRIPTOR; dl->desc.next = NULL; dl->desc.UpdateSet = datalink_UpdateSet; dl->desc.IsSet = datalink_IsSet; dl->desc.Read = datalink_Read; dl->desc.Write = datalink_Write; dl->state = DATALINK_CLOSED; *dl->cfg.script.dial = '\0'; *dl->cfg.script.login = '\0'; *dl->cfg.script.hangup = '\0'; *dl->cfg.phone.list = '\0'; *dl->phone.list = '\0'; dl->phone.next = NULL; dl->phone.alt = NULL; dl->phone.chosen = "N/A"; dl->script.run = 1; dl->script.packetmode = 1; mp_linkInit(&dl->mp); dl->bundle = bundle; dl->next = NULL; memset(&dl->dial_timer, '\0', sizeof dl->dial_timer); dl->dial_tries = 0; dl->cfg.dial.max = 1; dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT; dl->cfg.dial.timeout = DIAL_TIMEOUT; dl->reconnect_tries = 0; dl->cfg.reconnect.max = 0; dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT; dl->name = strdup(name); peerid_Init(&dl->peer); dl->parent = &bundle->fsm; dl->fsmp.LayerStart = datalink_LayerStart; dl->fsmp.LayerUp = datalink_LayerUp; dl->fsmp.LayerDown = datalink_LayerDown; dl->fsmp.LayerFinish = datalink_LayerFinish; dl->fsmp.object = dl; auth_Init(&dl->pap); auth_Init(&dl->chap.auth); if ((dl->physical = modem_Create(dl, type)) == NULL) { free(dl->name); free(dl); return NULL; } chat_Init(&dl->chat, dl->physical, NULL, 1, NULL); log_Printf(LogPHASE, "%s: Created in %s state\n", dl->name, datalink_State(dl)); return dl; } struct datalink * datalink_Clone(struct datalink *odl, const char *name) { struct datalink *dl; dl = (struct datalink *)malloc(sizeof(struct datalink)); if (dl == NULL) return dl; dl->desc.type = DATALINK_DESCRIPTOR; dl->desc.next = NULL; dl->desc.UpdateSet = datalink_UpdateSet; dl->desc.IsSet = datalink_IsSet; dl->desc.Read = datalink_Read; dl->desc.Write = datalink_Write; dl->state = DATALINK_CLOSED; memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg); mp_linkInit(&dl->mp); *dl->phone.list = '\0'; dl->phone.next = NULL; dl->phone.alt = NULL; dl->phone.chosen = "N/A"; dl->bundle = odl->bundle; dl->next = NULL; memset(&dl->dial_timer, '\0', sizeof dl->dial_timer); dl->dial_tries = 0; dl->reconnect_tries = 0; dl->name = strdup(name); peerid_Init(&dl->peer); dl->parent = odl->parent; memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp); dl->fsmp.object = dl; auth_Init(&dl->pap); dl->pap.cfg.fsmretry = odl->pap.cfg.fsmretry; auth_Init(&dl->chap.auth); dl->chap.auth.cfg.fsmretry = odl->chap.auth.cfg.fsmretry; if ((dl->physical = modem_Create(dl, PHYS_MANUAL)) == NULL) { free(dl->name); free(dl); return NULL; } memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg); memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg, sizeof dl->physical->link.lcp.cfg); memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg, sizeof dl->physical->link.ccp.cfg); memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg, sizeof dl->physical->async.cfg); chat_Init(&dl->chat, dl->physical, NULL, 1, NULL); log_Printf(LogPHASE, "%s: Cloned in %s state\n", dl->name, datalink_State(dl)); return dl; } struct datalink * datalink_Destroy(struct datalink *dl) { struct datalink *result; if (dl->state != DATALINK_CLOSED) { log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n", datalink_State(dl)); switch (dl->state) { case DATALINK_HANGUP: case DATALINK_DIAL: case DATALINK_LOGIN: chat_Destroy(&dl->chat); /* Gotta blat the timers ! */ break; } } result = dl->next; modem_Destroy(dl->physical); free(dl->name); free(dl); return result; } void datalink_Up(struct datalink *dl, int runscripts, int packetmode) { if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED)) /* Ignore scripts */ runscripts = 0; switch (dl->state) { case DATALINK_CLOSED: if (bundle_Phase(dl->bundle) == PHASE_DEAD || bundle_Phase(dl->bundle) == PHASE_TERMINATE) bundle_NewPhase(dl->bundle, PHASE_ESTABLISH); datalink_NewState(dl, DATALINK_OPENING); dl->reconnect_tries = dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max; dl->dial_tries = dl->cfg.dial.max; dl->script.run = runscripts; dl->script.packetmode = packetmode; break; case DATALINK_OPENING: if (!dl->script.run && runscripts) dl->script.run = 1; /* fall through */ case DATALINK_DIAL: case DATALINK_LOGIN: case DATALINK_READY: if (!dl->script.packetmode && packetmode) { dl->script.packetmode = 1; if (dl->state == DATALINK_READY) datalink_LoginDone(dl); } break; } } void datalink_Close(struct datalink *dl, int stay) { /* Please close */ switch (dl->state) { case DATALINK_OPEN: peerid_Init(&dl->peer); fsm_Down(&dl->physical->link.ccp.fsm); fsm_Close(&dl->physical->link.ccp.fsm); /* fall through */ case DATALINK_AUTH: case DATALINK_LCP: fsm_Close(&dl->physical->link.lcp.fsm); if (stay) { dl->dial_tries = -1; dl->reconnect_tries = 0; } break; default: datalink_ComeDown(dl, stay); } } void datalink_Down(struct datalink *dl, int stay) { /* Carrier is lost */ switch (dl->state) { case DATALINK_OPEN: peerid_Init(&dl->peer); fsm_Down(&dl->physical->link.ccp.fsm); fsm_Close(&dl->physical->link.ccp.fsm); /* fall through */ case DATALINK_AUTH: case DATALINK_LCP: fsm_Down(&dl->physical->link.lcp.fsm); if (stay) fsm_Close(&dl->physical->link.lcp.fsm); else fsm_Open(&dl->physical->link.ccp.fsm); /* fall through */ default: datalink_ComeDown(dl, stay); } } void datalink_StayDown(struct datalink *dl) { dl->reconnect_tries = 0; } int datalink_Show(struct cmdargs const *arg) { prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name); prompt_Printf(arg->prompt, " State: %s\n", datalink_State(arg->cx)); prompt_Printf(arg->prompt, " CHAP Encryption: %s\n", arg->cx->chap.using_MSChap ? "MSChap" : "MD5" ); prompt_Printf(arg->prompt, " Peer name: "); if (*arg->cx->peer.authname) prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname); else if (arg->cx->state == DATALINK_OPEN) prompt_Printf(arg->prompt, "None requested\n"); else prompt_Printf(arg->prompt, "N/A\n"); prompt_Printf(arg->prompt, " Discriminator: %s\n", mp_Enddisc(arg->cx->peer.enddisc.class, arg->cx->peer.enddisc.address, arg->cx->peer.enddisc.len)); prompt_Printf(arg->prompt, "\nDefaults:\n"); prompt_Printf(arg->prompt, " Phone List: %s\n", arg->cx->cfg.phone.list); if (arg->cx->cfg.dial.max) prompt_Printf(arg->prompt, " Dial tries: %d, delay ", arg->cx->cfg.dial.max); else prompt_Printf(arg->prompt, " Dial tries: infinite, delay "); if (arg->cx->cfg.dial.next_timeout > 0) prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout); else prompt_Printf(arg->prompt, "random/"); if (arg->cx->cfg.dial.timeout > 0) prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout); else prompt_Printf(arg->prompt, "random\n"); prompt_Printf(arg->prompt, " Reconnect tries: %d, delay ", arg->cx->cfg.reconnect.max); if (arg->cx->cfg.reconnect.timeout > 0) prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout); else prompt_Printf(arg->prompt, "random\n"); prompt_Printf(arg->prompt, " Dial Script: %s\n", arg->cx->cfg.script.dial); prompt_Printf(arg->prompt, " Login Script: %s\n", arg->cx->cfg.script.login); prompt_Printf(arg->prompt, " Hangup Script: %s\n", arg->cx->cfg.script.hangup); return 0; } int datalink_SetReconnect(struct cmdargs const *arg) { if (arg->argc == arg->argn+2) { arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]); arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]); return 0; } return -1; } int datalink_SetRedial(struct cmdargs const *arg) { int timeout; int tries; char *dot; if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) { if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 && (arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) { arg->cx->cfg.dial.timeout = -1; randinit(); } else { timeout = atoi(arg->argv[arg->argn]); if (timeout >= 0) arg->cx->cfg.dial.timeout = timeout; else { log_Printf(LogWARN, "Invalid redial timeout\n"); return -1; } } dot = strchr(arg->argv[arg->argn], '.'); if (dot) { if (strcasecmp(++dot, "random") == 0) { arg->cx->cfg.dial.next_timeout = -1; randinit(); } else { timeout = atoi(dot); if (timeout >= 0) arg->cx->cfg.dial.next_timeout = timeout; else { log_Printf(LogWARN, "Invalid next redial timeout\n"); return -1; } } } else /* Default next timeout */ arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT; if (arg->argc == arg->argn+2) { tries = atoi(arg->argv[arg->argn+1]); if (tries >= 0) { arg->cx->cfg.dial.max = tries; } else { log_Printf(LogWARN, "Invalid retry value\n"); return 1; } } return 0; } return -1; } static const char *states[] = { "closed", "opening", "hangup", "dial", "login", "ready", "lcp", "auth", "open" }; const char * datalink_State(struct datalink *dl) { if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0]) return "unknown"; return states[dl->state]; } static void datalink_NewState(struct datalink *dl, int state) { if (state != dl->state) { if (state >= 0 && state < sizeof states / sizeof states[0]) { log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl), states[state]); dl->state = state; } else log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state); } } struct datalink * iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov, int fd) { struct datalink *dl, *cdl; u_int retry; char *oname; dl = (struct datalink *)iov[(*niov)++].iov_base; dl->name = iov[*niov].iov_base; if (dl->name[DATALINK_MAXNAME-1]) { dl->name[DATALINK_MAXNAME-1] = '\0'; if (strlen(dl->name) == DATALINK_MAXNAME - 1) log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name); } /* Make sure the name is unique ! */ oname = NULL; do { for (cdl = bundle->links; cdl; cdl = cdl->next) if (!strcasecmp(dl->name, cdl->name)) { if (oname) free(datalink_NextName(dl)); else oname = datalink_NextName(dl); break; /* Keep renaming 'till we have no conflicts */ } } while (cdl); if (oname) { log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name); free(oname); } else { dl->name = strdup(dl->name); dl->physical->link.name = dl->name; free(iov[*niov].iov_base); } (*niov)++; dl->desc.type = DATALINK_DESCRIPTOR; dl->desc.next = NULL; dl->desc.UpdateSet = datalink_UpdateSet; dl->desc.IsSet = datalink_IsSet; dl->desc.Read = datalink_Read; dl->desc.Write = datalink_Write; mp_linkInit(&dl->mp); *dl->phone.list = '\0'; dl->phone.next = NULL; dl->phone.alt = NULL; dl->phone.chosen = "N/A"; dl->bundle = bundle; dl->next = NULL; memset(&dl->dial_timer, '\0', sizeof dl->dial_timer); dl->dial_tries = 0; dl->reconnect_tries = 0; dl->parent = &bundle->fsm; dl->fsmp.LayerStart = datalink_LayerStart; dl->fsmp.LayerUp = datalink_LayerUp; dl->fsmp.LayerDown = datalink_LayerDown; dl->fsmp.LayerFinish = datalink_LayerFinish; dl->fsmp.object = dl; retry = dl->pap.cfg.fsmretry; auth_Init(&dl->pap); dl->pap.cfg.fsmretry = retry; retry = dl->chap.auth.cfg.fsmretry; auth_Init(&dl->chap.auth); dl->chap.auth.cfg.fsmretry = retry; dl->physical = iov2modem(dl, iov, niov, maxiov, fd); if (!dl->physical) { free(dl->name); free(dl); dl = NULL; } else { chat_Init(&dl->chat, dl->physical, NULL, 1, NULL); log_Printf(LogPHASE, "%s: Transferred in %s state\n", dl->name, datalink_State(dl)); } return dl; } int datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov) { /* If `dl' is NULL, we're allocating before a Fromiov() */ int link_fd; if (dl) { timer_Stop(&dl->dial_timer); timer_Stop(&dl->pap.authtimer); timer_Stop(&dl->chap.auth.authtimer); } if (*niov >= maxiov - 1) { log_Printf(LogERROR, "Toiov: No room for datalink !\n"); if (dl) { free(dl->name); free(dl); } return -1; } iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl); iov[(*niov)++].iov_len = sizeof *dl; iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME); iov[(*niov)++].iov_len = DATALINK_MAXNAME; link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov); if (link_fd == -1 && dl) { free(dl->name); free(dl); } return link_fd; } char * datalink_NextName(struct datalink *dl) { int f, n; char *name, *oname; n = strlen(dl->name); name = (char *)malloc(n+3); for (f = n - 1; f >= 0; f--) if (!isdigit(dl->name[f])) break; n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name); sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1); oname = dl->name; dl->physical->link.name = dl->name = name; return oname; } diff --git a/usr.sbin/ppp/datalink.h b/usr.sbin/ppp/datalink.h index 1be0a05fec1e..7634a01c3a48 100644 --- a/usr.sbin/ppp/datalink.h +++ b/usr.sbin/ppp/datalink.h @@ -1,126 +1,128 @@ /*- * 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: datalink.h,v 1.1.2.22 1998/05/02 21:57:45 brian Exp $ + * $Id: datalink.h,v 1.1.2.23 1998/05/06 23:49:33 brian Exp $ */ #define DATALINK_CLOSED (0) #define DATALINK_OPENING (1) #define DATALINK_HANGUP (2) #define DATALINK_DIAL (3) #define DATALINK_LOGIN (4) #define DATALINK_READY (5) #define DATALINK_LCP (6) #define DATALINK_AUTH (7) #define DATALINK_OPEN (8) #define DATALINK_MAXNAME (20) struct iovec; struct prompt; struct physical; struct bundle; struct datalink { struct descriptor desc; /* We play either a physical or a chat */ int state; /* Our DATALINK_* state */ struct physical *physical; /* Our link */ struct chat chat; /* For bringing the link up & down */ struct { unsigned run : 1; /* run scripts ? */ unsigned packetmode : 1; /* Go into packet mode after login ? */ } script; struct pppTimer dial_timer; /* For timing between opens & scripts */ struct { struct { char dial[SCRIPT_LEN]; /* dial */ char login[SCRIPT_LEN]; /* login */ char hangup[SCRIPT_LEN]; /* hangup */ } script; struct { char list[SCRIPT_LEN]; /* Telephone Numbers */ } phone; struct { int max; /* initially try again this number of times */ int next_timeout; /* Redial next timeout value */ int timeout; /* Redial timeout value (end of phone list) */ } dial; struct { int max; /* initially try again this number of times */ int timeout; /* Timeout before reconnect on carrier loss */ } reconnect; } cfg; /* All our config data is in here */ struct { char list[SCRIPT_LEN]; /* copy of cfg.list for strsep() */ char *next; /* Next phone from the list */ char *alt; /* Next phone from the list */ const char *chosen; /* Chosen phone number after DIAL */ } phone; int dial_tries; /* currently try again this number of times */ unsigned reconnect_tries; /* currently try again this number of times */ char *name; /* Our name */ struct peerid peer; /* Peer identification */ struct fsm_parent fsmp; /* Our callback functions */ const struct fsm_parent *parent; /* Our parent */ struct authinfo pap; /* Authentication using pap */ struct chap chap; /* Authentication using chap */ struct mp_link mp; /* multilink data */ struct bundle *bundle; /* for the moment */ struct datalink *next; /* Next in the list */ }; #define descriptor2datalink(d) \ ((d)->type == DATALINK_DESCRIPTOR ? (struct datalink *)(d) : NULL) extern struct datalink *datalink_Create(const char *name, struct bundle *, int); extern struct datalink *datalink_Clone(struct datalink *, const char *); extern struct datalink *iov2datalink(struct bundle *, struct iovec *, int *, int, int); extern int datalink2iov(struct datalink *, struct iovec *, int *, int); extern struct datalink *datalink_Destroy(struct datalink *); extern void datalink_GotAuthname(struct datalink *, const char *, int); extern void datalink_Up(struct datalink *, int, int); extern void datalink_Close(struct datalink *, int); extern void datalink_Down(struct datalink *, int); extern void datalink_StayDown(struct datalink *); extern void datalink_AuthOk(struct datalink *); extern void datalink_AuthNotOk(struct datalink *); extern int datalink_Show(struct cmdargs const *); extern int datalink_SetRedial(struct cmdargs const *); extern int datalink_SetReconnect(struct cmdargs const *); extern const char *datalink_State(struct datalink *); extern char *datalink_NextName(struct datalink *); +extern int datalink_RemoveFromSet(struct datalink *, fd_set *, fd_set *, + fd_set *); diff --git a/usr.sbin/ppp/main.c b/usr.sbin/ppp/main.c index c875b6a1f0d8..0a57e62c037f 100644 --- a/usr.sbin/ppp/main.c +++ b/usr.sbin/ppp/main.c @@ -1,553 +1,548 @@ /* * User Process PPP * * 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: main.c,v 1.121.2.58 1998/05/08 18:50:21 brian Exp $ + * $Id: main.c,v 1.121.2.59 1998/05/10 22:20:09 brian Exp $ * * TODO: */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mbuf.h" #include "log.h" #include "defs.h" #include "id.h" #include "timer.h" #include "fsm.h" #include "lqr.h" #include "hdlc.h" #include "lcp.h" #include "ccp.h" #include "iplist.h" #include "throughput.h" #include "slcompress.h" #include "ipcp.h" #include "filter.h" #include "descriptor.h" #include "link.h" #include "mp.h" #include "bundle.h" #include "loadalias.h" #include "auth.h" #include "systems.h" #include "ip.h" #include "sig.h" #include "main.h" #include "tun.h" #include "server.h" #include "prompt.h" #include "chat.h" #include "chap.h" #include "datalink.h" #ifndef O_NONBLOCK #ifdef O_NDELAY #define O_NONBLOCK O_NDELAY #endif #endif static char pid_filename[MAXPATHLEN]; static void DoLoop(struct bundle *, struct prompt *); static void TerminalStop(int); static const char *ex_desc(int); static struct bundle *SignalBundle; static struct prompt *SignalPrompt; void Cleanup(int excode) { SignalBundle->CleaningUp = 1; if (bundle_Phase(SignalBundle) != PHASE_DEAD) bundle_Close(SignalBundle, NULL, 1); } void AbortProgram(int excode) { server_Close(SignalBundle); ID0unlink(pid_filename); log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode)); bundle_Close(SignalBundle, NULL, 1); bundle_Destroy(SignalBundle); log_Close(); exit(excode); } static void CloseConnection(int signo) { /* NOTE, these are manual, we've done a setsid() */ sig_signal(SIGINT, SIG_IGN); log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo); bundle_Down(SignalBundle); sig_signal(SIGINT, CloseConnection); } static void CloseSession(int signo) { log_Printf(LogPHASE, "Signal %d, terminate.\n", signo); Cleanup(EX_TERM); } static pid_t BGPid = 0; static void KillChild(int signo) { log_Printf(LogPHASE, "Parent: Signal %d\n", signo); kill(BGPid, SIGINT); } static void TerminalCont(int signo) { signal(SIGCONT, SIG_DFL); prompt_Continue(SignalPrompt); } static void TerminalStop(int signo) { prompt_Suspend(SignalPrompt); signal(SIGCONT, TerminalCont); raise(SIGSTOP); } static void BringDownServer(int signo) { /* Drops all child prompts too ! */ server_Close(SignalBundle); } static const char * ex_desc(int ex) { static char num[12]; static const char *desc[] = { "normal", "start", "sock", "modem", "dial", "dead", "done", "reboot", "errdead", "hangup", "term", "nodial", "nologin" }; if (ex >= 0 && ex < sizeof desc / sizeof *desc) return desc[ex]; snprintf(num, sizeof num, "%d", ex); return num; } static void Usage(void) { fprintf(stderr, "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ]" #ifndef NOALIAS " [ -alias ]" #endif " [system]\n"); exit(EX_START); } static char * ProcessArgs(int argc, char **argv, int *mode) { int optc, labelrequired; char *cp; optc = labelrequired = 0; *mode = PHYS_MANUAL; while (argc > 0 && **argv == '-') { cp = *argv + 1; if (strcmp(cp, "auto") == 0) { *mode = PHYS_DEMAND; labelrequired = 1; } else if (strcmp(cp, "background") == 0) { *mode = PHYS_1OFF; labelrequired = 1; } else if (strcmp(cp, "direct") == 0) *mode = PHYS_DIRECT; else if (strcmp(cp, "dedicated") == 0) *mode = PHYS_DEDICATED; else if (strcmp(cp, "ddial") == 0) { *mode = PHYS_PERM; labelrequired = 1; } else if (strcmp(cp, "alias") == 0) { #ifndef NOALIAS if (alias_Load() != 0) #endif log_Printf(LogWARN, "Cannot load alias library\n"); optc--; /* this option isn't exclusive */ } else Usage(); optc++; argv++; argc--; } if (argc > 1) { fprintf(stderr, "You may specify only one system label.\n"); exit(EX_START); } if (optc > 1) { fprintf(stderr, "You may specify only one mode.\n"); exit(EX_START); } if (labelrequired && argc != 1) { fprintf(stderr, "Destination system must be specified in" " auto, background or ddial mode.\n"); exit(EX_START); } return argc == 1 ? *argv : NULL; /* Don't SetLabel yet ! */ } int main(int argc, char **argv) { FILE *lockfile; char *name, *label; int nfds, mode; struct bundle *bundle; struct prompt *prompt; nfds = getdtablesize(); if (nfds >= FD_SETSIZE) /* * If we've got loads of file descriptors, make sure they're all * closed. If they aren't, we may end up with a seg fault when our * `fd_set's get too big when select()ing ! */ while (--nfds > 2) close(nfds); name = strrchr(argv[0], '/'); log_Open(name ? name + 1 : argv[0]); argc--; argv++; label = ProcessArgs(argc, argv, &mode); #ifdef __FreeBSD__ /* * A FreeBSD hack to dodge a bug in the tty driver that drops output * occasionally.... I must find the real reason some time. To display * the dodgy behaviour, comment out this bit, make yourself a large * routing table and then run ppp in interactive mode. The `show route' * command will drop chunks of data !!! */ if (mode == PHYS_MANUAL) { close(STDIN_FILENO); if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) { fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY); return 2; } } #endif /* Allow output for the moment (except in direct mode) */ if (mode == PHYS_DIRECT) prompt = NULL; else { const char *m; SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD); if (mode == PHYS_PERM) m = "direct dial"; else if (mode & PHYS_1OFF) m = "background"; else if (mode & PHYS_DEMAND) m = "auto"; else if (mode & PHYS_DEDICATED) m = "dedicated"; else if (mode & PHYS_MANUAL) m = "interactive"; else m = NULL; if (m) prompt_Printf(prompt, "Working in %s mode\n", m); } ID0init(); if (ID0realuid() != 0) { char conf[200], *ptr; snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE); do { if (!access(conf, W_OK)) { log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n", conf); return -1; } ptr = conf + strlen(conf)-2; while (ptr > conf && *ptr != '/') *ptr-- = '\0'; } while (ptr >= conf); } if (!system_IsValid(label, prompt, mode)) { fprintf(stderr, "You may not use ppp in this mode with this label\n"); if (mode == PHYS_DIRECT) { const char *l; l = label ? label : "default"; log_Printf(LogWARN, "Label %s rejected -direct connection\n", l); } log_Close(); return 1; } if ((bundle = bundle_Create(TUN_PREFIX, prompt, mode)) == NULL) { log_Printf(LogWARN, "bundle_Create: %s\n", strerror(errno)); return EX_START; } SignalBundle = bundle; if (system_Select(bundle, "default", CONFFILE, prompt) < 0) prompt_Printf(prompt, "Warning: No default entry found in config file.\n"); sig_signal(SIGHUP, CloseSession); sig_signal(SIGTERM, CloseSession); sig_signal(SIGINT, CloseConnection); sig_signal(SIGQUIT, CloseSession); sig_signal(SIGALRM, SIG_IGN); signal(SIGPIPE, SIG_IGN); if (mode == PHYS_MANUAL) sig_signal(SIGTSTP, TerminalStop); sig_signal(SIGUSR2, BringDownServer); if (label) { /* * Set label both before and after system_Select ! * This way, "set enddisc label" works during system_Select, and we * also end up with the correct label if we have embedded load * commands. */ bundle_SetLabel(bundle, label); if (system_Select(bundle, label, CONFFILE, prompt) < 0) { prompt_Printf(prompt, "Destination system (%s) not found.\n", label); AbortProgram(EX_START); } bundle_SetLabel(bundle, label); if (mode == PHYS_DEMAND && bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) { prompt_Printf(prompt, "You must \"set ifaddr\" with a peer address " "in label %s for auto mode.\n", label); AbortProgram(EX_START); } } if (mode != PHYS_MANUAL) { if (mode != PHYS_DIRECT) { int bgpipe[2]; pid_t bgpid; if (mode == PHYS_1OFF && pipe(bgpipe)) { log_Printf(LogERROR, "pipe: %s\n", strerror(errno)); AbortProgram(EX_SOCK); } bgpid = fork(); if (bgpid == -1) { log_Printf(LogERROR, "fork: %s\n", strerror(errno)); AbortProgram(EX_SOCK); } if (bgpid) { char c = EX_NORMAL; if (mode == PHYS_1OFF) { close(bgpipe[1]); BGPid = bgpid; /* If we get a signal, kill the child */ signal(SIGHUP, KillChild); signal(SIGTERM, KillChild); signal(SIGINT, KillChild); signal(SIGQUIT, KillChild); /* Wait for our child to close its pipe before we exit */ if (read(bgpipe[0], &c, 1) != 1) { prompt_Printf(prompt, "Child exit, no status.\n"); log_Printf(LogPHASE, "Parent: Child exit, no status.\n"); } else if (c == EX_NORMAL) { prompt_Printf(prompt, "PPP enabled.\n"); log_Printf(LogPHASE, "Parent: PPP enabled.\n"); } else { prompt_Printf(prompt, "Child failed (%s).\n", ex_desc((int) c)); log_Printf(LogPHASE, "Parent: Child failed (%s).\n", ex_desc((int) c)); } close(bgpipe[0]); } return c; } else if (mode == PHYS_1OFF) { close(bgpipe[0]); bundle->notify.fd = bgpipe[1]; } /* -auto, -dedicated, -ddial & -background */ prompt_Destroy(prompt, 0); close(STDOUT_FILENO); close(STDERR_FILENO); close(STDIN_FILENO); setsid(); } else { /* -direct: STDIN_FILENO gets used by modem_Open */ prompt_TtyInit(NULL); close(STDOUT_FILENO); close(STDERR_FILENO); } } else { /* Interactive mode */ close(STDERR_FILENO); prompt_TtyInit(prompt); prompt_TtyCommandMode(prompt); prompt_Required(prompt); } snprintf(pid_filename, sizeof pid_filename, "%stun%d.pid", _PATH_VARRUN, bundle->unit); lockfile = ID0fopen(pid_filename, "w"); if (lockfile != NULL) { fprintf(lockfile, "%d\n", (int) getpid()); fclose(lockfile); } #ifndef RELEASE_CRUNCH else log_Printf(LogALERT, "Warning: Can't create %s: %s\n", pid_filename, strerror(errno)); #endif log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(mode)); DoLoop(bundle, prompt); AbortProgram(EX_NORMAL); return EX_NORMAL; } static void DoLoop(struct bundle *bundle, struct prompt *prompt) { fd_set rfds, wfds, efds; int i, nfds; do { nfds = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); sig_Handle(); descriptor_UpdateSet(&bundle->desc, &rfds, &wfds, &efds, &nfds); descriptor_UpdateSet(&server.desc, &rfds, &wfds, &efds, &nfds); - descriptor_UpdateSet(&bundle->ncp.mp.server.desc, &rfds, &wfds, - &efds, &nfds); if (bundle_IsDead(bundle)) /* Don't select - we'll be here forever */ break; i = select(nfds, &rfds, &wfds, &efds, NULL); if (i == 0) continue; else if (i < 0) { if (errno == EINTR) continue; log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno)); if (log_IsKept(LogTIMER)) { struct timeval t; for (i = 0; i <= nfds; i++) { if (FD_ISSET(i, &rfds)) { log_Printf(LogTIMER, "Read set contains %d\n", i); FD_CLR(i, &rfds); t.tv_sec = t.tv_usec = 0; if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { log_Printf(LogTIMER, "The culprit !\n"); break; } } if (FD_ISSET(i, &wfds)) { log_Printf(LogTIMER, "Write set contains %d\n", i); FD_CLR(i, &wfds); t.tv_sec = t.tv_usec = 0; if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { log_Printf(LogTIMER, "The culprit !\n"); break; } } if (FD_ISSET(i, &efds)) { log_Printf(LogTIMER, "Error set contains %d\n", i); FD_CLR(i, &efds); t.tv_sec = t.tv_usec = 0; if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { log_Printf(LogTIMER, "The culprit !\n"); break; } } } } break; } for (i = 0; i <= nfds; i++) if (FD_ISSET(i, &efds)) { log_Printf(LogALERT, "Exception detected on descriptor %d\n", i); break; } if (i <= nfds) break; - if (descriptor_IsSet(&bundle->ncp.mp.server.desc, &rfds)) - descriptor_Read(&bundle->ncp.mp.server.desc, bundle, &rfds); - if (descriptor_IsSet(&server.desc, &rfds)) descriptor_Read(&server.desc, bundle, &rfds); if (descriptor_IsSet(&bundle->desc, &wfds)) descriptor_Write(&bundle->desc, bundle, &wfds); if (descriptor_IsSet(&bundle->desc, &rfds)) descriptor_Read(&bundle->desc, bundle, &rfds); } while (bundle_CleanDatalinks(bundle), !bundle_IsDead(bundle)); log_Printf(LogDEBUG, "DoLoop done.\n"); } diff --git a/usr.sbin/ppp/mp.c b/usr.sbin/ppp/mp.c index 570f5f143495..9f3cd488fab5 100644 --- a/usr.sbin/ppp/mp.c +++ b/usr.sbin/ppp/mp.c @@ -1,989 +1,992 @@ /*- * 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: mp.c,v 1.1.2.27 1998/05/10 22:20:11 brian Exp $ + * $Id: mp.c,v 1.1.2.28 1998/05/15 18:21:09 brian Exp $ */ #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 "defs.h" #include "timer.h" #include "fsm.h" #include "iplist.h" #include "throughput.h" #include "slcompress.h" #include "ipcp.h" #include "auth.h" #include "lcp.h" #include "lqr.h" #include "hdlc.h" #include "async.h" #include "ccp.h" #include "link.h" #include "descriptor.h" #include "physical.h" #include "chat.h" #include "lcpproto.h" #include "filter.h" #include "mp.h" #include "chap.h" #include "datalink.h" #include "bundle.h" #include "ip.h" #include "prompt.h" #include "id.h" #include "arp.h" void peerid_Init(struct peerid *peer) { peer->enddisc.class = 0; *peer->enddisc.address = '\0'; peer->enddisc.len = 0; *peer->authname = '\0'; } int peerid_Equal(const struct peerid *p1, const struct peerid *p2) { return !strcmp(p1->authname, p2->authname) && p1->enddisc.class == p2->enddisc.class && p1->enddisc.len == p2->enddisc.len && !memcmp(p1->enddisc.address, p2->enddisc.address, p1->enddisc.len); } static u_int32_t inc_seq(unsigned is12bit, u_int32_t seq) { seq++; if (is12bit) { if (seq & 0xfffff000) seq = 0; } else if (seq & 0xff000000) seq = 0; return seq; } static int isbefore(unsigned is12bit, u_int32_t seq1, u_int32_t seq2) { u_int32_t max = (is12bit ? 0xfff : 0xffffff) - 0x200; if (seq1 > max) { if (seq2 < 0x200 || seq2 > seq1) return 1; } else if ((seq1 > 0x200 || seq2 <= max) && seq1 < seq2) return 1; return 0; } static int mp_ReadHeader(struct mp *mp, struct mbuf *m, struct mp_header *header) { if (mp->local_is12bit) { header->seq = ntohs(*(u_int16_t *)MBUF_CTOP(m)); if (header->seq & 0x3000) { log_Printf(LogWARN, "Oops - MP header without required zero bits\n"); return 0; } header->begin = header->seq & 0x8000 ? 1 : 0; header->end = header->seq & 0x4000 ? 1 : 0; header->seq &= 0x0fff; return 2; } else { header->seq = ntohl(*(u_int32_t *)MBUF_CTOP(m)); if (header->seq & 0x3f000000) { log_Printf(LogWARN, "Oops - MP header without required zero bits\n"); return 0; } header->begin = header->seq & 0x80000000 ? 1 : 0; header->end = header->seq & 0x40000000 ? 1 : 0; header->seq &= 0x00ffffff; return 4; } } static void mp_LayerStart(void *v, struct fsm *fp) { /* The given FSM (ccp) is about to start up ! */ } static void mp_LayerUp(void *v, struct fsm *fp) { /* The given fsm (ccp) is now up */ } static void mp_LayerDown(void *v, struct fsm *fp) { /* The given FSM (ccp) has been told to come down */ } static void mp_LayerFinish(void *v, struct fsm *fp) { /* The given fsm (ccp) is now down */ if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE) fsm_Open(fp); /* CCP goes to ST_STOPPED */ } void mp_Init(struct mp *mp, struct bundle *bundle) { mp->peer_is12bit = mp->local_is12bit = 0; mp->peer_mrru = mp->local_mrru = 0; peerid_Init(&mp->peer); mp->out.seq = 0; mp->out.link = 0; mp->seq.min_in = 0; mp->seq.next_in = 0; mp->inbufs = NULL; mp->bundle = bundle; mp->link.type = MP_LINK; mp->link.name = "mp"; mp->link.len = sizeof *mp; throughput_init(&mp->link.throughput); memset(mp->link.Queue, '\0', sizeof mp->link.Queue); memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in); memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out); mp->fsmp.LayerStart = mp_LayerStart; mp->fsmp.LayerUp = mp_LayerUp; mp->fsmp.LayerDown = mp_LayerDown; mp->fsmp.LayerFinish = mp_LayerFinish; mp->fsmp.object = mp; mpserver_Init(&mp->server); mp->cfg.mrru = 0; mp->cfg.shortseq = NEG_ENABLED|NEG_ACCEPTED; mp->cfg.enddisc.class = 0; *mp->cfg.enddisc.address = '\0'; mp->cfg.enddisc.len = 0; lcp_Init(&mp->link.lcp, mp->bundle, &mp->link, NULL); ccp_Init(&mp->link.ccp, mp->bundle, &mp->link, &mp->fsmp); } int mp_Up(struct mp *mp, struct datalink *dl) { struct lcp *lcp = &dl->physical->link.lcp; if (mp->active) { /* We're adding a link - do a last validation on our parameters */ if (!peerid_Equal(&dl->peer, &mp->peer)) { log_Printf(LogPHASE, "%s: Inappropriate peer !\n", dl->name); return MP_FAILED; } if (mp->local_mrru != lcp->want_mrru || mp->peer_mrru != lcp->his_mrru || mp->local_is12bit != lcp->want_shortseq || mp->peer_is12bit != lcp->his_shortseq) { log_Printf(LogPHASE, "%s: Invalid MRRU/SHORTSEQ MP parameters !\n", dl->name); return MP_FAILED; } return MP_ADDED; } else { /* First link in multilink mode */ mp->local_mrru = lcp->want_mrru; mp->peer_mrru = lcp->his_mrru; mp->local_is12bit = lcp->want_shortseq; mp->peer_is12bit = lcp->his_shortseq; mp->peer = dl->peer; throughput_init(&mp->link.throughput); memset(mp->link.Queue, '\0', sizeof mp->link.Queue); memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in); memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out); mp->out.seq = 0; mp->out.link = 0; mp->seq.min_in = 0; mp->seq.next_in = 0; /* * Now we create our server socket. * If it already exists, join it. Otherwise, create and own it */ switch (mpserver_Open(&mp->server, &mp->peer)) { case MPSERVER_CONNECTED: log_Printf(LogPHASE, "mp: Transfer link on %s\n", mp->server.socket.sun_path); mp->server.send.dl = dl; /* Defer 'till it's safe to send */ return MP_LINKSENT; case MPSERVER_FAILED: return MP_FAILED; case MPSERVER_LISTENING: log_Printf(LogPHASE, "mp: Listening on %s\n", mp->server.socket.sun_path); log_Printf(LogPHASE, " First link: %s\n", dl->name); /* Re-point our IPCP layer at our MP link */ ipcp_SetLink(&mp->bundle->ncp.ipcp, &mp->link); /* Our lcp's already up 'cos of the NULL parent */ fsm_Up(&mp->link.ccp.fsm); fsm_Open(&mp->link.ccp.fsm); mp->active = 1; break; } } return MP_UP; } void mp_Down(struct mp *mp) { if (mp->active) { struct mbuf *next; /* Don't want any more of these */ mpserver_Close(&mp->server); /* CCP goes down with a bang */ fsm_Down(&mp->link.ccp.fsm); fsm_Close(&mp->link.ccp.fsm); /* Received fragments go in the bit-bucket */ while (mp->inbufs) { next = mp->inbufs->pnext; mbuf_Free(mp->inbufs); mp->inbufs = next; } peerid_Init(&mp->peer); mp->active = 0; } } void mp_linkInit(struct mp_link *mplink) { mplink->seq = 0; mplink->weight = 1500; } void mp_Input(struct mp *mp, struct mbuf *m, struct physical *p) { struct mp_header mh, h; struct mbuf *q, *last; int32_t seq; if (mp_ReadHeader(mp, m, &mh) == 0) { mbuf_Free(m); return; } seq = p->dl->mp.seq; p->dl->mp.seq = mh.seq; if (mp->seq.min_in == seq) { /* * We've received new data on the link that has our min (oldest) seq. * Figure out which link now has the smallest (oldest) seq. */ struct datalink *dl; mp->seq.min_in = p->dl->mp.seq; for (dl = mp->bundle->links; dl; dl = dl->next) if (mp->seq.min_in > dl->mp.seq) mp->seq.min_in = dl->mp.seq; } /* * Now process as many of our fragments as we can, adding our new * fragment in as we go, and ordering with the oldest at the top of * the queue. */ if (!mp->inbufs) { mp->inbufs = m; m = NULL; } last = NULL; seq = mp->seq.next_in; q = mp->inbufs; while (q) { mp_ReadHeader(mp, q, &h); if (m && isbefore(mp->local_is12bit, mh.seq, h.seq)) { /* Our received fragment fits in before this one, so link it in */ if (last) last->pnext = m; else mp->inbufs = m; m->pnext = q; q = m; h = mh; m = NULL; } if (h.seq != seq) { /* we're missing something :-( */ if (mp->seq.min_in > seq) { /* we're never gonna get it */ struct mbuf *next; /* Zap all older fragments */ while (mp->inbufs != q) { log_Printf(LogDEBUG, "Drop frag\n"); next = mp->inbufs->pnext; mbuf_Free(mp->inbufs); mp->inbufs = next; } /* * Zap everything until the next `end' fragment OR just before * the next `begin' fragment OR 'till seq.min_in - whichever * comes first. */ do { mp_ReadHeader(mp, mp->inbufs, &h); if (h.begin) { /* We might be able to process this ! */ h.seq--; /* We're gonna look for fragment with h.seq+1 */ break; } next = mp->inbufs->pnext; log_Printf(LogDEBUG, "Drop frag %u\n", h.seq); mbuf_Free(mp->inbufs); mp->inbufs = next; } while (mp->inbufs && (h.seq >= mp->seq.min_in || h.end)); /* * Continue processing things from here. * This deals with the possibility that we received a fragment * on the slowest link that invalidates some of our data (because * of the hole at `q'), but where there are subsequent `whole' * packets that have already been received. */ mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq); last = NULL; q = mp->inbufs; } else /* we may still receive the missing fragment */ break; } else if (h.end) { /* We've got something, reassemble */ struct mbuf **frag = &q; int len; u_long first = -1; do { *frag = mp->inbufs; mp->inbufs = mp->inbufs->pnext; len = mp_ReadHeader(mp, *frag, &h); if (first == -1) first = h.seq; (*frag)->offset += len; (*frag)->cnt -= len; (*frag)->pnext = NULL; if (frag == &q && !h.begin) { log_Printf(LogWARN, "Oops - MP frag %lu should have a begin flag\n", (u_long)h.seq); mbuf_Free(q); q = NULL; } else if (frag != &q && h.begin) { log_Printf(LogWARN, "Oops - MP frag %lu should have an end flag\n", (u_long)h.seq - 1); /* * Stuff our fragment back at the front of the queue and zap * our half-assembed packet. */ (*frag)->pnext = mp->inbufs; mp->inbufs = *frag; *frag = NULL; mbuf_Free(q); q = NULL; frag = &q; h.end = 0; /* just in case it's a whole packet */ } else do frag = &(*frag)->next; while (*frag != NULL); } while (!h.end); if (q) { u_short proto; u_char ch; q = mbuf_Read(q, &ch, 1); proto = ch; if (!(proto & 1)) { q = mbuf_Read(q, &ch, 1); proto <<= 8; proto += ch; } if (log_IsKept(LogDEBUG)) log_Printf(LogDEBUG, "MP: Reassembled frags %ld-%lu, length %d\n", first, (u_long)h.seq, mbuf_Length(q)); hdlc_DecodePacket(mp->bundle, proto, q, &mp->link); } mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq); last = NULL; q = mp->inbufs; } else { /* Look for the next fragment */ seq = inc_seq(mp->local_is12bit, seq); last = q; q = q->pnext; } } if (m) { /* We still have to find a home for our new fragment */ last = NULL; for (q = mp->inbufs; q; last = q, q = q->pnext) { mp_ReadHeader(mp, q, &h); if (isbefore(mp->local_is12bit, mh.seq, h.seq)) break; } /* Our received fragment fits in here */ if (last) last->pnext = m; else mp->inbufs = m; m->pnext = q; } } static void mp_Output(struct mp *mp, struct link *l, struct mbuf *m, u_int32_t begin, u_int32_t end) { struct mbuf *mo; /* Stuff an MP header on the front of our packet and send it */ mo = mbuf_Alloc(4, MB_MP); mo->next = m; if (mp->peer_is12bit) { u_int16_t *seq16; seq16 = (u_int16_t *)MBUF_CTOP(mo); *seq16 = htons((begin << 15) | (end << 14) | (u_int16_t)mp->out.seq); mo->cnt = 2; } else { u_int32_t *seq32; seq32 = (u_int32_t *)MBUF_CTOP(mo); *seq32 = htonl((begin << 31) | (end << 30) | (u_int32_t)mp->out.seq); mo->cnt = 4; } if (log_IsKept(LogDEBUG)) log_Printf(LogDEBUG, "MP[frag %d]: Send %d bytes on link `%s'\n", mp->out.seq, mbuf_Length(mo), l->name); mp->out.seq = inc_seq(mp->peer_is12bit, mp->out.seq); if (!ccp_Compress(&l->ccp, l, PRI_NORMAL, PROTO_MP, mo)) hdlc_Output(l, PRI_NORMAL, PROTO_MP, mo); } int mp_FillQueues(struct bundle *bundle) { struct mp *mp = &bundle->ncp.mp; struct datalink *dl, *fdl; int total, add, len, thislink, nlinks; u_int32_t begin, end; struct mbuf *m, *mo; thislink = nlinks = 0; for (fdl = NULL, dl = bundle->links; dl; dl = dl->next) { if (!fdl) { if (thislink == mp->out.link) fdl = dl; else thislink++; } nlinks++; } if (!fdl) { fdl = bundle->links; if (!fdl) return 0; thislink = 0; } total = 0; for (dl = fdl; nlinks > 0; dl = dl->next, nlinks--, thislink++) { if (!dl) { dl = bundle->links; thislink = 0; } if (dl->state != DATALINK_OPEN) continue; if (dl->physical->out) /* this link has suffered a short write. Let it continue */ continue; add = link_QueueLen(&dl->physical->link); total += add; if (add) /* this link has got stuff already queued. Let it continue */ continue; if (!link_QueueLen(&mp->link) && !ip_FlushPacket(&mp->link, bundle)) /* Nothing else to send */ break; m = link_Dequeue(&mp->link); len = mbuf_Length(m); begin = 1; end = 0; while (!end) { if (dl->state == DATALINK_OPEN) { if (len <= dl->mp.weight + LINK_MINWEIGHT) { /* * XXX: Should we remember how much of our `weight' wasn't sent * so that we can compensate next time ? */ mo = m; end = 1; } else { mo = mbuf_Alloc(dl->mp.weight, MB_MP); mo->cnt = dl->mp.weight; len -= mo->cnt; m = mbuf_Read(m, MBUF_CTOP(mo), mo->cnt); } mp_Output(mp, &dl->physical->link, mo, begin, end); begin = 0; } if (!end) { nlinks--; dl = dl->next; if (!dl) { dl = bundle->links; thislink = 0; } else thislink++; } } } mp->out.link = thislink; /* Start here next time */ return total; } int mp_SetDatalinkWeight(struct cmdargs const *arg) { int val; if (arg->argc != arg->argn+1) return -1; val = atoi(arg->argv[arg->argn]); if (val < LINK_MINWEIGHT) { log_Printf(LogWARN, "Link weights must not be less than %d\n", LINK_MINWEIGHT); return 1; } arg->cx->mp.weight = val; return 0; } int mp_ShowStatus(struct cmdargs const *arg) { struct mp *mp = &arg->bundle->ncp.mp; prompt_Printf(arg->prompt, "Multilink is %sactive\n", mp->active ? "" : "in"); if (mp->active) { struct mbuf *m; int bufs = 0; prompt_Printf(arg->prompt, "Socket: %s\n", mp->server.socket.sun_path); for (m = mp->inbufs; m; m = m->pnext) bufs++; prompt_Printf(arg->prompt, "Pending frags: %d\n", bufs); } prompt_Printf(arg->prompt, "\nMy Side:\n"); if (mp->active) { prompt_Printf(arg->prompt, " MRRU: %u\n", mp->local_mrru); prompt_Printf(arg->prompt, " Short Seq: %s\n", mp->local_is12bit ? "on" : "off"); } prompt_Printf(arg->prompt, " Discriminator: %s\n", mp_Enddisc(mp->cfg.enddisc.class, mp->cfg.enddisc.address, mp->cfg.enddisc.len)); prompt_Printf(arg->prompt, "\nHis Side:\n"); if (mp->active) { prompt_Printf(arg->prompt, " Auth Name: %s\n", mp->peer.authname); prompt_Printf(arg->prompt, " Next SEQ: %u\n", mp->out.seq); prompt_Printf(arg->prompt, " MRRU: %u\n", mp->peer_mrru); prompt_Printf(arg->prompt, " Short Seq: %s\n", mp->peer_is12bit ? "on" : "off"); } prompt_Printf(arg->prompt, " Discriminator: %s\n", mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address, mp->peer.enddisc.len)); prompt_Printf(arg->prompt, "\nDefaults:\n"); prompt_Printf(arg->prompt, " MRRU: "); if (mp->cfg.mrru) prompt_Printf(arg->prompt, "%d (multilink enabled)\n", mp->cfg.mrru); else prompt_Printf(arg->prompt, "disabled\n"); prompt_Printf(arg->prompt, " Short Seq: %s\n", command_ShowNegval(mp->cfg.shortseq)); return 0; } const char * mp_Enddisc(u_char c, const char *address, int len) { static char result[100]; int f, header; switch (c) { case ENDDISC_NULL: sprintf(result, "Null Class"); break; case ENDDISC_LOCAL: snprintf(result, sizeof result, "Local Addr: %.*s", len, address); break; case ENDDISC_IP: if (len == 4) snprintf(result, sizeof result, "IP %s", inet_ntoa(*(const struct in_addr *)address)); else sprintf(result, "IP[%d] ???", len); break; case ENDDISC_MAC: if (len == 6) { const u_char *m = (const u_char *)address; snprintf(result, sizeof result, "MAC %02x:%02x:%02x:%02x:%02x:%02x", m[0], m[1], m[2], m[3], m[4], m[5]); } else sprintf(result, "MAC[%d] ???", len); break; case ENDDISC_MAGIC: sprintf(result, "Magic: 0x"); header = strlen(result); if (len > sizeof result - header - 1) len = sizeof result - header - 1; for (f = 0; f < len; f++) sprintf(result + header + 2 * f, "%02x", address[f]); break; case ENDDISC_PSN: snprintf(result, sizeof result, "PSN: %.*s", len, address); break; default: sprintf(result, "%d: ", (int)c); header = strlen(result); if (len > sizeof result - header - 1) len = sizeof result - header - 1; for (f = 0; f < len; f++) sprintf(result + header + 2 * f, "%02x", address[f]); break; } return result; } int mp_SetEnddisc(struct cmdargs const *arg) { struct mp *mp = &arg->bundle->ncp.mp; struct in_addr addr; if (bundle_Phase(arg->bundle) != PHASE_DEAD) { log_Printf(LogWARN, "set enddisc: Only available at phase DEAD\n"); return 1; } if (arg->argc == arg->argn) { mp->cfg.enddisc.class = 0; *mp->cfg.enddisc.address = '\0'; mp->cfg.enddisc.len = 0; } else if (arg->argc > arg->argn) { if (!strcasecmp(arg->argv[arg->argn], "label")) { mp->cfg.enddisc.class = ENDDISC_LOCAL; strcpy(mp->cfg.enddisc.address, arg->bundle->cfg.label); mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address); } else if (!strcasecmp(arg->argv[arg->argn], "ip")) { if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY) addr = arg->bundle->ncp.ipcp.cfg.my_range.ipaddr; else addr = arg->bundle->ncp.ipcp.my_ip; memcpy(mp->cfg.enddisc.address, &addr.s_addr, sizeof addr.s_addr); mp->cfg.enddisc.class = ENDDISC_IP; mp->cfg.enddisc.len = sizeof arg->bundle->ncp.ipcp.my_ip.s_addr; } else if (!strcasecmp(arg->argv[arg->argn], "mac")) { struct sockaddr_dl hwaddr; int s; if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY) addr = arg->bundle->ncp.ipcp.cfg.my_range.ipaddr; else addr = arg->bundle->ncp.ipcp.my_ip; s = ID0socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { log_Printf(LogERROR, "set enddisc: socket(): %s\n", strerror(errno)); return 2; } if (get_ether_addr(s, addr, &hwaddr)) { mp->cfg.enddisc.class = ENDDISC_MAC; memcpy(mp->cfg.enddisc.address, hwaddr.sdl_data + hwaddr.sdl_nlen, hwaddr.sdl_alen); mp->cfg.enddisc.len = hwaddr.sdl_alen; } else { log_Printf(LogWARN, "set enddisc: Can't locate MAC address for %s\n", inet_ntoa(addr)); close(s); return 4; } close(s); } else if (!strcasecmp(arg->argv[arg->argn], "magic")) { int f; randinit(); for (f = 0; f < 20; f += sizeof(long)) *(long *)(mp->cfg.enddisc.address + f) = random(); mp->cfg.enddisc.class = ENDDISC_MAGIC; mp->cfg.enddisc.len = 20; } else if (!strcasecmp(arg->argv[arg->argn], "psn")) { if (arg->argc > arg->argn+1) { mp->cfg.enddisc.class = ENDDISC_PSN; strcpy(mp->cfg.enddisc.address, arg->argv[arg->argn+1]); mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address); } else { log_Printf(LogWARN, "PSN endpoint requires additional data\n"); return 5; } } else { log_Printf(LogWARN, "%s: Unrecognised endpoint type\n", arg->argv[arg->argn]); return 6; } } return 0; } static int mpserver_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) { struct mpserver *s = descriptor2mpserver(d); if (s->send.dl != NULL) { /* We've connect()ed */ if (!link_QueueLen(&s->send.dl->physical->link) && !s->send.dl->physical->out) { /* Only send if we've transmitted all our data (i.e. the ConfigAck) */ + datalink_RemoveFromSet(s->send.dl, r, w, e); bundle_SendDatalink(s->send.dl, s->fd, &s->socket); s->send.dl = NULL; close(s->fd); s->fd = -1; - } + } else + /* Never read from a datalink that's on death row ! */ + datalink_RemoveFromSet(s->send.dl, r, NULL, NULL); } else if (r && s->fd >= 0) { if (*n < s->fd + 1) *n = s->fd + 1; FD_SET(s->fd, r); log_Printf(LogTIMER, "mp: fdset(r) %d\n", s->fd); return 1; } return 0; } static int mpserver_IsSet(struct descriptor *d, const fd_set *fdset) { struct mpserver *s = descriptor2mpserver(d); return s->fd >= 0 && FD_ISSET(s->fd, fdset); } static void mpserver_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { struct mpserver *s = descriptor2mpserver(d); struct sockaddr in; int fd, size; size = sizeof in; fd = accept(s->fd, &in, &size); if (fd < 0) { log_Printf(LogERROR, "mpserver_Read: accept(): %s\n", strerror(errno)); return; } if (in.sa_family == AF_LOCAL) bundle_ReceiveDatalink(bundle, fd, (struct sockaddr_un *)&in); close(fd); } static void mpserver_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) { /* We never want to write here ! */ log_Printf(LogERROR, "mpserver_Write: Internal error: Bad call !\n"); } void mpserver_Init(struct mpserver *s) { s->desc.type = MPSERVER_DESCRIPTOR; s->desc.next = NULL; s->desc.UpdateSet = mpserver_UpdateSet; s->desc.IsSet = mpserver_IsSet; s->desc.Read = mpserver_Read; s->desc.Write = mpserver_Write; s->send.dl = NULL; s->fd = -1; memset(&s->socket, '\0', sizeof s->socket); } int mpserver_Open(struct mpserver *s, struct peerid *peer) { int f, l; mode_t mask; if (s->fd != -1) { log_Printf(LogERROR, "Internal error ! mpserver already open\n"); mpserver_Close(s); } l = snprintf(s->socket.sun_path, sizeof s->socket.sun_path, "%sppp-%s-%02x-", _PATH_VARRUN, peer->authname, peer->enddisc.class); for (f = 0; f < peer->enddisc.len && l < sizeof s->socket.sun_path - 2; f++) { snprintf(s->socket.sun_path + l, sizeof s->socket.sun_path - l, "%02x", *(u_char *)(peer->enddisc.address+f)); l += 2; } s->socket.sun_family = AF_LOCAL; s->socket.sun_len = sizeof s->socket; s->fd = ID0socket(PF_LOCAL, SOCK_STREAM, 0); if (s->fd < 0) { log_Printf(LogERROR, "mpserver: socket: %s\n", strerror(errno)); return MPSERVER_FAILED; } setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR, (struct sockaddr *)&s->socket, sizeof s->socket); mask = umask(0177); if (ID0bind_un(s->fd, &s->socket) < 0) { if (errno != EADDRINUSE) { log_Printf(LogPHASE, "mpserver: can't create bundle socket %s (%s)\n", s->socket.sun_path, strerror(errno)); umask(mask); close(s->fd); s->fd = -1; return MPSERVER_FAILED; } umask(mask); if (ID0connect_un(s->fd, &s->socket) < 0) { log_Printf(LogPHASE, "mpserver: can't connect to bundle socket %s (%s)\n", s->socket.sun_path, strerror(errno)); if (errno == ECONNREFUSED) log_Printf(LogPHASE, " Has the previous server died badly ?\n"); close(s->fd); s->fd = -1; return MPSERVER_FAILED; } /* Donate our link to the other guy */ return MPSERVER_CONNECTED; } /* Listen for other ppp invocations that want to donate links */ if (listen(s->fd, 5) != 0) { log_Printf(LogERROR, "mpserver: Unable to listen to socket" " - BUNDLE overload?\n"); mpserver_Close(s); } return MPSERVER_LISTENING; } void mpserver_Close(struct mpserver *s) { if (s->send.dl != NULL) { bundle_SendDatalink(s->send.dl, s->fd, &s->socket); s->send.dl = NULL; close(s->fd); s->fd = -1; } else if (s->fd >= 0) { close(s->fd); if (ID0unlink(s->socket.sun_path) == -1) log_Printf(LogERROR, "%s: Failed to remove: %s\n", s->socket.sun_path, strerror(errno)); memset(&s->socket, '\0', sizeof s->socket); s->fd = -1; } } diff --git a/usr.sbin/ppp/physical.c b/usr.sbin/ppp/physical.c index 51ef2b3eb38c..4f9dff32f874 100644 --- a/usr.sbin/ppp/physical.c +++ b/usr.sbin/ppp/physical.c @@ -1,186 +1,213 @@ /* * Written by Eivind Eklund * for Yes Interactive * * Copyright (C) 1998, Yes Interactive. All rights reserved. * * Redistribution and use in any form is permitted. Redistribution in * source form should include the above copyright and this set of * conditions, because large sections american law seems to have been * created by a bunch of jerks on drugs that are now illegal, forcing * me to include this copyright-stuff instead of placing this in the * public domain. The name of of 'Yes Interactive' or 'Eivind Eklund' * 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: physical.c,v 1.1.2.30 1998/05/01 19:25:35 brian Exp $ + * $Id: physical.c,v 1.1.2.31 1998/05/10 22:20:14 brian Exp $ * */ #include #include #include #include #include #include #include #include #include "defs.h" #include "mbuf.h" #include "timer.h" #include "lqr.h" #include "hdlc.h" #include "throughput.h" #include "fsm.h" #include "lcp.h" #include "async.h" #include "ccp.h" #include "link.h" #include "descriptor.h" #include "physical.h" #include "log.h" #include "id.h" /* External calls - should possibly be moved inline */ extern int IntToSpeed(int); int physical_GetFD(struct physical *phys) { return phys->fd; } int physical_IsATTY(struct physical *phys) { return isatty(phys->fd); } int physical_IsSync(struct physical *phys) { return phys->cfg.speed == 0; } const char *physical_GetDevice(struct physical *phys) { return phys->name.full; } void physical_SetDeviceList(struct physical *p, int argc, const char *const *argv) { int f, pos; p->cfg.devlist[sizeof p->cfg.devlist - 1] = '\0'; for (f = 0, pos = 0; f < argc && pos < sizeof p->cfg.devlist - 1; f++) { if (pos) p->cfg.devlist[pos++] = ' '; strncpy(p->cfg.devlist + pos, argv[f], sizeof p->cfg.devlist - pos - 1); pos += strlen(p->cfg.devlist + pos); } } int physical_SetSpeed(struct physical *phys, int speed) { if (IntToSpeed(speed) != B0) { phys->cfg.speed = speed; return 1; } else { return 0; } } void physical_SetSync(struct physical *phys) { phys->cfg.speed = 0; } int physical_SetRtsCts(struct physical *phys, int enable) { phys->cfg.rts_cts = enable ? 1 : 0; return 1; } /* Encapsulation for a read on the FD. Avoids some exposure, and concentrates control. */ ssize_t physical_Read(struct physical *phys, void *buf, size_t nbytes) { return read(phys->fd, buf, nbytes); } ssize_t physical_Write(struct physical *phys, const void *buf, size_t nbytes) { return write(phys->fd, buf, nbytes); } int physical_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n, int force) { struct physical *p = descriptor2physical(d); int sets; sets = 0; if (p->fd >= 0) { if (r) { FD_SET(p->fd, r); log_Printf(LogTIMER, "%s: fdset(r) %d\n", p->link.name, p->fd); sets++; } if (e) { FD_SET(p->fd, e); log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, p->fd); sets++; } if (w && (force || link_QueueLen(&p->link))) { FD_SET(p->fd, w); log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, p->fd); sets++; } if (sets && *n < p->fd + 1) *n = p->fd + 1; } return sets; } +int +physical_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e) +{ + int sets; + + sets = 0; + if (p->fd >= 0) { + if (r && FD_ISSET(p->fd, r)) { + FD_CLR(p->fd, r); + log_Printf(LogTIMER, "%s: fdunset(r) %d\n", p->link.name, p->fd); + sets++; + } + if (e && FD_ISSET(p->fd, e)) { + FD_CLR(p->fd, e); + log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, p->fd); + sets++; + } + if (w && FD_ISSET(p->fd, w)) { + FD_CLR(p->fd, w); + log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, p->fd); + sets++; + } + } + + return sets; +} + int physical_IsSet(struct descriptor *d, const fd_set *fdset) { struct physical *p = descriptor2physical(d); return p->fd >= 0 && FD_ISSET(p->fd, fdset); } void physical_Login(struct physical *phys, const char *name) { if (phys->type == PHYS_DIRECT && physical_IsATTY(phys)) { if (phys->Utmp) log_Printf(LogERROR, "Oops, already logged in on %s\n", phys->name.base); else { struct utmp ut; memset(&ut, 0, sizeof ut); time(&ut.ut_time); strncpy(ut.ut_name, name, sizeof ut.ut_name); strncpy(ut.ut_line, phys->name.base, sizeof ut.ut_line - 1); ID0login(&ut); phys->Utmp = 1; } } } void physical_Logout(struct physical *phys) { if (phys->Utmp) { ID0logout(phys->name.base); phys->Utmp = 0; } } diff --git a/usr.sbin/ppp/physical.h b/usr.sbin/ppp/physical.h index 8c0bcb1f7b05..abb43729205b 100644 --- a/usr.sbin/ppp/physical.h +++ b/usr.sbin/ppp/physical.h @@ -1,98 +1,100 @@ /* * Written by Eivind Eklund * for Yes Interactive * * Copyright (C) 1998, Yes Interactive. All rights reserved. * * Redistribution and use in any form is permitted. Redistribution in * source form should include the above copyright and this set of * conditions, because large sections american law seems to have been * created by a bunch of jerks on drugs that are now illegal, forcing * me to include this copyright-stuff instead of placing this in the * public domain. The name of of 'Yes Interactive' or 'Eivind Eklund' * 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: physical.h,v 1.1.2.23 1998/05/01 19:22:23 brian Exp $ + * $Id: physical.h,v 1.1.2.24 1998/05/01 19:25:37 brian Exp $ * */ struct bundle; struct physical { struct link link; struct descriptor desc; int type; /* What sort of PHYS_* link are we ? */ struct async async; /* Our async state */ struct hdlc hdlc; /* Our hdlc state */ int fd; /* File descriptor for this device */ int mbits; /* Current DCD status */ unsigned dev_is_modem : 1; /* Is the device an actual modem? Faked for sync devices, though... (Possibly this should be dev_is_not_tcp?) XXX-ML */ struct mbuf *out; /* mbuf that suffered a short write */ int connect_count; struct datalink *dl; /* my owner */ struct { char full[40]; char *base; } name; unsigned Utmp : 1; /* Are we in utmp ? */ /* XXX-ML Most of the below is device specific, and probably do not belong in the generic physical struct. It comes from modem.c. */ struct { unsigned rts_cts : 1; /* Is rts/cts enabled? */ unsigned parity; /* What parity is enabled? (TTY flags) */ unsigned speed; /* Modem speed */ char devlist[LINE_LEN]; /* Comma-separated list of devices */ } cfg; struct termios ios; /* To be able to reset from raw mode */ struct pppTimer Timer; /* CD checks */ }; #define field2phys(fp, name) \ ((struct physical *)((char *)fp - (int)(&((struct physical *)0)->name))) #define link2physical(l) \ ((l)->type == PHYSICAL_LINK ? field2phys(l, link) : NULL) #define descriptor2physical(d) \ ((d)->type == PHYSICAL_DESCRIPTOR ? field2phys(d, desc) : NULL) extern int physical_GetFD(struct physical *); extern int physical_IsATTY(struct physical *); extern int physical_IsSync(struct physical *); extern const char *physical_GetDevice(struct physical *); extern void physical_SetDeviceList(struct physical *, int, const char *const *); extern int physical_SetSpeed(struct physical *, int); /* * XXX-ML I'm not certain this is the right way to handle this, but we * can solve that later. */ extern void physical_SetSync(struct physical *); /* * Can this be set? (Might not be a relevant attribute for this * device, for instance) */ extern int physical_SetRtsCts(struct physical *, int); extern ssize_t physical_Read(struct physical *, void *, size_t); extern ssize_t physical_Write(struct physical *, const void *, size_t); extern int physical_UpdateSet(struct descriptor *, fd_set *, fd_set *, fd_set *, int *, int); extern int physical_IsSet(struct descriptor *, const fd_set *); extern void physical_Login(struct physical *, const char *); extern void physical_Logout(struct physical *); +extern int physical_RemoveFromSet(struct physical *, fd_set *, fd_set *, + fd_set *);