/* * link.cc - Self explanitory * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: link.cc,v 1.19 2004/10/20 05:09:54 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "link.h" #include "config.h" #include "d_aps_if.h" #include "aps_if.h" #include "babd.h" #include "selectops.h" #define MAX_CHANS 256 extern void killIfaces(void); static fd_set rfds; static fd_set wfds; channel_t *disc_chans[256000]; int n_disc_chans; #define NumChan 2 // // Global List of all links // CLink *linkListHead = NULL; CLink **linkListTailp = &linkListHead; extern CInterface *ifListHead; extern int Hangup(char *, u32); extern int SetPolicy(u8, char *, policy_t *); extern void DialReport(DialResponse_t *); extern unsigned int GetDevClassId(char *); extern policy_t g_policy; static char *phaseLookup[] = { "DEAD", "DIALING", "ESTABLISH", "AUTHENTICATE", "TERMINATE", "NETWORK" }; static char *chanStates[] = { "idle", "dialing", "ringing", "connecting", "connected", "disconnecting", "disconnected", "stalled", "unavailable" }; void write_link_options(int fd, CLink *link) { int l; char tmp[1024]; l = snprintf(tmp, sizeof(tmp), "[-1] 0: %-10s %-4s %-10x %-6d %-6s %-6s %-6x %-6s %-6s %-6d\n", link->m_channel->device_name, "Tx", link->m_rpolicy.Get(P_MAGIC), link->m_rpolicy.Get(P_MRRU), link->m_rpolicy.Get(P_PFC) ? "yes" : "no", link->m_rpolicy.Get(P_ACFC) ? "yes" : "no", link->m_rpolicy.Get(P_AUTH), link->m_rpolicy.Get(P_ANSWER) ? "yes" : "no", link->m_rpolicy.Get(P_SSN) ? "yes" : "no", link->m_rpolicy.Get(P_ECHO) ); l += snprintf(&tmp[l], sizeof(tmp), "[-1] 0: %-10s %-4s %-10x %-6d %-6s %-6s %-6x %-6s %-6s %-6d\n\n", link->m_channel->device_name, "Rx", link->m_lpolicy.Get(P_MAGIC), link->m_lpolicy.Get(P_MRRU), link->m_lpolicy.Get(P_PFC) ? "yes" : "no", link->m_lpolicy.Get(P_ACFC) ? "yes" : "no", link->m_lpolicy.Get(P_AUTH), link->m_lpolicy.Get(P_ANSWER) ? "yes" : "no", link->m_lpolicy.Get(P_SSN) ? "yes" : "no", link->m_lpolicy.Get(P_ECHO) ); for (char *s=tmp; l; ) { int i = write(fd, s, l); if (i <= 0) break; l -= i; s += i; } } void write_link_state(int fd, CLink *link) { extern char *iptostr(unsigned); const char *st = link->m_channel->state > CS_UNAVAIL ? "unknown" : chanStates[link->m_channel->state]; char loc_ip[32]="none", rem_ip[32]="none"; char tmp[1024]; int l; if (link->m_interface) { if (link->m_interface && link->m_interface->have_ip) { strcpy(loc_ip, iptostr(link->m_interface->m_lcfg.loc_ip)); strcpy(rem_ip, iptostr(link->m_interface->m_lcfg.rem_ip)); } } l = snprintf(tmp, sizeof(tmp), "[-1] 0: %-10s %-15s %-15s %-13s %-7s %-6s %-8s %-7s\n", (link->m_wereAcked || link->m_peerAcked) && link->ifOptions.user[0] ? link->ifOptions.user : "none", loc_ip, rem_ip, st, link->m_channel->dev_class, link->m_interface ? link->m_interface->m_name : "none", link->m_interface ? (link->m_interface->is_static ? link->m_interface->m_site: "n/a") : "n/a", link->m_channel->device_name ); for (char *s=tmp; l; ) { int i = write(fd, s, l); if (i <= 0) break; l -= i; s += i; } } /* * Construct a CLink from a channel (called via RegisterChannel()) */ CLink::CLink(channel_t *ch) { disc_chans_index = -1; m_hard_mtu = m_hard_mru = 1500; m_channel = ch; memset(&m_policy, 0, sizeof(m_policy)); memset(&m_npolicy, 0, sizeof(m_npolicy)); memset(&m_rpolicy, 0, sizeof(m_rpolicy)); memset(&m_lpolicy, 0, sizeof(m_lpolicy)); m_lcpProto.m_parent = this; m_papProto.m_parent = this; m_chapProto.m_parent = this; m_phase = PHASE_DEAD; m_interface = NULL; memset(&ifOptions, 0, sizeof(ifOptions)); // Initialize the bundle to 1 m_downPending = 0; m_interface = NULL; m_wereAcked = 0; m_peerAcked = 0; m_phase = PHASE_DEAD; m_loop_count = 0; m_ident = 0; m_nextBundled = NULL; m_next = NULL; m_ldisc = 0; m_lastStatus = 0; m_reason = NULL; // Add us to the global list of links m_prevp = linkListTailp; *m_prevp = this; m_next = NULL; linkListTailp = &m_next; memcpy(&m_policy, &g_policy, sizeof(policy_t)); } CLink::~CLink() { DebugEnter("CLink::~CLink()"); LinkCancelGetConfig(); if (-1 != disc_chans_index) { disc_chans[disc_chans_index] = NULL; disc_chans_index = -1; } // Make sure we're closed Close(-2, "interface deleted"); // Remove us from the list of links *m_prevp = m_next; if (m_next) m_next->m_prevp = m_prevp; if (linkListTailp == &m_next) linkListTailp = m_prevp; m_next = NULL; DebugExit(); } void CLink::LinkCancelGetConfig(void) { if (ifOptions.cancel_obj) { ifOptions.cancel_obj->CancelGetConfig(); ifOptions.cancel_obj = NULL; } } /* * Receive a packet on the link */ void CLink::Input(u8 *data, int len) { u16 proto = 0; u16 acf = 0; CPppPacket *packet; DebugEnter("CLink::Input()"); if(len > 4095 || len < 1) { Log(LF_INFO|LF_RX, "%s: Discarding invalid packet length = %d", m_channel->device_name, len); DebugVoidReturn; } Log(LF_INFO|LF_TX, "%s: RX packet length = %d", m_channel->device_name, len); LogPacket(LF_DEBUG|LF_PROTO, data, len); packets_in++; octets_in += len; packet = new CPppPacket(this); if (!packet) { Log(LF_ERROR|LF_RX, "%s: Error allocating packet!", m_channel->device_name); DebugVoidReturn; } packet->Put(data, len); /* * Make sure the link isn't dead */ if(m_phase < PHASE_ESTABLISH) { Log(LF_DEBUG|LF_RX, "%s: Link input with dead link, dropping", m_channel->device_name); delete packet; DebugVoidReturn; } /* * Remove any framing */ acf = packet->Pull16(); if(acf != 0xFF03) packet->Push16(acf); /* * Get the protocol ID */ proto = (u16) packet->Pull8(); if(!(proto & 0x0001)) { // Read the next byte proto = proto << 8; proto |= packet->Pull8(); } Log(LF_DEBUG|LF_RX, "%s: Rcv'd packet w/protocol %#x", m_channel->device_name, proto); if (PPP_PROTO_MP == proto) { if (m_lpolicy.Get(P_SSN)) packet->Pull16(); else packet->Pull32(); /* This shouldn't happen, but is included for robustness. */ acf = packet->Pull16(); if(acf != 0xFF03) packet->Push16(acf); proto = (u16) packet->Pull8(); if (!(proto & 0x0001)) { // Read the next byte proto = proto << 8; proto |= packet->Pull8(); } } /* * Network frames go to the interface */ if(proto < 0xC000) { if(m_phase < PHASE_NETWORK) { Log(LF_INFO|LF_RX, "%s: Recv'd Network packet out of phase", m_channel->device_name); delete packet; DebugVoidReturn; } packet->Push16(proto); m_interface->Input(packet); DebugVoidReturn; } /* * Process Link packets */ switch(proto) { case PPP_PROTO_LCP: if(m_phase >= PHASE_ESTABLISH) m_lcpProto.Input(packet); else { Log(LF_INFO | LF_RX, "%s: Rx LCP packet out of phase. Phase = %s", m_channel->device_name, phaseLookup[m_phase]); } break; case PPP_PROTO_PAP: if(m_phase >= PHASE_AUTHENTICATE) m_papProto.Input(packet); else { Log(LF_INFO | LF_RX, "%s: Rx PAP packet out of phase. Phase = %s", m_channel->device_name, phaseLookup[m_phase]); } break; case PPP_PROTO_CHAP: if(m_phase >= PHASE_AUTHENTICATE) m_chapProto.Input(packet); else { Log(LF_INFO | LF_RX, "%s: Rx CHAP packet out of phase. Phase = %s", m_channel->device_name, phaseLookup[m_phase]); } break; default: Log(LF_INFO | LF_RX, "%s: Rx unknown protocol type %#x", m_channel->device_name, proto); if(m_phase > PHASE_ESTABLISH) m_lcpProto.RejectProtocol(packet); break; } delete packet; DebugVoidReturn; } int CLink::JoinBundle(int ndev_id) { if (0 != ch_ioctl(BIOCJOINBUNDLE, ndev_id)) { perror("AddLink: ioctl(BIOCJOINBUNDLE)"); return -1; } return 0; } int CLink::ch_ioctl(unsigned int cmd, unsigned long arg) { return ioctl(m_channel->device_id, cmd, arg); } int CLink::HardOutput(CPppPacket *packet) { int ret; DebugEnter("CLink::HardOutput()"); if (CS_CONNECTED != m_channel->state) { if (CS_DISCONNECTING != m_channel->state) Log(LF_ALERT|LF_TX, "%s: output on channel in state %d", m_channel->device_name, m_channel->state); DebugReturn(-EIO); } ret = write(m_channel->device_id, packet->m_start, packet->GetLength()); if ((int)packet->GetLength() == ret) DebugReturn(0); if (-1 == ret && EPIPE == errno) m_channel->link->Hangup(); else if (-1 == ret) perror("bab->output:"); else Log(LF_ALERT|LF_TX, "%s: bab->output: write returned %d", m_channel->device_name, ret); DebugReturn(-EIO); } /* * Send a packet on the link, converting it to an skb */ int CLink::Output(CPppPacket *packet) { int ret; u16 pid; DebugEnter("CLink::Output()"); /* * Compress the protocol ID if required */ pid = packet->Pull16(); if (pid <= 0xFF && m_rpolicy.Get(P_PFC)) { packet->Push8((u8)pid); Log(LF_DEBUG|LF_TX, "%s: Compressed protocol ID", m_channel->device_name); } else packet->Push16(pid); /* * Add the ACF if required */ if (!m_rpolicy.Get(P_ACFC) || PPP_PROTO_LCP == pid || m_phase <= PHASE_ESTABLISH) { packet->Push16(0xFF03); Log(LF_DEBUG|LF_TX, "%s: Added ACF", m_channel->device_name); } ret = packet->GetLength(); Log(LF_INFO|LF_TX, "%s: TX packet length %d", m_channel->device_name, ret); LogPacket(LF_INFO|LF_TX, packet->m_start, ret); packets_out++; octets_out += packet->GetLength(); DebugReturn(HardOutput(packet)); } void CLink::Hangup() { if (m_phase > PHASE_DISCONNECTING) m_phase = PHASE_DISCONNECTING; if (m_reason) { free(m_reason); m_reason = NULL; } if (CS_IDLE == m_channel->state) { // if (ch_ioctl(BIOCHANGUP, 0)) // Log(LF_ERROR|LF_CALL, "%s: hangup failed: %s", m_channel->device_name, strerror(errno)); // reopen(1); HardHangup(); } else if (CS_DISCONNECTING != m_channel->state) { m_channel->state = CS_DISCONNECTING; disc_chans_index = n_disc_chans; disc_chans[n_disc_chans++] = m_channel; } } int CLink::Connect(const char *number, u32 callType) { int ret; m_phase = PHASE_DIALING; Log(LF_DEBUG|LF_IPC, "connect(%s '%s' %u)", m_channel->device_name, number, callType); if (CS_IDLE != m_channel->state) return -EBUSY; ret = HardConnect(number, callType); if (!ret) { if (0 != ch_ioctl(BIOC_SETLCFL, BF_PPP)) Log(LF_ERROR|LF_CALL, "ioctl(%s, BIOC_SETLCFL, BF_PPP): %s", m_channel->device_name, strerror(errno)); if (0 != ch_ioctl(BIOC_SETRCFL, BF_PPP)) Log(LF_ERROR|LF_CALL, "ioctl(%s, BIOC_SETRCFL, BF_PPP): %s", m_channel->device_name, strerror(errno)); } return ret; } int CLink::HardConnect(const char *number, u32 callType) { int ret; if (0 != (ret = ch_ioctl(BIOC_SETCALLTYPE, callType))) { perror("ioctl: BIOC_SETCALLTYPE"); goto make_idle; } m_channel->state = CS_DIALING; reopen(0); ret = ch_ioctl(BIOCDIAL, (long)(void *)number); if (-1 == ret) Log(LF_ERROR|LF_CALL, "ioctl(%s, BIOCDIAL): %s", m_channel->device_name, strerror(errno)); if (!ret) this->SelectSetEvents(m_channel->device_id, SEL_WRITE); else { make_idle: m_channel->state = CS_IDLE; reopen(1); } return ret; } void CLink::ConnectComplete(int status) { DialResponse_t dr; DebugEnter("CLink::ConnectComplete()"); dr.call = ifOptions.call; m_lastStatus = status; dr.status = status; if (!status) { strcpy(dr.msg, "Proceeding..."); m_phase = PHASE_CONNECTED; } else { char *str = NULL; if (PHASE_DISCONNECTING == m_phase) str = "Local hangup"; else if (status > 0) str = strcause(status & 0x7f); if (!str) str = "Failed"; strcpy(dr.msg, str); m_phase = PHASE_DEAD; } //printk("<7>ConnectComplete: %03x, %d\n", status, dr.call); DialReport(&dr); #if 1 //printk("<7>CLink::ConnectComplete(%d), ifc=%p\n", status, m_interface); if (status && m_interface) { m_interface->DropLink(this, status, m_reason); if (m_reason) { free(m_reason); m_reason = NULL; } } #endif DebugVoidReturn; } /* * Open a link */ void CLink::Open() { DebugEnter("CLink::Open()"); // If the working policy needs to be set // do it now. policy_t tmppol = m_npolicy; m_npolicy.Set(m_policy); m_npolicy.Merge(tmppol); // // Now clear the m_npolicy.valid_mask so that // on the next call we revert to the defaults // if nothing gets set. // m_npolicy.Set(P_LDISC, m_ldisc); // Set the working policy to specification defaults m_lpolicy.Clear(); m_rpolicy.Clear(); m_rpolicy.Set(P_MRU, m_hard_mru); if (m_hard_mru < LCP_OPT_MRU_DEFAULT) { m_lpolicy.Set(P_MRU, m_hard_mru); m_npolicy.Set(P_MRU, m_hard_mru); } if (m_hard_mtu < LCP_OPT_MRU_DEFAULT) m_policy.Set(P_MRU, m_hard_mtu); // // Zeroing ifOptions here could be trouble // memset(&ifOptions, 0, sizeof(OptionSet_t)); // Open the protocols m_lcpProto.Open(); m_papProto.Open(); m_chapProto.Open(); DebugVoidReturn; } /* * Close a link */ void CLink::Close(int status, const char *reason) { DebugEnter("CLink::Close()"); if (m_reason) free(m_reason); m_reason = strdup(reason); if (reason) { DialResponse_t dr(ifOptions.is_valid ? ifOptions.call : 0); dr.status = status; strcpy(dr.msg, reason); DialReport(&dr); } // Close the protocols m_papProto.Close(reason); m_chapProto.Close(reason); m_lcpProto.Close(reason); // Invalidate any configuration m_npolicy.Clear(); ifOptions.is_valid = 0; DebugVoidReturn; } extern void LinkGotConfig(void *obj, OptionSet_t *opt) { CLink *link = (CLink *)obj; if (opt->is_valid && &link->ifOptions != opt) memcpy(&link->ifOptions, opt, sizeof(link->ifOptions)); if (link->ifOptions.user[0] == '\0') link->m_npolicy.Set(P_AUTH, 0); link->m_lcpProto.Up(); } /* * Bring up a link */ void CLink::Up() { DebugEnter("CLink::Up()"); //SetACCM(m_channel, 0xffffffff, 0xffffffff); m_phase = PHASE_ESTABLISH; if (m_channel->no_auth) { strcpy(ifOptions.site, m_channel->device_name); m_npolicy.Set(P_AUTH, 0); this->GetConfig(LinkGotConfig, this, &ifOptions); DebugVoidReturn; } if (ifOptions.site[0] != '\0') m_npolicy.Set(P_AUTH, 0); m_lcpProto.Up(); DebugVoidReturn; } /* * Bring down a link */ void CLink::Down() { DebugEnter("CLink::Down()"); LinkCancelGetConfig(); m_phase = PHASE_DEAD; // Bring down the protocols m_papProto.Down(); m_chapProto.Down(); m_lcpProto.Down(); if (m_interface) { m_interface->DropLink(this, m_lastStatus, m_reason); if (m_reason) { free(m_reason); m_reason = NULL; } } // // Again, see Link::Open. Zeroing ifOptions // here could be trouble // memset(&ifOptions, 0, sizeof(OptionSet_t)); DebugVoidReturn; } void CLink::Ready() { DialResponse_t dr; DebugEnter("CLink::Ready()"); if (m_phase == PHASE_NETWORK) return; dr.call = ifOptions.call; m_phase = PHASE_NETWORK; //printk("rm link dialing %lu\n", ifOptions.call); RemoveLinkDialing(ifOptions.call); // // look for a multilink binding // if (m_rpolicy.Get(P_MRRU) == 0) goto new_iface; Log(LF_DEBUG|LF_IPC, "Looking for Multilink bundle to join"); for(CInterface *i = ifListHead ; i != NULL ; i = i->next) { if (i->nr_links && strcmp(i->m_lcfg.user, ifOptions.user)) { Log(LF_DEBUG|LF_IPC, "Auth Mismatch %s != %s", i->m_lcfg.user, ifOptions.user); continue; } // // Authentication match // if (!(!i->nr_links || (i->m_rpolicy.Get(P_EPD_CLASS) == m_rpolicy.Get(P_EPD_CLASS)) && (!memcmp(i->m_rpolicy.epd_addr, m_rpolicy.epd_addr, m_rpolicy.Get(P_EPD_LENGTH))))) { Log(LF_DEBUG|LF_IPC, "EPD Mismatch"); continue; } // // Auth + EPD, make sure the links are compatible and // add to bundle // if (!i->nr_links || ((i->links->m_rpolicy.Get(P_SSN) == m_rpolicy.Get(P_SSN)) && (i->links->m_rpolicy.Get(P_MRRU) == m_rpolicy.Get(P_MRRU)))) { if (!i->nr_links) i->m_rpolicy.Set(m_rpolicy); m_interface = i; dr.status = m_interface->have_ip ? -1 : 0; snprintf(dr.msg, sizeof(dr.msg), "Connected on %s with %s as %s", m_channel->device_name, m_interface->m_name, ifOptions.user); DialReport(&dr); m_interface->AddLink(this); Log(LF_DEBUG|LF_IPC, "MP Bundling complete to %s",i->m_name); DebugVoidReturn; } Log(LF_DEBUG|LF_IPC, "SSN/MRRU Mismatch"); } Log(LF_DEBUG|LF_IPC, "No more interfaces to check"); new_iface: if (m_interface) { Log(LF_ERROR|LF_IPC, "Unable to add link on %s to static interface %s", m_channel->device_name, m_interface->m_name); m_interface = NULL; Close(-2, "Static interface bundling failed"); DebugVoidReturn; } // // couldn't bind to a multilink bundle create a new // interface // Log(LF_DEBUG|LF_IPC, "Creating new interface"); m_interface = new CInterface(this); if(m_interface == NULL) { Log(LF_ERROR|LF_IPC, "Error allocating memory"); Close(-2, "Error allocating memory"); DebugVoidReturn; } dr.status = 0; snprintf(dr.msg, sizeof(dr.msg), "Connected on %s with %s as %s", m_channel->device_name, m_interface->m_name, ifOptions.user); DialReport(&dr); DebugVoidReturn; } int Hangup(char *port, Call *call) { CLink *curLink = linkListHead; DebugEnter("Hangup()"); if (port[0] != '\0') { Log(LF_DEBUG|LF_CALL, "Matching on port %s", port); while((curLink != NULL) && (strcmp(curLink->m_channel->device_name, port) != 0)) { Log(LF_DEBUG|LF_CALL, "Failed on port %s", curLink->ifOptions.port); curLink = curLink->m_next; } } else if (call != 0) { Log(LF_DEBUG|LF_CALL, "Matching on call %p", call); while ((curLink != NULL) && (curLink->ifOptions.call != call)) curLink = curLink->m_next; } else { Log(LF_DEBUG|LF_CALL, "Nothing to match on"); DebugReturn(-1); } if(curLink != NULL) { // // Found the link to hangup // Log(LF_DEBUG|LF_CALL, "Hanging up channel %s", curLink->m_channel->device_name); curLink->Hangup(); } else { // // Didn't find the link to hangup // Log(LF_DEBUG|LF_CALL, "Unable to find link"); DebugReturn(-1); } DebugReturn(0); } int SetPolicy(u8 scope, char *ident, policy_t *pol) { CLink *pLnk; u32 dev_id = 0; int touched = 0; DebugEnter("SetPolicy()"); /* Set policy globally */ for (pLnk = linkListHead; pLnk != NULL; pLnk = pLnk->m_next) { if ((scope == PSCOPE_CLASS) && (dev_id != pLnk->m_channel->devclass_id)) continue; if (ident && strcmp(pLnk->m_channel->device_name, ident)) continue; Log(LF_DEBUG|LF_IPC, "Set policy on %s", pLnk->m_channel->device_name); touched++; if (scope == PSCOPE_SESSION) pLnk->m_npolicy.Merge(*pol); else pLnk->m_policy.Merge(*pol); } DebugReturn(touched ? touched : -1); } void CLink::chclose(void) { if (~0U != m_channel->device_id) { Log(LF_DEBUG|LF_CALL, "chclose: %d", m_channel->device_id); SelectSetEvents(m_channel->device_id, SEL_NONE); close(m_channel->device_id); m_channel->device_id = ~0U; } } int CLink::reopen(int answer) { int fd; int i; Log(LF_DEBUG|LF_CALL, "reopen(%s)", m_channel->device_name); chclose(); fd = open(m_channel->file_name, O_RDWR | O_NONBLOCK); m_channel->device_id = fd; if (-1 == fd) { Log(LF_DEBUG|LF_CALL, "Unabled to open device %s: %s", m_channel->device_name, strerror(errno)); return 1; } ch_ioctl(BIOCLEAVEBUNDLE, 0); Log(LF_DEBUG | LF_CALL, "checking mru"); if (0 == ch_ioctl(BIOC_GET_MAX_MRU, (long)(void *)&i)) { m_hard_mtu = m_hard_mru = i; Log(LF_DEBUG | LF_CALL, "m_hard_mru: %d", i); } else Log(LF_DEBUG | LF_CALL, "m_hard_mru: FAILED(%s)", strerror(errno)); if (answer && -1 == ch_ioctl(BIOCANSWER, ~0)) { Log(LF_DEBUG|LF_CALL, "ioctl(%s, BIOCANSWER): %s", m_channel->device_name, strerror(errno)); close(fd); return 1; } //setup_select(m_channel->device_id, m_channel, answer ? NULL : bab_do_input, answer ? bab_do_write : NULL); SelectSetEvents(m_channel->device_id, answer ? SEL_WRITE : SEL_READ); return 0; } void CLink::SelectEvent(int fd, SelectEventType event) { if (event & SEL_WRITE) { int cause = 0x100; if (ch_ioctl(BIOC_GETCAUSECODE, (long)(void *)&cause)) cause = 0x200; if (cause) { Log(LF_DEBUG|LF_CALL, "Cause: %x", cause); m_channel->state = CS_IDLE; reopen(1); ConnectComplete(cause); Down(); return; } if (CS_IDLE == m_channel->state) { if (0 != ch_ioctl(BIOC_SETLCFL, BF_PPP)) Log(LF_DEBUG|LF_CALL, "ioctl(%s, BIOC_SETLCFL, BF_PPP): %s", m_channel->device_name, strerror(errno)); if (0 != ch_ioctl(BIOC_SETRCFL, BF_PPP)) Log(LF_DEBUG|LF_CALL, "ioctl(%s, BIOC_SETRCFL, BF_PPP): %s", m_channel->device_name, strerror(errno)); SelectSetEvents(m_channel->device_id, SEL_READ); m_channel->state = CS_CONNECTED; m_channel->link->Open(); m_channel->link->Up(); } else if (CS_DIALING == m_channel->state) { if (0 != ch_ioctl(BIOC_SETLCFL, BF_PPP)) Log(LF_ERROR|LF_CALL, "ioctl(%s, BIOC_SETLCFL, BF_PPP): %s", m_channel->device_name, strerror(errno)); if (0 != ch_ioctl(BIOC_SETRCFL, BF_PPP)) Log(LF_ERROR|LF_CALL, "ioctl(%s, BIOC_SETRCFL, BF_PPP): %s", m_channel->device_name, strerror(errno)); SelectSetEvents(m_channel->device_id, SEL_READ); m_channel->state = CS_CONNECTED; Log(LF_DEBUG|LF_CALL, "We're in the dialing state."); ConnectComplete(0); Up(); } else Log(LF_ERROR|LF_CALL, "bab_do_write: wrong state %d!", m_channel->state); } if (event & SEL_READ) { u8 buf[4096]; int len; if ((len=read(m_channel->device_id, buf, sizeof(buf))) > 0) { if (CS_CONNECTED == m_channel->state) { Input(buf, len); } else Log(LF_ERROR|LF_RX, "%s: bab_do_input: read %d byte packet, state = %d", m_channel->device_name, len, m_channel->state); } if (-1 == len && EAGAIN != errno) Log(LF_ERROR|LF_RX, "bab_do_input(%s): read: %s", m_channel->device_name, strerror(errno)); if (!len) { reopen(1); if (m_channel->state != CS_IDLE) Hangup(); } } } void CLink::HardHangup(void) { int cause = 0x300; ch_ioctl(BIOC_GETCAUSECODE, (long)(void *)&cause); if (!cause) cause = 0x400; if (ch_ioctl(BIOCHANGUP, 0)) Log(LF_ERROR|LF_CALL, "%s: hangup failed: %s", m_channel->device_name, strerror(errno)); reopen(1); Log(LF_DEBUG|LF_CALL, "We're in do_hangups."); ConnectComplete(cause); Down(); m_channel->state = CS_IDLE; } void CLink::GetConfig(void (*cbf)(void *, OptionSet_t *), void *obj, OptionSet_t *options) { LinkCancelGetConfig(); /* cancel any outstanding req */ babd_GetConfig(cbf, obj, options); } static void do_hangups(void) { while (n_disc_chans > 0) { channel_t *ch = disc_chans[--n_disc_chans]; if (ch) { disc_chans[ch->link->disc_chans_index] = NULL; ch->link->disc_chans_index = -1; ch->link->HardHangup(); } } } channel_t *findchan(char *name) { CLink *link = linkListHead; while (link) { if (!strcmp(link->m_channel->device_name, name)) return link->m_channel; link = link->m_next; } return NULL; } int addchan(char *dev, char *name, char *devclass, int no_auth) { channel_t *ch; if (findchan(name)) return 1; ch = new channel_t; if (!ch) { perror("malloc"); return 1; } memset(ch, 0, sizeof(*ch)); ch->device_id = ~0U; ch->unit = 0; ch->dev_class = strdup(devclass); ch->device_name = strdup(name); ch->file_name = strdup(dev); ch->no_auth = no_auth; if (RegisterChannel(ch)) goto failed; return 0; failed: delete ch; return 1; } void delchan(channel_t *ch) { UnregisterChannel(ch); ch->link->chclose(); delete ch; } static int bab_maxfd = -1; inline void SelectEventHandler::touch_fd(int fd) { if (-1 == fd_used) fd_used = fd; else if (fd_used != fd) scan_all = 1; if (fd > bab_maxfd) bab_maxfd = fd; } void bab_poll(void) { int maxfd = 0, i; struct timeval tv; killIfaces(); do_hangups(); killIfaces(); FD_ZERO(&rfds); FD_ZERO(&wfds); again: for (i=0; i<=bab_maxfd; i++) { if (selectObjs[0][i]) FD_SET(i, &rfds); if (selectObjs[1][i]) FD_SET(i, &wfds); } maxfd = i; tv = timer_getdelay(); maxfd = select(maxfd, &rfds, &wfds, NULL, &tv); if (maxfd < 0) { if (EINTR == errno) goto again; perror("select"); } for (i=0; i<=bab_maxfd; i++) { if (FD_ISSET(i, &rfds) || FD_ISSET(i, &wfds)) maxfd--; if (FD_ISSET(i, &rfds) && selectObjs[0][i]) selectObjs[0][i]->SelectEvent(i, SEL_READ); if (FD_ISSET(i, &wfds) && selectObjs[1][i]) selectObjs[1][i]->SelectEvent(i, SEL_WRITE); } killIfaces(); do_hangups(); killIfaces(); timer_run(); killIfaces(); do_hangups(); } SelectEventHandler::SelectEventHandler() { scan_all = 0; fd_used = -1; } SelectEventHandler::~SelectEventHandler() { int i; if (scan_all) { for (i=0; i bab_maxfd) { touch_fd(fd); } else if (fd == bab_maxfd && !selectObjs[0][fd] && !selectObjs[1][fd]) { int i; for (i=bab_maxfd-1; i>-1; i--) if (selectObjs[0][i] || selectObjs[1][i]) break; bab_maxfd = i; } } void SelectEventHandler::SelectRemoveEvent(int fd, SelectEventType event) { if (event & SEL_READ) selectObjs[0][fd] = 0; if (event & SEL_WRITE) selectObjs[1][fd] = 0; if (fd == bab_maxfd && !selectObjs[0][fd] && !selectObjs[1][fd]) { int i; for (i=bab_maxfd-1; i>-1; i--) if (selectObjs[0][i] || selectObjs[1][i]) break; bab_maxfd = i; } }