/* * iface.cc - Self explanitory * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: iface.cc,v 1.13 2004/10/16 22:56:45 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include #include #include #include #include #include #include #include #include #include #include /* RTF_UP */ #include #include "kernel.h" #include "iface.h" #include "aps_if.h" #include "bvirt_ns.h" #include "compat.h" class CPipe { int pipefds[2]; public: CPipe() { if (cloexec_pipe(pipefds)) { perror("cloexec_pipe"); exit(1); } } ~CPipe() { close(pipefds[0]); close(pipefds[1]); } ssize_t Read(char *buf, size_t count) { return read(pipefds[0], buf, count); } ssize_t Write(const char *buf, size_t count) { return write(pipefds[1], buf, count); } }; static CPipe async_ndev_pipe; static int iface_ndev_id; class CDeadIface : public CQueueItem { CInterface *m_iface; public: CDeadIface(CInterface *iface) { m_iface = iface; } ~CDeadIface() { if (m_iface) { m_iface->m_dead = NULL; delete m_iface; } } void ClearIface(void) { m_iface = NULL; } }; static CQueue killList(1); CInterface *ifListHead; CInterface **ifListTailp = &ifListHead; extern CLink *linkListHead; extern int broken_acfc; u32 iface_fixed_local_ip; #define CLASS_A_MASK 0x80000000 #define CLASS_B_MASK 0xC0000000 #define CLASS_C_MASK 0xD0000000 #define CLASS_D_MASK 0xF0000000 #define MAX_BPPP 255 extern int AddDefRoute(void *); extern int AddNetRoute(void *); extern int AddProxyArp(void *); extern void DialReport(DialResponse_t *); /* * Friends called from ndev.c */ extern struct enet_statistics *NetDevGetStats(void *); extern void NetDevDown(void *); extern void AddRoute(RouteMsg_t *); extern void AddProxy(ProxyMsg_t *); extern void SendAccounting(AcctMessage_t *); int num_ifaces; int iface_rename_to_ip = 0; void NetDevDown(void *iface) { CInterface *ifc = (CInterface *)iface; CLink *link; DebugEnter("NetDevDown()"); ifc->is_static = 0; // no longer will the interface be static /* * Close all links */ Log(LF_DEBUG|LF_PROTO, "Closing all links.."); if (ifc->links == NULL) { if (ifc->is_up) ifc->Down(); ifc->KissOfDeath(); DebugVoidReturn; } again: for (link=ifc->links; link; ) { if (link->m_phase > PHASE_DISCONNECTING) { link->Hangup(); goto again; } link=link->m_nextBundled; if (link == ifc->links) break; } for (int i = ifc->links_dialing-1; i>=0; i--) { for (link=linkListHead; NULL != link; link=link->m_next) { if (ifc->calls[i] == link->ifOptions.call && link->m_phase > PHASE_DISCONNECTING) { link->Hangup(); goto again; } } } DebugVoidReturn; } CInterface *FindIfaceDialing(Call *call) { return call->m_iface; } CInterface *FindIfaceName(const char *name) { char iface_name[32]; char *virt; CInterface *iface; virt = strchr((char *)name, '^'); for (iface = ifListHead; iface; iface = iface->next) { iface->GetName(iface_name, sizeof(iface_name), virt ? 1 : 0); if (!strcmp(name, iface_name)) return iface; } return NULL; } void RemoveLinkDialing(Call *call) { CInterface *iface; int i; if (!call) return; iface = call->m_iface; call->m_iface = NULL; if (iface) { for (i = 0; ilinks_dialing; i++) if (iface->calls[i] == call) { iface->links_dialing--; if (iface->links_dialing > 0) iface->calls[i] = iface->calls[iface->links_dialing]; return; } } } void CInterface::do_dial (void) { char buf[64]; unsigned i; char *dst = buf; for (i=dial_offset; m_phone[i] && (m_phone[i] != ';' && m_phone[i] != '/'); i++) *dst++ = m_phone[i]; *dst++ = 0; if (m_phone[i]) dial_offset = i + 1; else dial_offset = 0; if (!m_phone[dial_offset]) dial_offset = 0; Call *call = calls[links_dialing++] = new Call(0, this); do_bdial(NULL, m_site, buf, call); } void doRedialTimeout(void *data) { CInterface *iface = (CInterface *)data; iface->RedialTimeout(); } void CInterface::RedialTimeout(void) { struct bdev_stats bstats; unsigned long rx_delta, tx_delta; int rx_bpls, tx_bpls; time_t time_delta, current_time; static int drop_time; int active_links = nr_links + links_dialing; if (ndev_ioctl(BIOC_GETBSTATS, (long)(void *)&bstats)) perror("BIOC_GETBSTATS"); #if 0 Log(LF_DEBUG|LF_CALL, "RedialTimeout: dev=%d nr_links=%d links_dialing=%d", ndev_id, nr_links, links_dialing); Log(LF_DEBUG|LF_CALL, "rx: drop: %d raise: %d tx: drop: %d raise: %d", rx_drop_bpls, rx_raise_bpls, tx_drop_bpls, tx_raise_bpls); Log(LF_DEBUG|LF_CALL, " rx_bytes=%ld tx_bytes=%ld", bstats.rx_bytes, bstats.tx_bytes); Log(LF_DEBUG|LF_CALL, " last_rxb=%ld last_txb=%ld", last_rx_bytes, last_tx_bytes); #endif rx_delta = bstats.rx_bytes - last_rx_bytes; tx_delta = bstats.tx_bytes - last_tx_bytes; last_rx_bytes = bstats.rx_bytes; last_tx_bytes = bstats.tx_bytes; current_time = time(NULL); time_delta = (current_time - last_time) * (nr_links + !nr_links); if (time_delta) { rx_bpls = rx_delta/time_delta; tx_bpls = tx_delta/time_delta; } else rx_bpls = tx_bpls = 0; last_time = current_time; if (rx_delta || tx_delta) last_io_time = last_time; #if 0 Log(LF_DEBUG|LF_CALL, "RedialTimeout: rx_Bpls=%d tx_Bpls=%d", rx_bpls, tx_bpls); Log(LF_DEBUG|LF_CALL, "last_time: %ld last_io_time=%ld", last_time, last_io_time); #endif if (!links_dialing) { if ((active_links < min_links) || (!nr_links && tx_delta)) { drop_time = 0; do_dial(); } else if ( ( (rx_raise_bpls && (rx_bpls >= rx_raise_bpls)) || (tx_raise_bpls && (tx_bpls >= tx_raise_bpls)) || ( !rx_raise_bpls && !tx_raise_bpls && (min_links || max_links) && (tx_delta || rx_delta) ) )) { drop_time = 0; if (active_links < max_links) do_dial(); } else if ((nr_links > 1) && (nr_links > min_links) && ( (rx_drop_bpls && (rx_bpls < rx_drop_bpls)) || (tx_drop_bpls && (tx_bpls < tx_drop_bpls)) )) { drop_time += time_delta; if (drop_time > idle_secs) { drop_time = 0; links->Close(-2, "below bandwidth constraints"); } } else if ((nr_links > 0) && (nr_links > min_links) && idle_secs && (last_time - last_io_time) > idle_secs) links->Close(-2, "idle link detected"); } redialTimer.Start(redial_interval); } static CInterface *lookup_iface_index(bvirt_ns_t *netns, unsigned int ifindex) { CInterface *iface; for (iface = ifListHead; iface; iface = iface->next) { /*FIXME: O(n)*/ if (iface->m_ifindex == ifindex && iface->m_netns == netns) return iface; } return NULL; } void iface_change_flags(bvirt_ns_t *netns, unsigned int ifi_index, unsigned int ifi_flags, unsigned int ifi_change) { CInterface *iface = lookup_iface_index(netns, ifi_index); if (!iface) return; if (ifi_flags & IFF_UP) iface->m_iface_was_up = 1; /* Did IFF_UP change? If not, ignore */ if (!(ifi_change & IFF_UP)) return; if (!(ifi_flags & IFF_UP)) { if (!iface) return; if (!iface->m_iface_was_up) return; iface->Close(0, "interface downed"); } } int CInterface::ndev_ioctl(unsigned int cmd, unsigned long arg) { if (-1 == ndev_fd && links) return links->ch_ioctl(cmd, arg); return ioctl(ndev_fd, cmd, arg); } int async_create_bundle_rpc(void *arg1, void *arg2) { CLink *link = (CLink *)arg1; CInterface *iface = link->m_interface; unsigned long ndev_id = (unsigned long)arg2; int ret; if (iface->name_has_ip) { char name[32]; iface->GetName(name, sizeof(name), 0); ret = link->ch_ioctl(BIOCCREATEBUNDLENAME, (unsigned long)(void *)&name); if (ret) { fprintf(stderr, "failed to create interface name '%s': %d\n", name, errno); fflush(stderr); iface->name_has_ip = 0; ret = link->ch_ioctl(BIOCCREATEBUNDLE, ndev_id); } else { fprintf(stderr, "created interface name '%s'\n", name); fflush(stderr); } } else ret = link->ch_ioctl(BIOCCREATEBUNDLE, ndev_id); iface->async_create_bundle_complete(ret); return 0; } void CInterface::async_create_bundle_complete(int ret) { if (ret) *(char *)0 = 0; /* FIXME */ ndev_id = ndev_ioctl(BIOCGETDEVID, 0); if (ndev_id < 0) { perror("new iface: ndev_ioctl(BIOCGETDEVID)"); //FIXME abort = 1; } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); if (name_has_ip) GetName(ifr.ifr_name, sizeof(ifr.ifr_name)); else sprintf(ifr.ifr_name, "aps%d", ndev_id); int sock_fd = m_netns ? m_netns->m_b_sock_fd : b_sock_fd; if (0 == ioctl(sock_fd, SIOCGIFINDEX, &ifr)) { m_ifindex = ifr.ifr_ifindex; } else perror("SIOCGIFINDEX"); async_create_bundle_in_progress = 0; async_ndev_pipe.Write("", 1); } void CInterface::wait_for_ndev(void) { while (async_create_bundle_in_progress) { char buf; if (1 != async_ndev_pipe.Read(&buf, 1)) perror("CInterface::wait_for_ndev: Read"); } } int CInterface::async_create_bundle(CLink *link, unsigned long ndev_id) { async_create_bundle_in_progress = 1; if (m_netns) { int ret; ret = m_netns->generic_rpc(async_create_bundle_rpc, link, (void *)ndev_id, 0); if (!ret) { m_has_bundle = 1; m_netns->m_nr_ifaces++; } else *(char *)0 = 0; return ret; } return async_create_bundle_rpc(link, (void *)ndev_id); } #if 0 static int create_bundle_rpc(void *arg1, void *arg2) { CLink *link = (CLink *)arg1; unsigned long ndev_id = (unsigned long)arg2; return link->ch_ioctl(BIOCCREATEBUNDLE, ndev_id); } #endif int CInterface::create_bundle(CLink *link, unsigned long ndev_id) { if (m_netns && link->ch_ioctl(BIOCSETBUNDLENETNS, m_netns->m_tid)) { perror("BIOCSETBUNDLENETNS"); /* FIXME */ } int ret = link->ch_ioctl(BIOCCREATEBUNDLE, ndev_id); return ret; } CInterface::CInterface(CLink *link, const char *username) { int abort = 0; DebugEnter("CInterface::CInterface()"); m_netns = NULL; if (username) { const char *domain = strchr(username, '@'); if (domain) { domain ++; m_netns = lookup_bvirt_iface_virt_realm(domain); } } link->m_interface = this; ndev_created = 0; async_create_bundle_in_progress = 0; num_ifaces++; m_ipcpProto.m_parent = this; m_ipv6cpProto.m_parent = this; //m_bacpProto.m_parent = this; //m_bapProto.m_parent = this; nr_links = 0; links = link; is_up = 0; m_dead = NULL; lflags = rflags = 0; min_links = max_links = 0; rx_drop_bpls = rx_raise_bpls = tx_drop_bpls = tx_raise_bpls = 0; redial_interval = idle_secs = 0; links_dialing = 0; dial_offset = 0; is_static = 0; m_throttled = 0; memset(&calls, 0, sizeof(calls)); name_has_ip = 0; m_lcfg.Clear(); m_rpolicy.Clear(); m_phone = NULL; m_site = NULL; m_has_bundle = 0; have_ip = 0; m_have_ipv6 = 0; m_trying_ipv6 = 0; m_iface_was_up = 0; m_ifindex = ~0U; ndev_id = ndev_fd = -1; if (do_userland_only) { ndev_created = 1; ndev_id = iface_ndev_id++; } else if (link) { if (iface_rename_to_ip && (m_lcfg.rem_ip || links->ifOptions.rem_ip)) name_has_ip = 1; if (0 != async_create_bundle(link, ~0L)) { abort = 1; perror("Interface: create bundle failed"); } else ndev_created = 1; } /* * Add to global list */ m_prevp = ifListTailp; *m_prevp = this; next = NULL; ifListTailp = &next; last_rx_bytes = last_tx_bytes = 0; links_dialing = 0; m_closeReason = NULL; last_time = time(NULL); redialTimer.SetFunction(doRedialTimeout, this); if (link) { Open(); AddLink(link, 1); } if (abort) { Log(LF_WARN|LF_PROTO, "Error creating kernel interface, closing"); Close(-2, "Error creating kernel interface"); /* we can't abort any sooner */ } DebugExit(); } CInterface::~CInterface() { DebugEnter("CInterface::~CInterface()"); if (m_dead) { m_dead->ClearIface(); delete m_dead; m_dead = NULL; } release_dyn_ips(); /* * Remove ourselves from the global list of interfaces */ if (next) next->m_prevp = m_prevp; *m_prevp = next; if (ifListTailp == &next) ifListTailp = m_prevp; if (-1 != ndev_fd) close(ndev_fd); if (m_closeReason) { free((void*)m_closeReason); m_closeReason = NULL; } if (m_netns && m_has_bundle) m_netns->m_nr_ifaces--; num_ifaces--; DebugExit(); } void killIfaces(void) { CDeadIface *qi; while ((qi = (CDeadIface *)killList.Pull())) delete qi; } void CInterface::KissOfDeath() { Log(LF_DEBUG|LF_IPC, "KissOfDeath with links_dialing = %d", links_dialing); if (links_dialing) return; if (m_dead) return; m_dead = new CDeadIface(this); killList.Append(m_dead); } /* DropLink * removes a link from the interface's bundle. Also responsible for * initiating the events that lead to the deletion of the interface. */ void CInterface::DropLink(CLink *link, int cause = 0, const char *reason) { CLink **link_pp; DebugEnter("CInterFace::DropLink()"); link->m_interface = NULL; RemoveLinkDialing(link->ifOptions.call); if (!is_static && is_up) { int data = 0; if (ndev_ioctl(BIOC_GETLBFL, (unsigned long)(void *)&data)) perror("DropLink: ioctl(BIOC_GETLBFL)"); data |= BF_STAY_UP; if (ndev_ioctl(BIOC_SETLBFL, data)) perror("DropLink: ioctl(BIOC_SETLBFL)"); } if (0 != link->ch_ioctl(BIOCLEAVEBUNDLE, 0) #ifdef EUNATCH && EUNATCH != errno #endif ) perror("DropLink: ioctl(BIOCLEAVEBUNDLE)"); /* * Remove the link from the bundle */ Log(LF_DEBUG|LF_IPC, "startLink= %p", links); char iface_name[32]; GetName(iface_name, sizeof(iface_name)); if (links == NULL || nr_links == 0) { Log(LF_ERROR|LF_CALL, "%s/%s: link not in bundle", iface_name, link->m_channel->device_name); DebugVoidReturn; } link->m_interim_update_timer.Stop(); SendAcctMsg(link, cause, reason, ACCT_STOP, iface_name); int found = 0; for (link_pp = &links; *link_pp; link_pp = &(*link_pp)->m_nextBundled) { if (*link_pp == link) { *link_pp = (*link_pp)->m_nextBundled; Log(LF_DEBUG|LF_IPC, "Link dropped, bundled = %d", nr_links); found = 1; break; } } if (!found) { Log(LF_ERROR|LF_CALL, "%s: Link %s not in bundle!", iface_name, link->m_channel->device_name); DebugVoidReturn; } link->m_nextBundled = NULL; if(nr_links == 1) { Log(LF_DEBUG|LF_IPC, "Last link removed from bundle"); if (!is_static) { Down(); KissOfDeath(); if (ndev_fd == -1) { link->ch_ioctl(BIOCDESTROYBUNDLE, 0); if (m_netns && m_has_bundle) m_netns->m_nr_ifaces--; m_has_bundle = 0; } release_dyn_ips(); } /* * If there is only a single link, break the ring */ m_lcfg.Clear(); m_rpolicy.Clear(); have_ip = 0; m_have_ipv6 = 0; m_trying_ipv6 = 0; if (m_closeReason) { free((void *)m_closeReason); m_closeReason = NULL; } } nr_links--; DebugVoidReturn; } void CInterface::SendAcctMsg(CLink *link, int cause, const char *reason, unsigned type, const char *iface_name) { // // Offer an accounting record // AcctMessage_t *am = new AcctMessage_t; if (am == NULL) return; memset(am, 0, sizeof(AcctMessage_t)); am->type = type; strcpy(am->port, link->m_channel->device_name); strcpy(am->dev_class, link->m_channel->dev_class); strcpy(am->ifname, iface_name); strcpy(am->user, link->ifOptions.user); am->call = link->ifOptions.call; am->loc_ip_address = m_lcfg.loc_ip; am->rem_ip_address = m_lcfg.rem_ip; am->connected_time = link->get_connected_time(); if (link->ifOptions.qos_profile_name) strcpy(am->qos_profile_name, link->ifOptions.qos_profile_name); struct bdev_stats stats; memset(&stats, 0, sizeof(stats)); if (0 == link->ch_ioctl(BIOC_GET_LINK_STATS, (long)&stats)) { am->in_octets = stats.rx_bytes; am->out_octets = stats.tx_bytes; am->in_packets = stats.rx_packets; am->out_packets = stats.tx_packets; } else { am->in_octets = link->octets_in; am->out_octets = link->octets_out; am->in_packets = link->packets_in; am->out_packets = link->packets_out; } strncpy(am->phone, link->ifOptions.phone, PHONE_LEN); am->term_cause = cause; if (m_closeReason) strcpy(am->reason, m_closeReason); else if (reason) strcpy(am->reason, reason); else snprintf(am->reason, sizeof(am->reason), "%s (%d)", cause >= 0 ? strcause(cause & 0x7f) : "Unknown", cause); SendAccounting(am); } u16 checksum(u16 *data, int len) { unsigned long sum = 0; while (len > 1) { sum += ntohs(*data++); len -= 2; } if (len) sum += ntohl(((u16)*(u8 *)data) << 8); sum = (sum >> 16) + (sum & 0xffff); sum += sum >> 16; return ~sum; } void ip_Input(CInterface *iface, CPppPacket *pkt) { u8 ihl_ver = pkt->Pull8(); u8 tos = pkt->Pull8(); u16 len = pkt->Pull16(); u16 id = pkt->Pull16(); u16 frag_off = pkt->Pull16(); u8 ttl = pkt->Pull8(); u8 proto = pkt->Pull8(); u16 csum = pkt->Pull16(); u32 source_addr = pkt->Pull32(); u32 dest_addr = pkt->Pull32(); #if 0 printf("ihl_ver=0x%02x len=0x%04x id=0x%04x ttl=%d proto=0x%02x\n" "frag_off=0x%04x src=0x%08x dst=0x%08x\n", ihl_ver, len, id, ttl, proto, frag_off, source_addr, dest_addr); #endif /* Is this an ICMP packet we handle? */ if (ihl_ver == 0x45 && proto == 0x01 && dest_addr == iface->m_lcfg.loc_ip) { u8 icmp_type = pkt->Pull8(); u8 icmp_code = pkt->Pull8(); u16 icmp_chksum = pkt->Pull16(); u16 icmp_id = pkt->Pull16(); u16 icmp_seq = pkt->Pull16(); #if 0 printf("icmp_type = 0x%02x icmp_code = 0x%02x icmp_id = 0x%04x icmp_seq = 0x%04x\n", icmp_type, icmp_code, icmp_id, icmp_seq); #endif if (icmp_type == 0x08) { /* ICMP_ECHO */ CPppPacket rep_pkt; rep_pkt.Put16(PPP_PROTO_IP); rep_pkt.Put8(ihl_ver); rep_pkt.Put8(tos); rep_pkt.Put16(len); rep_pkt.Put16(id); rep_pkt.Put16(frag_off); rep_pkt.Put8(64); /* ttl */ rep_pkt.Put8(proto); rep_pkt.Put16(0); /* csum */ rep_pkt.Put32(dest_addr); rep_pkt.Put32(source_addr); rep_pkt.Put8(0x00); /* icmp_type ICMP_ECHOREPLY */ rep_pkt.Put8(icmp_code); rep_pkt.Put16(0); /* icmp_chksum */ rep_pkt.Put16(icmp_id); rep_pkt.Put16(icmp_seq); rep_pkt.Put(pkt); u16 *start = (u16 *)rep_pkt.m_start; start[6] = htons(checksum(start + 1, 20)); iface->OutputQ(&rep_pkt); } (void)icmp_chksum; } (void)ttl; (void)csum; } /* * Receive data on an interface */ void CInterface::Input(CPppPacket *pkt) { u16 proto; DebugEnter("CInterface::Input()"); proto = pkt->Pull16(); if(!proto) { Log(LF_INFO|LF_RX, "MP Fragment consumed"); delete pkt; DebugVoidReturn; } Log(LF_DEBUG|LF_RX, "Received packet: procotol 0x%x", proto); switch(proto) { case PPP_PROTO_IP: if (do_userland_only) ip_Input(this, pkt); else Log(LF_DEBUG|LF_PROTO, "Received an ip packet... dropping."); break; case PPP_PROTO_IPCP: m_ipcpProto.Input(pkt); break; case PPP_PROTO_IPV6CP: if (m_trying_ipv6) m_ipv6cpProto.Input(pkt); else goto reject; break; case PPP_PROTO_BACP: // m_bacpProto.Input(pkt); break; case PPP_PROTO_BAP: // m_bapProto.Input(pkt); break; default: Log(LF_DEBUG|LF_PROTO, "Unknown protocol...rejecting"); reject: pkt->Push16(proto); links->m_lcpProto.RejectProtocol(pkt); } delete pkt; DebugVoidReturn; } /* * Send a packet on the interface, queuing if needed */ int CInterface::OutputQ(CPppPacket *pkt) { CPppPacket *copy; copy = new CPppPacket(pkt); if (!copy) return -ENOMEM; return OutputDelete(copy); } int CInterface::OutputDelete(CPppPacket *pkt) { if (nr_links) { //links->Output(pkt); int len = pkt->GetLength(); Log(LF_INFO | LF_TX, "%s: TX packet len=%d", GetName(), len); LogPacket(LF_INFO | LF_TX, pkt->m_start, len); if (-1 == ndev_fd && links) { links->Output(pkt); } else if (-1 == write(ndev_fd, pkt->m_start, len)) { perror("iface: write"); Log(LF_ALERT | LF_TX, "%s: write (len=%d) failed: %s", GetName(), len, strerror(errno)); } } delete pkt; return 0; } void CInterface::RcvRejIpv6(void) { if (!m_trying_ipv6) return; /* Passively accept IPv6CP rejection by stopping it. */ m_ipv6cpProto.Down(); m_trying_ipv6 = 0; DialResponse_t dr; if (have_ip) dr.status = -1; else dr.status = 0; sprintf(dr.msg, "IPv6CP rejected"); CLink *link = links; while (link) { dr.call = link->ifOptions.call; DialReport(&dr); link = link->m_nextBundled; } } void CInterface::AddIpv6Route(int sock6_fd, RouteIpv6_t *route, u64 remote_token, int index) { struct in6_rtmsg rt; memset(&rt, 0, sizeof(rt)); memcpy(&rt.rtmsg_dst, route->dst, 16); rt.rtmsg_dst_len = route->dst_len; rt.rtmsg_gateway.s6_addr16[0] = htons(0xfe80); rt.rtmsg_gateway.s6_addr16[4] = htons(remote_token >> 48); rt.rtmsg_gateway.s6_addr16[5] = htons(remote_token >> 32); rt.rtmsg_gateway.s6_addr16[6] = htons(remote_token >> 16); rt.rtmsg_gateway.s6_addr16[7] = htons(remote_token); rt.rtmsg_flags = RTF_UP | RTF_GATEWAY; rt.rtmsg_ifindex = index; rt.rtmsg_metric = 1; if (ioctl(sock6_fd, SIOCADDRT, &rt)) perror("ioctl(sock6_fd, SIOCADDRT)"); } void CInterface::ReadyIpv6(u64 token, u64 remote_token) { struct ifreq ifr; int sock6_fd = m_netns ? m_netns->m_b_sock_fd : b_sock6_fd; memset(&ifr, 0, sizeof ifr); ReadyPrepareName(&ifr, sock6_fd); if (ioctl(sock6_fd, SIOCGIFINDEX, &ifr) < 0) perror("ioctl(SIOGIFINDEX)"); int index = ifr.ifr_ifindex; ifr.ifr_flags = IFF_UP | IFF_POINTOPOINT; if (0 != ioctl(sock6_fd, SIOCSIFFLAGS, &ifr)) perror("error setting interface flags"); u16 mrru = links->m_rpolicy.Get(P_MRRU); u16 mru = links->m_rpolicy.Get(P_MRU); mru = (mru >= 68) ? mru : links->DefaultMRU(); ifr.ifr_mtu = mrru ? mrru : mru; if (!do_userland_only && 0 != ioctl(sock6_fd, SIOCSIFMTU, &ifr)) perror("error setting interface mtu"); struct in6_ifreq { struct in6_addr ifr6_addr; u32 ifr6_prefixlen; int ifr6_ifindex; }; struct in6_ifreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); ifr6.ifr6_ifindex = index; ifr6.ifr6_prefixlen = 10; ifr6.ifr6_addr.s6_addr16[0] = htons(0xfe80); ifr6.ifr6_addr.s6_addr16[4] = htons(token >> 48); ifr6.ifr6_addr.s6_addr16[5] = htons(token >> 32); ifr6.ifr6_addr.s6_addr16[6] = htons(token >> 16); ifr6.ifr6_addr.s6_addr16[7] = htons(token); if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) perror("ioctl(SIOCSIFADDR)"); /* Setup global scope address on interface. */ if (links->ifOptions.ipv6_ndra_len) { ifr6.ifr6_prefixlen = links->ifOptions.ipv6_ndra_len; memcpy(ifr6.ifr6_addr.s6_addr16, links->ifOptions.ipv6_ndra, 8); ifr6.ifr6_addr.s6_addr16[4] = htons(token >> 48); ifr6.ifr6_addr.s6_addr16[5] = htons(token >> 32); ifr6.ifr6_addr.s6_addr16[6] = htons(token >> 16); ifr6.ifr6_addr.s6_addr16[7] = htons(token); m_have_ipv6 = 1; if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) perror("ioctl(SIOCSIFADDR)"); } rflags |= BF_PASS_IPV6; lflags |= BF_PASS_IPV6; /* Set bundle flags to propagate ipv6 */ if (0 != links->ch_ioctl(BIOC_SETLBFL, lflags)) perror("AddLink3: ioctl(BIOC_SETLBFL)"); if (0 != links->ch_ioctl(BIOC_SETRBFL, rflags)) perror("AddLink4: ioctl(BIOC_SETRBFL)"); if (links->ifOptions.ipv6_ndra_len) { struct in6_rtmsg rt; memset(&rt, 0, sizeof(rt)); memcpy(rt.rtmsg_dst.s6_addr, links->ifOptions.ipv6_ndra, 16); rt.rtmsg_flags = RTF_UP; rt.rtmsg_dst_len = links->ifOptions.ipv6_ndra_len; rt.rtmsg_ifindex = index; fprintf(stderr, "index = %d ndra_len = %d\n", index, links->ifOptions.ipv6_ndra_len); rt.rtmsg_metric = 1; if (ioctl(sock6_fd, SIOCADDRT, &rt)) perror("ioctl(sock6_fd, SIOCADDRT)"); } if ((m_rpolicy.Valid(P_IPV6_DEFROUTE) && m_rpolicy.Get(P_IPV6_DEFROUTE)) || (links->m_lpolicy.Valid(P_IPV6_DEFROUTE) && links->m_lpolicy.Get(P_IPV6_DEFROUTE))) { struct in6_rtmsg rt; memset(&rt, 0, sizeof(rt)); rt.rtmsg_gateway.s6_addr16[0] = htons(0xfe80); rt.rtmsg_gateway.s6_addr16[4] = htons(remote_token >> 48); rt.rtmsg_gateway.s6_addr16[5] = htons(remote_token >> 32); rt.rtmsg_gateway.s6_addr16[6] = htons(remote_token >> 16); rt.rtmsg_gateway.s6_addr16[7] = htons(remote_token); rt.rtmsg_flags = RTF_UP | RTF_GATEWAY; rt.rtmsg_dst_len = 0; rt.rtmsg_ifindex = index; fprintf(stderr, "index = %d ndra_len = %d\n", index, links->ifOptions.ipv6_ndra_len); rt.rtmsg_metric = 1; if (ioctl(sock6_fd, SIOCADDRT, &rt)) perror("ioctl(sock6_fd, SIOCADDRT)"); } for (int i=0; i < m_lcfg.nr_ipv6_routes; i++) AddIpv6Route(sock6_fd, &m_lcfg.ipv6_routes[i], remote_token, index); DialResponse_t dr; if (!m_trying_ip || have_ip) dr.status = -1; else dr.status = 0; sprintf(dr.msg, "Local IPv6: fe80::%04x:%04x:%04x:%04x" " Remote IPv6: fe80::%04x:%04x:%04x:%04x", (unsigned)(token >> 48) & 0xffff, (unsigned)(token >> 32) & 0xffff, (unsigned)(token >> 16) & 0xffff, (unsigned)(token >> 0) & 0xffff, (unsigned)(remote_token >> 48) & 0xffff, (unsigned)(remote_token >> 32) & 0xffff, (unsigned)(remote_token >> 16) & 0xffff, (unsigned)(remote_token >> 0) & 0xffff); CLink *link = links; while (link) { dr.call = link->ifOptions.call; DialReport(&dr); link = link->m_nextBundled; } } void CInterface::ReadyPrepareName(struct ifreq *req, int sock_fd) { GetName(req->ifr_name, sizeof(req->ifr_name), 0); if (iface_rename_to_ip && !name_has_ip) { name_has_ip = 1; GetName(req->ifr_newname, sizeof(req->ifr_newname), 0); if (0 != ioctl(sock_fd, SIOCSIFNAME, req)) { Log(LF_ERROR|LF_STATE, "error setting interface name %s -> %s", req->ifr_name, req->ifr_newname); name_has_ip = 0; } else { name_has_ip = 1; Log(LF_INFO|LF_STATE, "renamed %s -> %s\n", req->ifr_name, req->ifr_newname); } GetName(req->ifr_name, sizeof(req->ifr_name), 0); } } static int set_ipv4_rp_filter_fn(void *ifname_p, void *val_p) { char *ifname = (char *)ifname_p; long val = (long)val_p; char filename[64]; int fd; snprintf(filename, sizeof(filename), "/proc/sys/net/ipv4/conf/%s/rp_filter", ifname); fd = open(filename, O_WRONLY); if (fd >= 0) { char str[32]; int len; len = snprintf(str, sizeof(str), "%ld\n", val); int ret = write(fd, str, len); close(fd); return (ret >= len) ? 0 : -EINVAL; } return -errno; } int CInterface::set_ipv4_rp_filter(long val) { char ifname[32]; GetName(ifname, sizeof(ifname), false); if (m_netns) return m_netns->generic_rpc(set_ipv4_rp_filter_fn, (void *)ifname, (void *)val, true); return set_ipv4_rp_filter_fn(ifname, (void *)val); } // // An NCP is ready so create a kernel network interface // void CInterface::Ready(OptionSet_t *opts) { struct ifreq req; struct sockaddr_in sin; RouteMsg_t rt; ProxyMsg_t proxy; DebugEnter("CInterface::Ready()"); if (have_ip && opts) { fprintf(stderr, "CInterface::Ready() -- have_ip already set\n"); DebugVoidReturn; } int sock_fd = b_sock_fd; if (m_netns) sock_fd = m_netns->m_b_sock_fd; have_ip = 1; if (opts) m_lcfg.Set(opts); if (!do_userland_only) { if (m_lcfg.min_frags && (0 != ndev_ioctl(BIOC_SET_MIN_FRAGS, m_lcfg.min_frags))) perror("CInterface::Ready: error setting MIN_FRAGS\n"); if (m_lcfg.max_frags && (0 != ndev_ioctl(BIOC_SET_MAX_FRAGS, m_lcfg.max_frags))) perror("CInterface::Ready: error setting MAX_FRAGS\n"); // We put this here because some routers incorrectly // won't negotiate IPCP options unless the ACFC stuff // if added to the packet....which can get turned off // during LCP negotation. Thus, we turn it on now the // interface is ready so these broken routers can work // with us. // Flags is set for us in Addlink below #if 0 if (lflags!=-1) { Log(LF_DEBUG|LF_PROTO, "Setting link flags (deferred)"); if (0 != linkFlags->ch_ioctl(BIOC_SETLCFL, lflags)) perror("AddLink: ioctl(BIOC_SETLCFL)"); if (0 != linkFlags->ch_ioctl(BIOC_SETRCFL, rflags)) perror("AddLink: ioctl(BIOC_SETRCFL)"); } #endif } // // Bring Up the interface // memset(&req, 0, sizeof(req)); memset(&sin, 0, sizeof sin); ReadyPrepareName(&req, sock_fd); if (m_lcfg.pol && m_lcfg.pol->Valid(P_REVERSE_PATH)) set_ipv4_rp_filter(m_lcfg.pol->Get(P_REVERSE_PATH)); else if (links->m_lpolicy.Valid(P_REVERSE_PATH)) set_ipv4_rp_filter(links->m_lpolicy.Get(P_REVERSE_PATH)); else if (links->m_policy.Valid(P_REVERSE_PATH)) set_ipv4_rp_filter(links->m_policy.Get(P_REVERSE_PATH)); u16 mrru = links->m_rpolicy.Get(P_MRRU); u16 mru = links->m_rpolicy.Get(P_MRU); mru = (mru >= 68) ? mru : links->DefaultMRU(); req.ifr_mtu = mrru ? mrru : mru; if (!do_userland_only && 0 != ioctl(sock_fd, SIOCSIFMTU, &req)) perror("error setting interface mtu"); if (!do_userland_only && (!iface_fixed_local_ip || (iface_fixed_local_ip != m_lcfg.loc_ip))) { sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(m_lcfg.loc_ip); memcpy(&req.ifr_addr, &sin, sizeof(sin)); if (0 != ioctl(sock_fd, SIOCSIFADDR, &req)) perror("error setting interface address"); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(m_lcfg.rem_ip); memcpy(&req.ifr_dstaddr, &sin, sizeof(sin)); if (0 != ioctl(sock_fd, SIOCSIFDSTADDR, &req)) perror("error setting interface dest addr"); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(~0U); memcpy(&req.ifr_netmask, &sin, sizeof(sin)); if (0 != ioctl(sock_fd, SIOCSIFNETMASK, &req)) perror("error setting interface netmask"); } req.ifr_flags = IFF_UP | IFF_POINTOPOINT; if (!do_userland_only && 0 != ioctl(sock_fd, SIOCSIFFLAGS, &req)) perror("error setting interface flags"); //m_iface_was_up = 1; if (is_static) { last_time = time(NULL); redialTimer.Start(redial_interval); } // // Add host route // if ((iface_fixed_local_ip && iface_fixed_local_ip == m_lcfg.loc_ip) || (LINUX_VERSION_CODE < 0x20200)) { memset(&rt, 0, sizeof(RouteMsg_t)); rt.dst_addr = htonl(m_lcfg.rem_ip); rt.mask = 0xFFFFFFFF; rt.flags = ROUTE_HOST; GetName(rt.dev, sizeof(rt.dev), 0); AddRoute(&rt); } if (m_lcfg.defroute) { Log(LF_DEBUG|LF_STATE, "Adding default route"); memset(&rt, 0, sizeof(RouteMsg_t)); rt.gw_addr = htonl(m_lcfg.rem_ip); rt.flags = ROUTE_GATEWAY; GetName(rt.dev, sizeof(rt.dev), 0); AddRoute(&rt); } if (m_lcfg.netroute) { u32 netmask = m_lcfg.netmask; memset(&rt, 0, sizeof(RouteMsg_t)); if (!netmask) { // Use the default netmask for network type if ((m_lcfg.rem_ip & CLASS_A_MASK) == 0) { // This is a class A address Log(LF_DEBUG|LF_STATE, "Class A ip address"); netmask = 0xFF000000; } else if ((m_lcfg.rem_ip & CLASS_B_MASK) == 0x80000000) { // This is a class B address Log(LF_DEBUG|LF_STATE, "Class B ip address"); netmask = 0xFFFF0000; } else if ((m_lcfg.rem_ip & CLASS_C_MASK) == 0xC0000000) { // This is a class C address Log(LF_DEBUG|LF_STATE, "Class C ip address"); netmask = 0xFFFFFF00; } else if ((m_lcfg.rem_ip & CLASS_D_MASK) == 0xE000000) { // This is a class D address Log(LF_DEBUG|LF_STATE, "Class D ip address"); netmask = 0xFFFFFFFF; } else { Log(LF_DEBUG|LF_STATE, "Unable to determine netmask"); } } Log(LF_DEBUG|LF_STATE, "Using ip 0x%x, Netmask 0x%x", m_lcfg.rem_ip, m_lcfg.netmask); rt.dst_addr = htonl(m_lcfg.rem_ip & netmask); rt.mask = htonl(netmask); rt.flags = ROUTE_NET; GetName(rt.dev, sizeof(rt.dev), 0); // Don't add a route for the pointopoint address of the iface. if (netmask != 0xffffffffU) AddRoute(&rt); } // // Add Proxy ARP entry // if (m_lcfg.proxy) { Log(LF_DEBUG|LF_STATE, "Adding proxy ARP entry"); memset(&proxy, 0, sizeof(ProxyMsg_t)); proxy.dst_addr = htonl(m_lcfg.rem_ip); Log(LF_DEBUG|LF_STATE, "Adding proxy ARP entry at %x", proxy.dst_addr); AddProxy(&proxy); } if (m_lcfg.nr_routes) { for (int i=0; i> 24) & 0xff, (unsigned)(m_lcfg.loc_ip >> 16) & 0xff, (unsigned)(m_lcfg.loc_ip >> 8) & 0xff, (unsigned)(m_lcfg.loc_ip >> 0) & 0xff, (unsigned)(m_lcfg.rem_ip >> 24) & 0xff, (unsigned)(m_lcfg.rem_ip >> 16) & 0xff, (unsigned)(m_lcfg.rem_ip >> 8) & 0xff, (unsigned)(m_lcfg.rem_ip >> 0) & 0xff ); CLink *link=links; while (link) { dr.call = link->ifOptions.call; DialReport(&dr); SendAcctStart(link); link = link->m_nextBundled; } DebugVoidReturn; } void CInterface::SendAcctStart(CLink *link) { AcctMessage_t *am; // Offer an accounting record am = new AcctMessage_t; if (am != NULL) { memset(am, 0, sizeof(AcctMessage_t)); am->type = ACCT_START; strcpy(am->port, link->m_channel->device_name); strcpy(am->dev_class, link->m_channel->dev_class); GetName(am->ifname, sizeof(am->ifname)); strcpy(am->user, link->ifOptions.user); am->call = link->ifOptions.call; am->loc_ip_address = m_lcfg.loc_ip; am->rem_ip_address = m_lcfg.rem_ip; strncpy(am->phone, link->ifOptions.phone, PHONE_LEN); if (link->ifOptions.qos_profile_name) strcpy(am->qos_profile_name, link->ifOptions.qos_profile_name); SendAccounting(am); } link->m_interim_update_timer.Start(interim_update_interval); } void CInterface::AddLink(CLink *link, int already_present) { if (link->m_nextBundled && !already_present) { Log(LF_ALERT|LF_CALL, "%s/%s: uh-oh -- link already in bundle", GetName(), link->m_channel->device_name); return; } wait_for_ndev(); /* dfs_restore() results in late creation of bundles with specific * interface ids -- handle this now. */ if (!ndev_created) { if (do_userland_only) { ndev_created = 1; ndev_id = iface_ndev_id++; name_has_ip = 0; } else if (0 != create_bundle(link, ndev_id)) perror("Interface: create bundle failed"); else { ndev_created = 1; if (ndev_id == -1) { ndev_id = link->ch_ioctl(BIOCGETDEVID, 0); if (ndev_id < 0) perror("new iface: ndev_ioctl(BIOCGETDEVID)"); } name_has_ip = 0; } } // // if this is the first link, negotiate our protocols // after binding // if (!already_present) { link->m_nextBundled = links; links = link; } else if (links != link) *(char *)0 = 0; nr_links++; link->JoinBundle(ndev_id); /* Flags on receive (local) side */ lflags = BF_PPP | BF_PASS_IP; if (m_have_ipv6) lflags |= BF_PASS_IPV6; if (link->m_lpolicy.Get(P_ACFC)) lflags |= BF_ACFC; if (link->m_lpolicy.Get(P_PFC)) lflags |= BF_PFC; if (link->m_lpolicy.Get(P_SSN)) lflags |= BF_SSN; if (link->m_lpolicy.Get(P_MRRU)) lflags |= BF_PASS_ML; if (link->m_lpolicy.Get(P_WANT_RAIN)) { lflags |= BF_RAIN; rflags |= BF_RAIN; } /* Flags on transmit (remote) side */ rflags = BF_PPP | BF_PASS_IP; if (m_have_ipv6) rflags |= BF_PASS_IPV6; if (link->m_rpolicy.Get(P_ACFC)) rflags |= BF_ACFC; if (link->m_rpolicy.Get(P_PFC)) rflags |= BF_PFC; if (link->m_rpolicy.Get(P_SSN)) rflags |= BF_SSN; if (link->m_rpolicy.Get(P_MRRU)) rflags |= BF_PASS_ML; if (link->m_rpolicy.Get(P_WANT_RAIN)) { rflags |= BF_RAIN; lflags |= BF_RAIN; } if (link->ifOptions.pol && link->ifOptions.pol->Get(P_WANT_RAIN)) { lflags |= BF_RAIN; rflags |= BF_RAIN; } /* Hack: we must not send ACFC headers on L2TP packets. Forcibly * enable the ACFC flag to ensure the 0x3ff header is not included * in the L2TP payload. */ if (!strcmp(link->m_channel->dev_class, "l2tp") || !strcmp(link->m_channel->dev_class, "pppoe")) { lflags |= BF_ACFC; rflags |= BF_ACFC; } // Check if we need to set the interface options NOW or after // LCP is done on the first link. This works around a bug // in some routers that don't turn off ACFC during IPCP // which causes the link to time out. This only applies to the // first link. if (nr_links==1) { // First link: Check if we are broken if (broken_acfc) { // Broken router on remote. Don't set link flags now // Save the link point for later when we need it linkFlags=link; Log(LF_DEBUG|LF_PROTO, "Deferring setting link flags at user's request"); } else if (!do_userland_only) { // Not broken...just set the flags Log(LF_DEBUG|LF_PROTO, "Setting link flags"); if (0 != link->ch_ioctl(BIOC_SETLCFL, lflags)) perror("AddLink1: ioctl(BIOC_SETLCFL)"); if (0 != link->ch_ioctl(BIOC_SETRCFL, rflags)) perror("AddLink2: ioctl(BIOC_SETRCFL)"); if (0 != link->ch_ioctl(BIOC_SETLBFL, lflags)) perror("AddLink3: ioctl(BIOC_SETLBFL)"); if (0 != link->ch_ioctl(BIOC_SETRBFL, rflags)) perror("AddLink4: ioctl(BIOC_SETRBFL)"); linkFlags=NULL; } } else { Log(LF_DEBUG|LF_PROTO, "Setting link flags"); // Not the first link...just set the flags if (0 != link->ch_ioctl(BIOC_SETLCFL, lflags)) perror("AddLink5: ioctl(BIOC_SETCFL)"); if (0 != link->ch_ioctl(BIOC_SETLCFL, rflags)) perror("AddLink6: ioctl(BIOC_SETLCFL)"); #if 0 if (0 != ndev_ioctl(BIOC_SETLBFL, lflags)) perror("AddLink7: ioctl(BIOC_SETLBFL)"); if (0 != ndev_ioctl(BIOC_SETRBFL, rflags)) perror("AddLink8: ioctl(BIOC_SETRBFL)"); #endif linkFlags=NULL; } // Reset accounting data for the link link->packets_in = link->packets_out = 0; link->octets_in = link->octets_out = 0; if (links->m_nextBundled == NULL) { Log(LF_DEBUG|LF_IPC, "Added first link"); // // Only up the interface if the link isn't already nailed up // if (!is_up || !have_ip) Up(); } if (have_ip) SendAcctStart(link); DialResponse_t dr; dr.call = link->ifOptions.call; dr.status = have_ip ? -1 : 0; if (m_trying_ipv6 && !m_have_ipv6) dr.status = 0; snprintf(dr.msg, sizeof(dr.msg), "Connected on %s with %s as %s", link->m_channel->device_name, GetName(), link->ifOptions.user); DialReport(&dr); } /* * Open an interface */ void CInterface::Open() { DebugEnter("CInterface::Open()"); m_ipcpProto.Open(); m_ipv6cpProto.Open(); DebugVoidReturn; } void CInterface::Up() { DebugEnter("CInterface::Up()"); if (have_ip) goto out; is_up = 1; m_lcfg.Set(links->ifOptions); if (m_lcfg.rem_ip == 0xffffffffU && m_lcfg.rem_ip_pool) m_lcfg.rem_ip = request_ip_address(m_lcfg.rem_ip_pool, this); if (m_lcfg.loc_ip == 0xffffffffU && m_lcfg.loc_ip_pool) m_lcfg.loc_ip = request_ip_address(m_lcfg.loc_ip_pool, this); m_ipcpProto.SetWill(m_lcfg); m_rpolicy.Set(links->m_rpolicy); m_phone = m_lcfg.phone; m_site = m_lcfg.site; m_trying_ip = 1; m_ipcpProto.Up(); if ((m_lcfg.pol && m_lcfg.pol->Valid(P_WANT_IPV6) && m_lcfg.pol->Get(P_WANT_IPV6)) || (m_rpolicy.Valid(P_WANT_IPV6) && m_rpolicy.Get(P_WANT_IPV6)) || (links->m_lpolicy.Valid(P_WANT_IPV6) && links->m_lpolicy.Get(P_WANT_IPV6))) { DialResponse_t dr; dr.status = 0; sprintf(dr.msg, "Attempting to negotiate IPv6"); if (links) { dr.call = links->ifOptions.call; DialReport(&dr); } m_trying_ipv6 = 1; m_ipv6cpProto.Up(); } // m_bacpProto.Up(); min_links = m_lcfg.min_links; max_links = m_lcfg.max_links; rx_drop_bpls = m_lcfg.rx_drop_bpls; rx_raise_bpls = m_lcfg.rx_raise_bpls; tx_drop_bpls = m_lcfg.tx_drop_bpls; tx_raise_bpls = m_lcfg.tx_raise_bpls; redial_interval = m_lcfg.addtime * 100; if (redial_interval < 100) redial_interval = 100; idle_secs = m_lcfg.droptime; is_static = (min_links || max_links); out: DebugVoidReturn; } void CInterface::release_dyn_ips(void) { if (m_lcfg.is_valid) { if (m_lcfg.loc_ip_pool && m_lcfg.loc_ip != 0xffffffffU) { release_ip_address(m_lcfg.loc_ip, (void *)this); m_lcfg.loc_ip = 0xffffffffU; } if (m_lcfg.rem_ip_pool && m_lcfg.rem_ip != 0xffffffffU) { release_ip_address(m_lcfg.rem_ip, (void *)this); m_lcfg.rem_ip = 0xffffffffU; } } } void CInterface::Down() { int sock_fd = m_netns ? m_netns->m_b_sock_fd : b_sock_fd; DebugEnter("CInterface::Down()"); if (is_up || have_ip) { m_ipcpProto.Down(); m_ipv6cpProto.Down(); is_up = 0; have_ip = 0; m_have_ipv6 = 0; m_trying_ipv6 = 0; struct ifreq req; memset(&req, 0, sizeof(req)); GetName(req.ifr_name, sizeof(req.ifr_name), 0); req.ifr_flags = IFF_POINTOPOINT; /* clear IFF_UP */ if (!do_userland_only && 0 != ioctl(sock_fd, SIOCSIFFLAGS, &req)) perror("error setting interface flags"); } DebugVoidReturn; } void CInterface::Close(int status, const char *reason) { DialResponse_t dr; CLink *link; DebugEnter("CInterface::Close()"); dr.status = status; strcpy(dr.msg, reason); if (m_closeReason) free((void*)m_closeReason); m_closeReason = strdup(reason); for (link=links; link; link=link->m_nextBundled) { dr.call = link->ifOptions.call; DialReport(&dr); } m_ipcpProto.Close(reason); m_ipv6cpProto.Close(reason); NetDevDown(this); DebugVoidReturn; } void CInterface::set_min_frags(ctrlfd_t *cfd, unsigned long num) { if (0 != ndev_ioctl(BIOC_SET_MIN_FRAGS, num)) { cfd->perror("BIOC_SET_MIN_FRAGS"); cfd->done(-2); return; } cfd->printf("min_frags %lu\n", num); cfd->done(0); } void CInterface::set_max_frags(ctrlfd_t *cfd, unsigned long num) { if (0 != ndev_ioctl(BIOC_SET_MAX_FRAGS, num)) { cfd->perror("BIOC_SET_MAX_FRAGS"); cfd->done(-2); return; } cfd->printf("max_frags %lu\n", num); cfd->done(0); } int CInterface::get_min_frags(void) { unsigned num = 0; if (ndev_ioctl(BIOC_GET_MIN_FRAGS, (long)&num)) return -1; return num; } int CInterface::get_max_frags(void) { unsigned num = 0; if (ndev_ioctl(BIOC_GET_MAX_FRAGS, (long)&num)) return -1; return num; } void CInterface::GetName(char *str, size_t len, int verbose) { if (verbose && m_netns) { strcpy(str, m_netns->m_name); strcat(str, "^"); str += strlen(str); } if (name_has_ip) { u32 rem_ip = m_lcfg.rem_ip; if (!rem_ip) rem_ip = links->ifOptions.rem_ip; snprintf(str, len, "aps%d.%d.%d", (rem_ip >> 16) & 0xff, (rem_ip >> 8) & 0xff, rem_ip & 0xff); } else { wait_for_ndev(); snprintf(str, len, "aps%d", ndev_id); } } char *CInterface::GetName(int verbose) { static char name[32]; /* FIXME: not reentrant */ GetName(name, sizeof(name), verbose); return name; }