/* * 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 #include "link.h" #include "config.h" #include "d_aps_if.h" #include "aps_if.h" #include "babd.h" #include "selectops.h" #include "l2tpd.h" #include "l2tp_tunnel.h" #define MAX_CHANS 256 channel_t *disc_chans[256000]; int n_disc_chans; int want_mlcp = 0; #define NumChan 2 // // Global List of all links // CLink *linkListHead = NULL; CLink **linkListTailp = &linkListHead; extern CInterface *ifListHead; extern int Hangup(char *, u32); extern void DialReport(DialResponse_t *); extern unsigned int GetDevClassId(char *); extern policy_t g_policy; static const char *phaseLookup[] = { "DEAD", "DISCONNECTING", "DIALING", "CONNECTED", "ESTABLISH", "AUTHENTICATE", "MLCP", "TERMINATE", "NETWORK" }; static const 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 %-9d %-9d %-9d\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), link->m_rpolicy.Get(P_ECHO_INTERVAL), link->m_rpolicy.Get(P_ECHO_LEN) ); l += snprintf(&tmp[l], sizeof(tmp), "[-1] 0: %-10s %-4s %-10x %-6d %-6s %-6s %-6x %-6s %-6s %-9d %-9d %-9d\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), link->m_lpolicy.Get(P_ECHO_INTERVAL), link->m_lpolicy.Get(P_ECHO_LEN) ); for (char *s=tmp; l; ) { int i = write(fd, s, l); if (i <= 0) break; l -= i; s += i; } } const char *CLink::get_multihop_name(void) { if (multihop_link) return multihop_link->get_name(); return NULL; } const char *CLink::get_user(void) { if ((m_wereAcked || m_peerAcked || multihop_link) && ifOptions.user[0]) return ifOptions.user; return NULL; } long CLink::get_connected_time(void) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now)) *(char *)0 = 0; long secs = now.tv_sec - m_connect_time.tv_sec; return secs; } void write_link_state(int fd, CLink *link, int verbose) { 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]; char tmp2[24]; const char *connected; const char *user; int l; if (link->m_channel->state == CS_CONNECTED) { long secs = link->get_connected_time(); long days, hours, minutes, weeks; weeks = secs / (86400 * 7); secs -= weeks * 86400 * 7; days = secs / 86400; secs -= days * 86400; hours = secs / 3600; secs -= hours * 3600; minutes = secs / 60; secs -= minutes * 60; if (weeks) sprintf(tmp2, "%3ldw%02ldd%02ldh%02ldm", weeks, days, hours, minutes); else sprintf(tmp2, "%3ldd%02ldh%02ldm%02lds", days, hours, minutes, secs); connected = tmp2; } else connected = "n/a"; 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)); } } user = link->get_user(); if (!user) user = "none"; unsigned long idle_ms = 0; link->ch_ioctl(BIOC_GET_IDLE_MS, (unsigned long)&idle_ms); l = snprintf(tmp, sizeof(tmp), "[-1] 0: %-15s %-15s %*s%*s%-13s %*s%-13s %16s %3lus %*s%s\n", loc_ip, rem_ip, verbose ? -14 : 0, verbose ? st : "", verbose ? -9 : 0, verbose ? link->get_class() : "", link->m_interface ? link->m_interface->GetName() : (link->get_multihop_name() ? link->get_multihop_name() : "none"), verbose ? -25 : 0, verbose ? link->ifOptions.phone : "", link->m_channel->device_name, connected, idle_ms / 1000, verbose ? -9 : 0, (verbose && link->ifOptions.qos_profile_name) ? link->ifOptions.qos_profile_name : "", user ); 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; m_fd = -1; multihop_link = NULL; m_iccn_pkt = NULL; 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_mlcpProto.m_parent = this; m_phase = PHASE_DEAD; m_interface = NULL; ifOptions.Clear(); // Initialize the bundle to 1 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; m_nrLCPRestartForMRRU = 0; octets_in = 0; octets_out = 0; packets_in = 0; packets_out = 0; // Add us to the global list of links m_prevp = linkListTailp; *m_prevp = this; m_next = NULL; linkListTailp = &m_next; m_policy.Set(g_policy); m_interim_update_timer.m_link = this; } void CLink::remove_disc_chans(void) { if (-1 != disc_chans_index) { disc_chans[disc_chans_index] = NULL; disc_chans_index = -1; } } void CLink::remove_multihop_link(void) { if (!multihop_link) return; /* this ends up setting multihop_link = NULL */ multihop_link->multihop_unlink(); } void CLink::multihop_unlink(void) { CLink *other = multihop_link; if (!other) return; if (other->multihop_link != this) fprintf(stderr, "CLink::multihop_unlink: BUG - multihop clink\n"); other->multihop_link = NULL; multihop_link = NULL; Hangup(); } CLink::~CLink() { DebugEnter("CLink::~CLink()"); if (m_interface) m_interface->DropLink(this, 0, "link deleted"); LinkCancelGetConfig(); remove_multihop_link(); // 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; if (ifOptions.call) { Call *call = ifOptions.call; ifOptions.SetCall(NULL); delete call; } chclose(); remove_disc_chans(); if (m_reason) free(m_reason); if (m_iccn_pkt) *(char *)0 = 0; DebugExit(); } void CLink::LinkCancelGetConfig(void) { if (ifOptions.cancel_obj) { ifOptions.cancel_obj->CancelGetConfig(); ifOptions.cancel_obj = NULL; } } /* GotConfigFromAuth() * This method is called when an authentication protocol has received * a response to an authentication triggered configuration request. * This is primarily useful for configuration options driven by RADIUS * responses keyed by the user being authenticated. * * This method should return 0 if everything is okay and the session * should continue past authentication. If an option has changed that * requires restarting LCP, this method should return non-zero and * Down() the authentication protocol. */ int CLink::GotConfigFromAuth(OptionSet_t *os, CPapProtocol *auth_proto) { // FIXME: handle options other than MRRU changes, like echo // request timeouts and intervals. int initial = -1024; int last_sent = -1024; int last_recv = -1024; if (m_lcpProto.m_initialReceivedConfReq) initial = GetMRRUFromPacket(m_lcpProto.m_initialReceivedConfReq); if (m_lcpProto.m_lastSentConfReq) last_sent = GetMRRUFromPacket(m_lcpProto.m_lastSentConfReq); if (m_lcpProto.m_lastReceivedConfReq) last_recv = GetMRRUFromPacket(m_lcpProto.m_lastReceivedConfReq); ifOptions.Set(os); if ((initial >= 576) && (last_sent != initial) && (last_recv != initial) && os->pol && os->pol->Valid(P_MRRU) && (os->pol->Get(P_MRRU) > 0)) { if (++m_nrLCPRestartForMRRU > 3) return 0; if (m_lpolicy.Valid(P_MRRU) && m_lpolicy.Get(P_MRRU) > 0) return 0; auth_proto->Down(); m_lcpProto.RestartLCPForMRRU(os); return 1; } return 0; } void CLink::GotConfigTunnelGroup(OptionSet_t *os) { l2tp_tunnel_t *multihop = the_l2tpd->try_multihop(os); if (multihop) { m_lcpProto.Down(); multihop_link = new l2tp_session_t(multihop, this); return; } fprintf(stderr, "CLink::GotConfigTunnelGroup: couldn't get multihop for ERX-Tunnel-Group(%s)\n", os->tunnel_group); Hangup(); } /* * 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); if (multihop_link) { multihop_link->HardOutput(packet); delete packet; return; } /* * 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); if (m_interface) m_interface->Input(packet); else delete packet; // FIXME: should this be reinput? 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; case PPP_PROTO_MLCP: if (!want_mlcp && !m_npolicy.Get(P_WANT_MLCP) && !(ifOptions.pol && ifOptions.pol->Get(P_WANT_MLCP))) goto reject; if (m_phase >= PHASE_MLCP) m_mlcpProto.Input(packet); else { Log(LF_INFO | LF_RX, "%s: Rx MLCP packet out of phase. Phase = %s", m_channel->device_name, phaseLookup[m_phase]); } break; default: reject: Log(LF_INFO | LF_RX, "%s: Rx unknown protocol type %#x", m_channel->device_name, proto); if(m_phase > PHASE_ESTABLISH) { packet->Push16(proto); m_lcpProto.RejectProtocol(packet); } break; } delete packet; DebugVoidReturn; } int CLink::JoinBundle(int ndev_id) { if (!do_userland_only && 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_fd, cmd, arg); } int CLink::HardOutput(CPppPacket *packet, int pri) { 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_fd, 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 pri) { 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) { if (strcmp(m_channel->dev_class, "pppoe") && strcmp(m_channel->dev_class, "l2tp")) { 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, pri)); } void CLink::Hangup() { if (m_phase > PHASE_DISCONNECTING) m_phase = PHASE_DISCONNECTING; #if 0 if (m_reason) { free(m_reason); m_reason = NULL; } #endif if (!m_channel) return; 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) { if (disc_chans_index != -1) return; 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; if (m_reason) { free(m_reason); m_reason = NULL; } 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_fd, 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 { const 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 if (status && multihop_link) { /* this ends up setting multihop_link = NULL */ multihop_link->multihop_unlink(); } 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 < DefaultMRU()) { m_lpolicy.Set(P_MRU, m_hard_mru); if (!m_npolicy.Valid(P_MRU) || m_hard_mru < m_npolicy.Get(P_MRU)) m_npolicy.Set(P_MRU, m_hard_mru); } if (m_hard_mtu < DefaultMRU()) m_policy.Set(P_MRU, m_hard_mtu); // // Zeroing ifOptions here could be trouble // ifOptions.Clear(); // Open the protocols m_lcpProto.Open(); m_papProto.Open(); m_chapProto.Open(); m_mlcpProto.Open(); DebugVoidReturn; } /* * Close a link */ void CLink::Close(int status, const char *reason) { DebugEnter("CLink::Close()"); fprintf(stderr, "CLink::Close(%d, '%s')\n", status, reason); if (!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); m_mlcpProto.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) link->ifOptions.Set(*opt); if (link->ifOptions.user[0] == '\0') link->m_npolicy.Set(P_AUTH, 0); link->m_lcpProto.Up(); } void CLink::SetTimeConnected(void) { if (clock_gettime(CLOCK_MONOTONIC, &m_connect_time)) *(char *)0 = 0; } /* * Bring up a link */ void CLink::Up() { DebugEnter("CLink::Up()"); /* FIXME */ //SetACCM(m_channel, 0xffffffff, 0xffffffff); SetTimeConnected(); m_phase = PHASE_ESTABLISH; m_nrLCPRestartForMRRU = 0; 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; } void CLink::SetDownReason(const char *reason) { if (!m_reason) m_reason = strdup(reason); } /* * Bring down a link */ void CLink::Down() { DebugEnter("CLink::Down()"); LinkCancelGetConfig(); m_iccn_pkt = NULL; m_phase = PHASE_DEAD; // Bring down the protocols m_papProto.Down(); m_chapProto.Down(); m_lcpProto.Down(); m_mlcpProto.Down(); if (m_interface) { m_interface->DropLink(this, m_lastStatus, m_reason); if (m_reason) { free(m_reason); m_reason = NULL; } } DebugVoidReturn; } void CLink::AuthReady(void) { if (m_phase != PHASE_AUTHENTICATE) return; if (want_mlcp || m_npolicy.Get(P_WANT_MLCP) || (ifOptions.pol && ifOptions.pol->Get(P_WANT_MLCP))) { if (debug) fprintf(stderr, "AuthReady(): starting MLCP\n"); m_phase = PHASE_MLCP; if (m_npolicy.Get(P_MRRU)) { m_mlcpProto.their_mrru = m_npolicy.Get(P_MRRU); m_mlcpProto.our_mrru = m_npolicy.Get(P_MRRU); } if (ifOptions.pol && ifOptions.pol->Get(P_MRRU)) { m_mlcpProto.their_mrru = ifOptions.pol->Get(P_MRRU); m_mlcpProto.our_mrru = ifOptions.pol->Get(P_MRRU); } if (m_lpolicy.Get(P_MLCP_SSN)) { m_mlcpProto.their_ssn = m_npolicy.Get(P_MLCP_SSN); m_mlcpProto.our_ssn = m_npolicy.Get(P_MLCP_SSN); } if (m_npolicy.Get(P_MLCP_SSN)) { m_mlcpProto.their_ssn = m_npolicy.Get(P_MLCP_SSN); m_mlcpProto.our_ssn = m_npolicy.Get(P_MLCP_SSN); } if (ifOptions.pol && ifOptions.pol->Get(P_MLCP_SSN)) { m_mlcpProto.their_ssn = ifOptions.pol->Get(P_MLCP_SSN); m_mlcpProto.our_ssn = ifOptions.pol->Get(P_MLCP_SSN); } m_mlcpProto.Up(); } else Ready(); } void CLink::Ready(void) { DebugEnter("CLink::Ready()"); if (m_phase == PHASE_NETWORK) return; 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; m_interface->AddLink(this); Log(LF_DEBUG|LF_IPC, "MP Bundling complete to %s", i->GetName()); 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->GetName()); 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, ifOptions.user); if(m_interface == NULL) { Log(LF_ERROR|LF_IPC, "Error allocating memory"); Close(-2, "Error allocating memory"); DebugVoidReturn; } 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, const 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 (-1 != m_fd) { Log(LF_DEBUG|LF_CALL, "chclose: %d", m_fd); SelectSetEvents(m_fd, SEL_NONE); close(m_fd); m_fd = -1; } } int CLink::reopen(int answer) { char tmp_name[32]; int i; Log(LF_DEBUG|LF_CALL, "reopen(%s)", m_channel->device_name); chclose(); m_fd = open(m_channel->file_name, O_RDWR | O_NONBLOCK); if (-1 == m_fd) { Log(LF_DEBUG|LF_CALL, "Unabled to open device %s: %s", m_channel->device_name, strerror(errno)); return 1; } if (!ioctl(m_fd, BIOCGETCHNAME, tmp_name)) { free((void *)m_channel->device_name); m_channel->device_name = strdup(tmp_name); } 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(m_fd); m_fd = -1; return 1; } //setup_select(m_channel->device_id, m_channel, answer ? NULL : bab_do_input, answer ? bab_do_write : NULL); SelectSetEvents(m_fd, 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; ConnectComplete(cause); reopen(1); 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_fd, 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_fd, 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_fd, 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)); Log(LF_DEBUG|LF_CALL, "We're in do_hangups."); ConnectComplete(cause); Down(); reopen(1); m_channel->state = CS_IDLE; } void CLink::GetConfig(void (*cbf)(void *, OptionSet_t *), void *obj, OptionSet_t *options) { LinkCancelGetConfig(); /* cancel any outstanding req */ if (multihop_link) return; if (the_l2tpd) { l2tp_tunnel_t *multihop = the_l2tpd->try_multihop(options); if (multihop) { fprintf(stderr, "got multihop: options->user('%s')\n", options->user); Down(); // shutdown lcp multihop_link = new l2tp_session_t(multihop, this); return; } } babd_GetConfig(cbf, obj, options); } 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(const 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; } static int get_dev_class(const char *dev, char *dev_class) { int fd = open(dev, O_NONBLOCK|O_RDWR); int ret = -1; if (fd == -1) { perror("open"); return ret; } if (!ioctl(fd, BIOCGETCHCLASS, dev_class)) ret = 0; else perror("ioctl(BIOCGETCHCLASS)"); close(fd); return ret; } int addchan(const char *dev, const char *name, const char *devclass, int no_auth) { channel_t *ch; char tmp_class[32]; /* The strcmp() is a hack. FIXME. */ if (findchan(name)) return 1; if (dev && !devclass) { if (get_dev_class(dev, tmp_class)) return 1; devclass = tmp_class; } 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; } #include "ctrlfd.h" void CLink::dump_CLink(ctrlfd_t *cfd) { if (multihop_link) cfd->printf("\tmultihop_link(%p)\n", multihop_link); // FIXME cfd->printf("\tm_hard_mru(%u)\n", m_hard_mru); cfd->printf("\tm_hard_mtu(%u)\n", m_hard_mtu); if (m_interface) cfd->printf("\tm_interface(%s)\n", m_interface->GetName()); cfd->printf("\tm_wereAcked(%u)\n", m_wereAcked); cfd->printf("\tm_peerAcked(%u)\n", m_peerAcked); cfd->printf("\tm_loop_count(%u)\n", m_loop_count); cfd->printf("\tm_ident(%u)\n", m_ident); cfd->printf("\tm_ldisc(%u)\n", m_ldisc); cfd->printf("\tm_lastStatus(%u)\n", m_lastStatus); cfd->printf("\tm_phase(%s)\n", phaseLookup[m_phase]); cfd->printf("\tm_connect_time(%ld)\n", m_connect_time.tv_sec); cfd->printf("\tm_connect_time_nsec(%ld)\n", m_connect_time.tv_nsec); if (m_reason) cfd->printf("\tm_reason(\"%s\")\n", m_reason); cfd->printf("m_policy {\n"); m_policy.dump_full_state(cfd); cfd->printf("}\nm_npolicy {\n"); m_npolicy.dump_full_state(cfd); cfd->printf("}\nm_rpolicy {\n"); m_rpolicy.dump_full_state(cfd); cfd->printf("}\nm_lpolicy {\n"); m_lpolicy.dump_full_state(cfd); cfd->printf("}\nifOptions {\n"); ifOptions.dump_full_state(cfd); cfd->printf("}\n"); cfd->printf("m_lcpProto {\n"); m_lcpProto.dump_full_state(cfd); cfd->printf("}\n"); cfd->printf("m_papProto {\n"); m_papProto.dump_full_state(cfd); cfd->printf("}\n"); cfd->printf("m_chapProto {\n"); m_chapProto.dump_full_state(cfd); cfd->printf("}\n"); cfd->printf("m_mlcpProto {\n"); m_mlcpProto.dump_full_state(cfd); cfd->printf("}\n"); } void dump_clinks(ctrlfd_t *cfd) { if (1) return; for (CLink *link = linkListHead; link; link = link->m_next) { cfd->printf("CLink(%s) {\n", link->get_name()); link->dump_CLink(cfd); cfd->printf("}\n"); } } void CLinkInterimUpdateTimer::TimerExpired(void) { char iface_name[32]; m_link->m_interface->GetName(iface_name, sizeof(iface_name)); m_link->m_interface->SendAcctMsg(m_link, 0, NULL, ACCT_INTERIM_UPDATE, iface_name); Start(interim_update_interval); }