/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* ----------------------------------------------------------------------------- * * Theory of operation : * * this file implements the interface driver for the ppp family * * it's the responsability of the driver to update the statistics * whenever that makes sense * ifnet.if_lastchange = a packet is present, a packet starts to be sent * ifnet.if_ibytes = nb of correct PPP bytes received (does not include escapes...) * ifnet.if_obytes = nb of correct PPP bytes sent (does not include escapes...) * ifnet.if_ipackets = nb of PPP packet received * ifnet.if_opackets = nb of PPP packet sent * ifnet.if_ierrors = nb on input packets in error * ifnet.if_oerrors = nb on ouptut packets in error * ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Includes ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "slcompress.h" #include "ppp_defs.h" // public ppp values #include "if_ppp.h" // public ppp API #include "if_ppplink.h" // public link API #include "ppp_domain.h" #include "ppp_if.h" #include "ppp_ip.h" #include "ppp_ipv6.h" #include "ppp_compress.h" #include "ppp_comp.h" #include "ppp_link.h" /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Forward declarations ----------------------------------------------------------------------------- */ static errno_t ppp_if_output(ifnet_t ifp, mbuf_t m); static void ppp_if_if_free(ifnet_t ifp); static errno_t ppp_if_ioctl(ifnet_t ifp, u_int32_t cmd, void *data); static int ppp_if_demux(ifnet_t ifp, mbuf_t m, char *frame_header, protocol_family_t *protocol_family); static int ppp_if_add_proto(ifnet_t ifp, protocol_family_t protocol_family, const struct ifnet_demux_desc *demux_list, u_int32_t demux_count); static int ppp_if_del_proto(ifnet_t ifp, protocol_family_t protocol_family); static int ppp_if_frameout(ifnet_t ifp, mbuf_t *m0, struct sockaddr *ndest, char *edst, char *ppp_type); static int ppp_if_detach(ifnet_t ifp); static struct ppp_if *ppp_if_findunit(u_short unit); static u_short ppp_if_findfreeunit(); static int ppp_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func); /* ----------------------------------------------------------------------------- Globals ----------------------------------------------------------------------------- */ static TAILQ_HEAD(, ppp_if) ppp_if_head; static lck_grp_attr_t *ppp_if_lck_grp_attr = 0; static lck_attr_t *ppp_if_lck_attr = 0; static lck_grp_t *ppp_if_lck_grp = 0; extern lck_mtx_t *ppp_domain_mutex; /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_init() { TAILQ_INIT(&ppp_if_head); ppp_if_lck_grp_attr = lck_grp_attr_alloc_init(); LOGNULLFAIL(ppp_if_lck_grp_attr, "ppp_if_init: lck_grp_attr_alloc_init failed\n"); lck_grp_attr_setdefault(ppp_if_lck_grp_attr); ppp_if_lck_grp = lck_grp_alloc_init("PPP", ppp_if_lck_grp_attr); LOGNULLFAIL(ppp_if_lck_grp, "ppp_if_init: lck_grp_alloc_init failed\n"); ppp_if_lck_attr = lck_attr_alloc_init(); LOGNULLFAIL(ppp_if_lck_attr, "ppp_if_init: lck_attr_alloc_init failed\n"); lck_attr_setdefault(ppp_if_lck_attr); //lck_attr_setdebug(ppp_if_lck_attr); return 0; fail: if (ppp_if_lck_grp) { lck_grp_free(ppp_if_lck_grp); ppp_if_lck_grp = 0; } if (ppp_if_lck_grp_attr) { lck_grp_attr_free(ppp_if_lck_grp_attr); ppp_if_lck_grp_attr = 0; } if (ppp_if_lck_attr) { lck_attr_free(ppp_if_lck_attr); ppp_if_lck_attr = 0; } return KERN_FAILURE; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_dispose() { // can't dispose if interface are in use if (!TAILQ_EMPTY(&ppp_if_head)) return EBUSY; lck_grp_free(ppp_if_lck_grp); ppp_if_lck_grp = 0; lck_grp_attr_free(ppp_if_lck_grp_attr); ppp_if_lck_grp_attr = 0; lck_attr_free(ppp_if_lck_attr); ppp_if_lck_attr = 0; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_attach(u_short *unit) { int ret = 0; struct ppp_if *wan; struct ifnet_init_params init; struct ifnet_stats_param stats; MALLOC(wan, struct ppp_if *, sizeof(struct ppp_if), M_TEMP, M_WAITOK); if (!wan) return ENOMEM; bzero(wan, sizeof(struct ppp_if)); // check if number requested is already in use if ((*unit != 0xFFFF) && ppp_if_findunit(*unit)) { lck_mtx_unlock(ppp_domain_mutex); ret = EINVAL; goto error; } if (*unit == 0xFFFF) *unit = ppp_if_findfreeunit(); wan->mtx = lck_mtx_alloc_init(ppp_if_lck_grp, ppp_if_lck_attr); if (wan->mtx == 0) { ret = ENOMEM; goto error; } bzero(&init, sizeof(init)); init.name = APPLE_PPP_NAME; init.unit = *unit; init.family = IFNET_FAMILY_PPP; init.type = IFT_PPP; init.output = ppp_if_output; init.demux = ppp_if_demux; init.add_proto = ppp_if_add_proto; init.del_proto = ppp_if_del_proto; init.framer = ppp_if_frameout; init.softc = wan; init.ioctl = ppp_if_ioctl; init.detach = ppp_if_if_free; init.set_bpf_tap = ppp_if_set_bpf_tap; lck_mtx_unlock(ppp_domain_mutex); ret = ifnet_allocate(&init, &wan->net); if (ret) goto error; TAILQ_INIT(&wan->link_head); ifnet_set_hdrlen(wan->net, PPP_HDRLEN); ifnet_set_flags(wan->net, IFF_POINTOPOINT | IFF_MULTICAST, 0xFFFF); // || IFF_RUNNING ifnet_set_mtu(wan->net, PPP_MTU); ifnet_set_baudrate(wan->net, 0 /* 10 Mbits/s ??? */); bzero(&stats, sizeof(stats)); ifnet_set_stat(wan->net, &stats); ifnet_touch_lastchange(wan->net); lck_mtx_lock(ppp_domain_mutex); TAILQ_INSERT_TAIL(&ppp_if_head, wan, next); lck_mtx_unlock(ppp_domain_mutex); ret = ifnet_attach(wan->net, NULL); if (ret) { TAILQ_REMOVE(&ppp_if_head, wan, next); goto error; } bpfattach(wan->net, DLT_PPP, PPP_HDRLEN); // attach network protocols wan->sndq.maxlen = IFQ_MAXLEN; wan->npmode[NP_IP] = NPMODE_ERROR; wan->npmode[NP_IPV6] = NPMODE_ERROR; lck_mtx_lock(ppp_domain_mutex); return 0; error: if (wan->net) ifnet_release(wan->net); if (wan->mtx) lck_mtx_free(wan->mtx, ppp_if_lck_grp); FREE(wan, M_TEMP); lck_mtx_lock(ppp_domain_mutex); return ret; } /* ----------------------------------------------------------------------------- detach ppp interface from dlil layer ----------------------------------------------------------------------------- */ int ppp_if_detach(ifnet_t ifp) { struct ppp_if *wan = ifnet_softc(ifp); int ret; struct ppp_link *link; mbuf_t m; // need to remove all ref to ifnet in link structures TAILQ_FOREACH(link, &wan->link_head, lk_bdl_next) { // do we need a free function ? link->lk_ifnet = 0; } ppp_comp_close(wan); // detach protocols when detaching interface, just in case pppd forgot... lck_mtx_unlock(ppp_domain_mutex); ppp_ipv6_detach(ifp, PF_INET6); ppp_ip_detach(ifp, PF_INET); lck_mtx_lock(ppp_domain_mutex); if (wan->vjcomp) { FREE(wan->vjcomp, M_TEMP); wan->vjcomp = 0; } wan->state |= PPP_IF_STATE_DETACHING; lck_mtx_unlock(ppp_domain_mutex); ret = ifnet_detach(ifp); if (ret) { wan->state &= ~PPP_IF_STATE_DETACHING; lck_mtx_lock(ppp_domain_mutex); return KERN_FAILURE; } lck_mtx_lock(wan->mtx); /* interface release is in progress, wait for callback */ if (wan->state & PPP_IF_STATE_DETACHING) msleep(ifp, wan->mtx, PZERO+1, 0, 0); lck_mtx_unlock(wan->mtx); //sleep(ifp, PZERO+1); lck_mtx_lock(ppp_domain_mutex); do { m = ppp_dequeue(&wan->sndq); mbuf_freem(m); } while (m); lck_mtx_unlock(ppp_domain_mutex); ifnet_release(ifp); lck_mtx_lock(ppp_domain_mutex); TAILQ_REMOVE(&ppp_if_head, wan, next); lck_mtx_free(wan->mtx, ppp_if_lck_grp); FREE(wan, M_TEMP); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_attachclient(u_short unit, void *host, ifnet_t *ifp) { struct ppp_if *wan; wan = ppp_if_findunit(unit); if (!wan) return ENODEV; *ifp = wan->net; if (!wan->host) // don't override the first attachment (use a list ?) wan->host = host; wan->nbclients++; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_if_detachclient(ifnet_t ifp, void *host) { struct ppp_if *wan = ifnet_softc(ifp); if (wan->host) { if (wan->host == host) wan->host = 0; wan->nbclients--; if (!wan->nbclients) ppp_if_detach(ifp); } } /* ----------------------------------------------------------------------------- find a free unit in the interface list ----------------------------------------------------------------------------- */ u_short ppp_if_findfreeunit() { struct ppp_if *wan = TAILQ_FIRST(&ppp_if_head); u_short unit = 0; while (wan) { if (ifnet_unit(wan->net) == unit) { unit++; wan = TAILQ_FIRST(&ppp_if_head); // restart } else wan = TAILQ_NEXT(wan, next); // continue } return unit; } /* ----------------------------------------------------------------------------- find a the unit number in the interface list ----------------------------------------------------------------------------- */ struct ppp_if *ppp_if_findunit(u_short unit) { struct ppp_if *wan; TAILQ_FOREACH(wan, &ppp_if_head, next) { if (ifnet_unit(wan->net) == unit) return wan; } return NULL; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static errno_t ppp_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func) { struct ppp_if *wan = ifnet_softc(ifp); lck_mtx_lock(ppp_domain_mutex); switch (mode) { case BPF_MODE_DISABLED: wan->bpf_input = wan->bpf_output = NULL; break; case BPF_MODE_INPUT: wan->bpf_input = func; break; case BPF_MODE_OUTPUT: wan->bpf_output = func; break; case BPF_MODE_INPUT_OUTPUT: wan->bpf_input = wan->bpf_output = func; break; default: break; } lck_mtx_unlock(ppp_domain_mutex); return 0; } /* ----------------------------------------------------------------------------- called when data are present ----------------------------------------------------------------------------- */ int ppp_if_input(ifnet_t ifp, mbuf_t m, u_int16_t proto, u_int16_t hdrlen) { struct ppp_if *wan = ifnet_softc(ifp); int inlen, vjlen; u_char *iphdr, *p = mbuf_data(m); u_int hlen; int error = ENOMEM; struct timespec tv; struct ifnet_stat_increment_param statsinc; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); mbuf_pkthdr_setheader(m, p); // header point to the protocol header (0x21 or 0x0021) mbuf_adj(m, hdrlen); // the packet points to the real data (0x45) p = mbuf_data(m); if (wan->sc_flags & SC_DECOMP_RUN) { switch (proto) { case PPP_COMP: if (ppp_comp_decompress(wan, &m) != DECOMP_OK) { LOGDBG(ifp, (LOGVAL, "ppp%d: decompression error\n", ifnet_unit(ifp))); goto free; } p = mbuf_data(m); proto = p[0]; hdrlen = 1; if (!(proto & 0x1)) { // lowest bit set for lowest byte of protocol proto = (proto << 8) + p[1]; hdrlen = 2; } mbuf_pkthdr_setheader(m, p);// header point to the protocol header (0x21 or 0x0021) mbuf_adj(m, hdrlen); // the packet points to the real data (0x45) p = mbuf_data(m); break; default: ppp_comp_incompress(wan, m); } } switch (proto) { case PPP_VJC_COMP: case PPP_VJC_UNCOMP: if (!(wan->sc_flags & SC_COMP_TCP)) goto reject; if (!wan->vjcomp) { LOGDBG(ifp, (LOGVAL, "ppp%d: VJ structure not allocated\n", ifnet_unit(ifp))); goto free; } inlen = mbuf_pkthdr_len(m); if (proto == PPP_VJC_COMP) { vjlen = sl_uncompress_tcp_core(p, mbuf_len(m), inlen, TYPE_COMPRESSED_TCP, wan->vjcomp, &iphdr, &hlen); if (vjlen <= 0) { LOGDBG(ifp, (LOGVAL, "ppp%d: VJ uncompress failed on type PPP_VJC_COMP\n", ifnet_unit(ifp))); goto free; } // we must move data in the buffer, to add the uncompressed TCP/IP header... if (mbuf_trailingspace(m) < (hlen - vjlen)) { goto free; } bcopy(p + vjlen, p + hlen, inlen - vjlen); bcopy(iphdr, p, hlen); mbuf_setlen(m, mbuf_len(m) + hlen - vjlen); mbuf_pkthdr_setlen(m, mbuf_pkthdr_len(m) + hlen - vjlen); } else { vjlen = sl_uncompress_tcp_core(p, mbuf_len(m), inlen, TYPE_UNCOMPRESSED_TCP, wan->vjcomp, &iphdr, &hlen); if (vjlen < 0) { LOGDBG(ifp, (LOGVAL, "ppp%d: VJ uncompress failed on type TYPE_UNCOMPRESSED_TCP\n", ifnet_unit(ifp))); goto free; } } *(u_char *)mbuf_pkthdr_header(m) = PPP_IP; // change the protocol, use 1 byte proto = PPP_IP; //no break; case PPP_IP: if (wan->npmode[NP_IP] != NPMODE_PASS) goto reject; if (wan->npafmode[NP_IP] & NPAFMODE_SRC_IN) { if (ppp_ip_af_src_in(ifp, mbuf_data(m))) { error = 0; goto free; } } break; case PPP_IPV6: if (wan->npmode[NP_IPV6] != NPMODE_PASS) goto reject; break; case PPP_CCP: ppp_comp_ccp(wan, m, 1); goto reject; default: goto reject; } // See if bpf wants to look at the packet. if (wan->bpf_input) { if (mbuf_prepend(&m, 4, MBUF_WAITOK) != 0) { bzero(&statsinc, sizeof(statsinc)); statsinc.errors_in = 1; ifnet_stat_increment(ifp, &statsinc); return ENOMEM; } p = mbuf_data(m); *(u_int16_t *)p = htons(0xFF03); *(u_int16_t *)(p + 2) = htons(proto); (*wan->bpf_input)(ifp, m); mbuf_adj(m, 4); } bzero(&statsinc, sizeof(statsinc)); statsinc.packets_in = 1; statsinc.bytes_in = mbuf_pkthdr_len(m); mbuf_pkthdr_setrcvif(m, ifp); nanouptime(&tv); wan->last_recv = tv.tv_sec; lck_mtx_unlock(ppp_domain_mutex); ifnet_input(ifp, m, &statsinc); lck_mtx_lock(ppp_domain_mutex); return 0; reject: // unexpected network protocol, prepend the 2 bytes protocol header expected by pppd if (mbuf_prepend(&m, 2, MBUF_WAITOK) != 0) { bzero(&statsinc, sizeof(statsinc)); statsinc.errors_in = 1; ifnet_stat_increment(ifp, &statsinc); return ENOMEM; } p = mbuf_data(m); *(u_int16_t *)p = htons(proto); ppp_proto_input(wan->host, m); return 0; free: mbuf_freem(m); bzero(&statsinc, sizeof(statsinc)); statsinc.errors_in = 1; ifnet_stat_increment(ifp, &statsinc); return error; } /* ----------------------------------------------------------------------------- This gets called when the interface is freed (if dlil_if_detach has returned DLIL_WAIT_FOR_FREE) ----------------------------------------------------------------------------- */ void ppp_if_if_free(ifnet_t ifp) { struct ppp_if *wan = ifnet_softc(ifp); lck_mtx_lock(wan->mtx); wan->state &= ~PPP_IF_STATE_DETACHING; lck_mtx_unlock(wan->mtx); wakeup(ifp); } /* ----------------------------------------------------------------------------- Process an ioctl request to the ppp interface ----------------------------------------------------------------------------- */ int ppp_if_control(ifnet_t ifp, u_long cmd, void *data) { struct ppp_if *wan = ifnet_softc(ifp); int error = 0, npx; u_int16_t mru, flags16; u_int32_t flags; u_int32_t t; struct npioctl *npi; struct npafioctl *npafi; struct timespec tv; //LOGDBG(ifp, (LOGVAL, "ppp_if_control, (ifnet = %s%d), cmd = 0x%x\n", ifp->if_name, ifp->if_unit, cmd)); lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); switch (cmd) { case PPPIOCSDEBUG: flags = *(int *)data; LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSDEBUG (level = 0x%x)\n", flags)); flags16 = 0; if (flags & 1) flags16 |= IFF_DEBUG; // general purpose debugging if (flags & 2) flags16 |= PPP_LOG_INPKT; // trace all packets in if (flags & 4) flags16 |= PPP_LOG_OUTPKT; // trace all packets out ifnet_set_flags(ifp, flags16, IFF_DEBUG + PPP_LOG_INPKT + PPP_LOG_OUTPKT); break; case PPPIOCGDEBUG: flags16 = ifnet_flags(ifp); flags = 0; if (flags16 & IFF_DEBUG) flags |= 1; if (flags16 & PPP_LOG_INPKT) flags |= 2; if (flags16 & PPP_LOG_OUTPKT) flags |= 4; *(int *)data = flags; LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGDEBUG (level = 0x%x)\n", flags)); break; case PPPIOCSMRU: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSMRU\n")); mru = *(int *)data; wan->mru = mru; break; case PPPIOCSFLAGS: flags = *(int *)data & SC_MASK; LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSFLAGS, old flags = 0x%x new flags = 0x%x, \n", wan->sc_flags, (wan->sc_flags & ~SC_MASK) | flags)); wan->sc_flags = (wan->sc_flags & ~SC_MASK) | flags; break; case PPPIOCGFLAGS: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGFLAGS\n")); *(int *)data = wan->sc_flags; break; case PPPIOCSCOMPRESS: error = ppp_comp_setcompressor(wan, data); break; case PPPIOCGUNIT: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGUNIT\n")); *(int *)data = ifnet_unit(ifp); break; case PPPIOCGIDLE: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGIDLE\n")); nanouptime(&tv); t = tv.tv_sec; ((struct ppp_idle *)data)->xmit_idle = t - wan->last_xmit; ((struct ppp_idle *)data)->recv_idle = t - wan->last_recv; break; case PPPIOCSMAXCID: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSMAXCID\n")); // allocate the vj structure first if (!wan->vjcomp) { MALLOC(wan->vjcomp, struct slcompress *, sizeof(struct slcompress), M_TEMP, M_WAITOK); if (!wan->vjcomp) return ENOMEM; sl_compress_init(wan->vjcomp, -1); } // reeinit the compressor sl_compress_init(wan->vjcomp, *(int *)data); break; case PPPIOCSNPMODE: case PPPIOCGNPMODE: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSNPMODE/PPPIOCGNPMODE\n")); npi = (struct npioctl *) data; switch (npi->protocol) { case PPP_IP: npx = NP_IP; break; case PPP_IPV6: npx = NP_IPV6; break; default: return EINVAL; } if (cmd == PPPIOCGNPMODE) { npi->mode = wan->npmode[npx]; } else { if (npi->mode != wan->npmode[npx]) { wan->npmode[npx] = npi->mode; if (npi->mode != NPMODE_QUEUE) { //ppp_requeue(sc); //(*sc->sc_start)(sc); } } } break; case PPPIOCSNPAFMODE: case PPPIOCGNPAFMODE: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSNPAFMODE/PPPIOCGNPAFMODE\n")); npafi = (struct npafioctl *) data; switch (npafi->protocol) { case PPP_IP: npx = NP_IP; break; case PPP_IPV6: npx = NP_IPV6; break; default: return EINVAL; } if (cmd == PPPIOCGNPMODE) { npafi->mode = wan->npafmode[npx]; } else { wan->npafmode[npx] = npafi->mode; } break; default: LOGDBG(ifp, (LOGVAL, "ppp_if_control: unknown ioctl\n")); error = EINVAL; } return error; } /* ----------------------------------------------------------------------------- Process an ioctl request to the ppp interface ----------------------------------------------------------------------------- */ errno_t ppp_if_ioctl(ifnet_t ifp, u_int32_t cmd, void *data) { //struct ppp_if *wan = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error = 0; struct ppp_stats *psp; struct ifnet_stats_param statspar; //LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, cmd = 0x%x\n", cmd)); lck_mtx_lock(ppp_domain_mutex); switch (cmd) { case SIOCSIFFLAGS: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl: cmd = SIOCSIFFLAGS\n")); // even if this case does nothing, it must be there to return 0 //if ((ifp->if_flags & IFF_RUNNING) == 0) // ifp->if_flags &= ~IFF_UP; break; case SIOCSIFADDR: case SIOCAIFADDR: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl: cmd = SIOCSIFADDR/SIOCAIFADDR\n")); // dlil protocol module already took care of the ioctl break; case SIOCADDMULTI: case SIOCDELMULTI: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl: cmd = SIOCADDMULTI/SIOCDELMULTI\n")); break; case SIOCDIFADDR: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl: cmd = SIOCDIFADDR\n")); break; case SIOCSIFDSTADDR: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl: cmd = SIOCSIFDSTADDR\n")); break; case SIOCGPPPSTATS: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, SIOCGPPPSTATS\n")); psp = &((struct ifpppstatsreq *) data)->stats; bzero(psp, sizeof(*psp)); ifnet_stat(ifp, &statspar); /* XXX ppp pcounters are only 32 bits. need to implement a second ioctl for 64 bits conters */ psp->p.ppp_ibytes = statspar.bytes_in; psp->p.ppp_obytes = statspar.bytes_out; psp->p.ppp_ipackets = statspar.packets_in; psp->p.ppp_opackets = statspar.packets_out; psp->p.ppp_ierrors = statspar.errors_in; psp->p.ppp_oerrors = statspar.errors_out; #if 0 if (sc->sc_comp) { psp->vj.vjs_packets = sc->sc_comp->sls_packets; psp->vj.vjs_compressed = sc->sc_comp->sls_compressed; psp->vj.vjs_searches = sc->sc_comp->sls_searches; psp->vj.vjs_misses = sc->sc_comp->sls_misses; psp->vj.vjs_uncompressedin = sc->sc_comp->sls_uncompressedin; psp->vj.vjs_compressedin = sc->sc_comp->sls_compressedin; psp->vj.vjs_errorin = sc->sc_comp->sls_errorin; psp->vj.vjs_tossed = sc->sc_comp->sls_tossed; } #endif break; case SIOCSIFMTU: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, SIOCSIFMTU\n")); // should we check the minimum MTU for all channels attached to that interface ? if (ifr->ifr_mtu > PPP_MTU) error = EINVAL; else ifnet_set_mtu(ifp, ifr->ifr_mtu); break; default: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, unknown ioctl, cmd = 0x%x\n", cmd)); error = EOPNOTSUPP; } lck_mtx_unlock(ppp_domain_mutex); return error; } /* ----------------------------------------------------------------------------- This gets called at splnet from if_ppp.c at various times when there is data ready to be sent ----------------------------------------------------------------------------- */ errno_t ppp_if_output(ifnet_t ifp, mbuf_t m) { struct ppp_if *wan = ifnet_softc(ifp); int error = 0; u_int16_t proto; enum NPmode mode; enum NPAFmode afmode; char *p; struct timespec tv; struct ifnet_stat_increment_param statsinc; lck_mtx_lock(ppp_domain_mutex); proto = ntohs(*(u_int16_t*)mbuf_data(m)); switch (proto) { case PPP_IP: mode = wan->npmode[NP_IP]; afmode = wan->npafmode[NP_IP]; break; case PPP_IPV6: mode = wan->npmode[NP_IPV6]; afmode = wan->npafmode[NP_IPV6]; break; default: // should never happen, since we attached the protocol ourself error = EAFNOSUPPORT; goto bad; } switch (mode) { case NPMODE_ERROR: error = ENETDOWN; goto bad; case NPMODE_QUEUE: case NPMODE_DROP: error = 0; goto bad; case NPMODE_PASS: break; } if (afmode & NPAFMODE_SRC_OUT) { mbuf_pullup(&m, sizeof(struct ip) + 2); if (m == 0) { bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); return ENOBUFS; } p = mbuf_data(m); p += 2; switch (proto) { case PPP_IP: error = ppp_ip_af_src_out(ifp, p); break; } if (error) { error = 0; goto bad; } } // See if bpf wants to look at the packet. lck_mtx_unlock(ppp_domain_mutex); if (wan->bpf_output) { if (mbuf_prepend(&m, 2, MBUF_WAITOK) != 0) { bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); return ENOBUFS; } *(u_int16_t*)mbuf_data(m) = 0xFF03; (*wan->bpf_output)(ifp, m); mbuf_adj(m, 2); } lck_mtx_lock(ppp_domain_mutex); // Update interface statistics. ifnet_touch_lastchange(ifp); nanouptime(&tv); wan->last_xmit = tv.tv_sec; bzero(&statsinc, sizeof(statsinc)); statsinc.bytes_out = mbuf_pkthdr_len(m) - 2; // don't count protocol header; statsinc.packets_out = 1; ifnet_stat_increment(ifp, &statsinc); if (wan->sc_flags & SC_LOOP_TRAFFIC) { ppp_proto_input(wan->host, m); lck_mtx_unlock(ppp_domain_mutex); return 0; } ppp_if_send(ifp, m); lck_mtx_unlock(ppp_domain_mutex); return 0; bad: mbuf_freem(m); bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); lck_mtx_unlock(ppp_domain_mutex); return error; } /* ----------------------------------------------------------------------------- add protocol function called from dlil when a network protocol is attached for an interface from that family (i.e ip is attached through ppp_attach_ip) ----------------------------------------------------------------------------- */ int ppp_if_add_proto(ifnet_t ifp, protocol_family_t protocol_family, const struct ifnet_demux_desc *demux_list, u_int32_t demux_count) { LOGDBG(ifp, (LOGVAL, "ppp_if_add_proto = %d, ifp = 0x%x\n", protocol_family, ifp)); switch (protocol_family) { case PF_INET: case PF_INET6: break; default: return EINVAL; // happen for unknown protocol, or for empty descriptor } return 0; } /* ----------------------------------------------------------------------------- delete protocol function called from dlil when a network protocol is detached for an interface from that family (i.e ip is attached through ppp_detach_ip) ----------------------------------------------------------------------------- */ int ppp_if_del_proto(ifnet_t ifp, protocol_family_t protocol_family) { LOGDBG(ifp, (LOGVAL, "ppp_if_del_proto, ifp = 0x%x\n", ifp)); return 0; } /* ----------------------------------------------------------------------------- demux function ----------------------------------------------------------------------------- */ int ppp_if_demux(ifnet_t ifp, mbuf_t m, char *frame_header, protocol_family_t *protocol_family) { u_int16_t proto; proto = frame_header[0]; if (!proto & 0x1) { // lowest bit set for lowest byte of protocol proto = (proto << 8) + frame_header[1]; } switch (proto) { case PPP_IP: // We could check pppfam->ip_attached, but dlil will handle case where it isn't *protocol_family = PF_INET; break; case PPP_IPV6: // We could check pppfam->ipv6_attached, but dlil will handle case where it isn't *protocol_family = PF_INET6; break; default : LOGDBG(ifp, (LOGVAL, "ppp_fam_demux, ifp = 0x%x, bad proto = 0x%x\n", ifp, proto)); return ENOENT; // should never happen } return 0; } /* ----------------------------------------------------------------------------- a network packet needs to be send through the interface. add the ppp header to the packet (as a network interface, we only worry about adding our protocol number) ----------------------------------------------------------------------------- */ int ppp_if_frameout(ifnet_t ifp, mbuf_t *m0, struct sockaddr *ndest, char *edst, char *ppp_type) { struct ifnet_stat_increment_param statsinc; if (mbuf_prepend(m0, 2, MBUF_DONTWAIT) != 0) { LOGDBG(ifp, (LOGVAL, "ppp_fam_ifoutput : no memory for transmit header\n")); bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); return EJUSTRETURN; // just return, because the buffer was freed in m_prepend } // place protocol number at the beginning of the mbuf *(u_int16_t*)mbuf_data(*m0) = htons(*(u_int16_t *)ppp_type); return 0; } /* ----------------------------------------------------------------------------- * Connect a PPP channel to a PPP interface unit. ----------------------------------------------------------------------------- */ int ppp_if_attachlink(struct ppp_link *link, int unit) { struct ppp_if *wan; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (link->lk_ifnet) // already attached return EINVAL; wan = ppp_if_findunit(unit); if (!wan) return EINVAL; ifnet_set_flags(wan->net, IFF_RUNNING, IFF_RUNNING); ifnet_set_baudrate(wan->net, ifnet_baudrate(wan->net) + link->lk_baudrate); TAILQ_INSERT_TAIL(&wan->link_head, link, lk_bdl_next); wan->nblinks++; link->lk_ifnet = wan->net; return 0; } /* ----------------------------------------------------------------------------- * Disconnect a channel from its ppp unit. ----------------------------------------------------------------------------- */ int ppp_if_detachlink(struct ppp_link *link) { struct ifnet *ifp = (struct ifnet *)link->lk_ifnet; struct ppp_if *wan; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (!ifp) return EINVAL; // link already detached wan = ifp->if_softc; // check if this is the last link, when multilink is coded ifp->if_flags &= ~IFF_RUNNING; ifp->if_baudrate -= link->lk_baudrate; TAILQ_REMOVE(&wan->link_head, link, lk_bdl_next); wan->nblinks--; link->lk_ifnet = 0; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_send(ifnet_t ifp, mbuf_t m) { struct ppp_if *wan = ifnet_softc(ifp); u_int16_t proto = *(u_int16_t*)mbuf_data(m); // always the 2 first bytes struct ifnet_stat_increment_param statsinc; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (ppp_qfull(&wan->sndq)) { ppp_drop(&wan->sndq); bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); mbuf_freem(m); return ENOBUFS; } switch (proto) { case PPP_IP: // see if we can compress it if ((wan->sc_flags & SC_COMP_TCP) && wan->vjcomp) { mbuf_t mp = m; struct ip *ip = mbuf_data(m) + 2; int vjtype, len; // skip mbuf, in case the ppp header and ip header are not in the same mbuf if (mbuf_len(mp) <= 2) { mp = mbuf_next(mp); if (!mp) break; ip = mbuf_data(m); } // this code assumes the IP/TCP header is in one non-shared mbuf if (ip->ip_p == IPPROTO_TCP) { vjtype = sl_compress_tcp(mp, ip, wan->vjcomp, !(wan->sc_flags & SC_NO_TCP_CCID)); switch (vjtype) { case TYPE_UNCOMPRESSED_TCP: *(u_int16_t*)mbuf_data(m) = PPP_VJC_UNCOMP; // update protocol break; case TYPE_COMPRESSED_TCP: *(u_int16_t*)mbuf_data(m) = PPP_VJC_COMP; // header has moved, update protocol break; } // adjust packet len len = 0; for (mp = m; mp != 0; mp = mbuf_next(mp)) len += mbuf_len(mp); mbuf_pkthdr_setlen(m, len); } } break; case PPP_CCP: mbuf_adj(m, 2); ppp_comp_ccp(wan, m, 0); mbuf_prepend(&m, 2, MBUF_DONTWAIT); break; } if (wan->sc_flags & SC_COMP_RUN) { if (ppp_comp_compress(wan, &m) == COMP_OK) { if (mbuf_prepend(&m, 2, MBUF_DONTWAIT) != 0) { bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); return ENOBUFS; } *(u_int16_t*)mbuf_data(m) = PPP_COMP; // update protocol } } if (wan->sndq.len) { ppp_enqueue(&wan->sndq, m); } else ppp_if_xmit(ifp, m); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_xmit(ifnet_t ifp, mbuf_t m) { struct ppp_if *wan = ifnet_softc(ifp); struct ppp_link *link; int err, len; struct ifnet_stat_increment_param statsinc; lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); if (m == 0) m = ppp_dequeue(&wan->sndq); while (m) { link = TAILQ_FIRST(&wan->link_head); if (link == 0) { LOGDBG(ifp, (LOGVAL, "ppp%d: Trying to send data with link detached\n", ifnet_unit(ifp))); // just flush everything ifnet_touch_lastchange(ifp); while (m) { bzero(&statsinc, sizeof(statsinc)); statsinc.errors_out = 1; ifnet_stat_increment(ifp, &statsinc); mbuf_freem(m); m = ppp_dequeue(&wan->sndq); }; return 1; } if (link->lk_flags & SC_HOLD) { // should try next link mbuf_freem(m); m = ppp_dequeue(&wan->sndq); continue; } if (link->lk_flags & (SC_XMIT_BUSY | SC_XMIT_FULL)) { // should try next link ppp_prepend(&wan->sndq, m); return 0; } // get the len before we send the packet, // we can not assume the state of the mbuf when we return len = mbuf_len(m); // since we tested the lk_flags, ppp_link_send should not failed // except if there is a dramatic error link->lk_flags |= SC_XMIT_BUSY; err = ppp_link_send(link, m); link->lk_flags &= ~SC_XMIT_BUSY; if (err) { // packet has been freed by link lower layer return err; } m = ppp_dequeue(&wan->sndq); } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_if_error(ifnet_t ifp) { struct ppp_if *wan = ifnet_softc(ifp); lck_mtx_assert(ppp_domain_mutex, LCK_MTX_ASSERT_OWNED); // reset vj compression if (wan->vjcomp) { sl_uncompress_tcp(NULL, 0, TYPE_ERROR, wan->vjcomp); } }