/* * 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_if.h" #include "ppp_domain.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 int ppp_if_output(struct ifnet *ifp, struct mbuf *m); static int ppp_if_if_free(struct ifnet *ifp); static int ppp_if_ioctl(struct ifnet *ifp, u_long cmd, void *data); static int ppp_if_detach(struct ifnet *ifp); static struct ppp_if *ppp_if_findunit(u_short unit); static u_short ppp_if_findfreeunit(); /* ----------------------------------------------------------------------------- Globals ----------------------------------------------------------------------------- */ static TAILQ_HEAD(, ppp_if) ppp_if_head; /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_init() { TAILQ_INIT(&ppp_if_head); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_dispose() { // can't dispose if interface are in use if (!TAILQ_EMPTY(&ppp_if_head)) return EBUSY; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_attach(u_short *unit) { int ret = 0; struct ppp_if *wan; struct ifnet *ifp; MALLOC(wan, struct ppp_if *, sizeof(struct ppp_if), M_TEMP, M_WAITOK); if (!wan) return ENOMEM; bzero(wan, sizeof(struct ppp_if)); ret = dlil_if_acquire(APPLE_IF_FAM_PPP, 0, 0, &wan->net); if (ret) goto error; // check if number requested is already in use if ((*unit != 0xFFFF) && ppp_if_findunit(*unit)) { ret = EINVAL; goto error; } // it's time now to register our brand new channel ifp = wan->net; ifp->if_softc = wan; ifp->if_name = APPLE_PPP_NAME; ifp->if_family = APPLE_IF_FAM_PPP; ifp->if_mtu = PPP_MTU; ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; // || IFF_RUNNING ifp->if_type = IFT_PPP; ifp->if_hdrlen = PPP_HDRLEN; ifp->if_ioctl = ppp_if_ioctl; ifp->if_output = ppp_if_output; ifp->if_free = ppp_if_if_free; ifp->if_baudrate = 0; // 10 Mbits/s ??? ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; getmicrotime(&ifp->if_lastchange); ifp->if_ibytes = ifp->if_obytes = 0; ifp->if_ipackets = ifp->if_opackets = 0; ifp->if_ierrors = ifp->if_oerrors = 0; TAILQ_INIT(&wan->link_head); ifp->if_unit = *unit != 0xFFFF ? *unit : ppp_if_findfreeunit(); *unit = ifp->if_unit; ret = dlil_if_attach(ifp); if (ret) goto error; TAILQ_INSERT_TAIL(&ppp_if_head, wan, next); bpfattach(ifp, DLT_PPP, PPP_HDRLEN); // attach network protocols wan->npmode[NP_IP] = NPMODE_ERROR; wan->npmode[NP_IPV6] = NPMODE_ERROR; return 0; error: if (wan->net) dlil_if_release(wan->net); FREE(wan, M_TEMP); return ret; } /* ----------------------------------------------------------------------------- detach ppp interface from dlil layer ----------------------------------------------------------------------------- */ int ppp_if_detach(struct ifnet *ifp) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; int ret; struct ppp_link *link; struct mbuf *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... ppp_ipv6_detach(ifp, 0 /* not used */); ppp_ip_detach(ifp, 0 /* not used */); if (wan->vjcomp) { FREE(wan->vjcomp, M_TEMP); wan->vjcomp = 0; } ret = dlil_if_detach(ifp); switch (ret) { case 0: break; case DLIL_WAIT_FOR_FREE: sleep(ifp, PZERO+1); break; default: return KERN_FAILURE; } do { IF_DEQUEUE(&ifp->if_snd, m); m_freem(m); } while (m); ifp->if_softc = 0; dlil_if_release(ifp); TAILQ_REMOVE(&ppp_if_head, wan, next); FREE(wan, M_TEMP); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_attachclient(u_short unit, void *host, struct ifnet **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(struct ifnet *ifp, void *host) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; 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 (wan->net->if_unit == 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 (wan->net->if_unit == unit) return wan; } return NULL; } /* ----------------------------------------------------------------------------- called when data are present ----------------------------------------------------------------------------- */ int ppp_if_input(struct ifnet *ifp, struct mbuf *m, u_int16_t proto, u_int16_t hdrlen) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; int inlen, vjlen; u_char *iphdr, *p = mtod(m, u_char *); u_int hlen; int error = ENOMEM; m->m_pkthdr.header = p; // header point to the protocol header (0x21 or 0x0021) m_adj(m, hdrlen); // the packet points to the real data (0x45) p = mtod(m, u_char *); 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", ifp->if_unit)); goto free; } p = mtod(m, u_char *); proto = p[0]; hdrlen = 1; if (!(proto & 0x1)) { // lowest bit set for lowest byte of protocol proto = (proto << 8) + p[1]; hdrlen = 2; } m->m_pkthdr.header = p;// header point to the protocol header (0x21 or 0x0021) m_adj(m, hdrlen); // the packet points to the real data (0x45) p = mtod(m, u_char *); 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", ifp->if_unit)); goto free; } inlen = m->m_pkthdr.len; if (proto == PPP_VJC_COMP) { vjlen = sl_uncompress_tcp_core(p, m->m_len, 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", ifp->if_unit)); goto free; } // we must move data in the buffer, to add the uncompressed TCP/IP header... if (M_TRAILINGSPACE(m) < (hlen - vjlen)) { goto free; } bcopy(p + vjlen, p + hlen, inlen - vjlen); bcopy(iphdr, p, hlen); m->m_len += hlen - vjlen; m->m_pkthdr.len += hlen - vjlen; } else { vjlen = sl_uncompress_tcp_core(p, m->m_len, 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", ifp->if_unit)); goto free; } } *(u_char *)m->m_pkthdr.header = 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, mtod(m, char *))) { 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 (ifp->if_bpf) { M_PREPEND(m, 4, M_WAIT); if (m == 0) { ifp->if_ierrors++; return ENOMEM; } p = mtod(m, u_char *); *(u_int16_t *)p = 0xFF03; *(u_int16_t *)(p + 2) = proto; bpf_mtap(ifp, m); m_adj(m, 4); } getmicrotime(&ifp->if_lastchange); ifp->if_ibytes += m->m_pkthdr.len; ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; wan->last_recv = clock_get_system_value().tv_sec; dlil_input(ifp, m, m); return 0; reject: // unexpected network protocol M_PREPEND(m, hdrlen, M_DONTWAIT); // just reput the header before to send it to pppd ppp_proto_input(wan->host, m); return 0; free: m_free(m); ifp->if_ierrors++; return error; } /* ----------------------------------------------------------------------------- This gets called when the interface is freed (if dlil_if_detach has returned DLIL_WAIT_FOR_FREE) ----------------------------------------------------------------------------- */ int ppp_if_if_free(struct ifnet *ifp) { wakeup(ifp); return 0; } /* ----------------------------------------------------------------------------- Process an ioctl request to the ppp interface ----------------------------------------------------------------------------- */ int ppp_if_control(struct ifnet *ifp, u_long cmd, void *data) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; int error = 0, npx; u_int16_t mru; u_int32_t flags; u_int32_t t; struct npioctl *npi; struct npafioctl *npafi; //LOGDBG(ifp, (LOGVAL, "ppp_if_control, (ifnet = %s%d), cmd = 0x%x\n", ifp->if_name, ifp->if_unit, cmd)); switch (cmd) { case PPPIOCSDEBUG: flags = *(int *)data; LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSDEBUG (level = 0x%x)\n", flags)); ifp->if_flags &= ~(IFF_DEBUG + PPP_LOG_INPKT + PPP_LOG_OUTPKT); if (flags & 1) ifp->if_flags |= IFF_DEBUG; // general purpose debugging if (flags & 2) ifp->if_flags |= PPP_LOG_INPKT; // trace all packets in if (flags & 4) ifp->if_flags |= PPP_LOG_OUTPKT; // trace all packets out break; case PPPIOCGDEBUG: flags = 0; if (ifp->if_flags & IFF_DEBUG) flags |= 1; if (ifp->if_flags & PPP_LOG_INPKT) flags |= 2; if (ifp->if_flags & 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 = ifp->if_unit; break; case PPPIOCGIDLE: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGIDLE\n")); t = clock_get_system_value().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 ----------------------------------------------------------------------------- */ int ppp_if_ioctl(struct ifnet *ifp, u_long cmd, void *data) { //struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int error = 0; struct ppp_stats *psp; //LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, cmd = 0x%x\n", cmd)); 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)); psp->p.ppp_ibytes = ifp->if_ibytes; psp->p.ppp_obytes = ifp->if_obytes; psp->p.ppp_ipackets = ifp->if_ipackets; psp->p.ppp_opackets = ifp->if_opackets; psp->p.ppp_ierrors = ifp->if_ierrors; psp->p.ppp_oerrors = ifp->if_oerrors; #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 ifp->if_mtu = ifr->ifr_mtu; break; default: LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, unknown ioctl, cmd = 0x%x\n", cmd)); error = EOPNOTSUPP; } return error; } /* ----------------------------------------------------------------------------- This gets called at splnet from if_ppp.c at various times when there is data ready to be sent ----------------------------------------------------------------------------- */ int ppp_if_output(struct ifnet *ifp, struct mbuf *m) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; int error; u_int16_t proto; enum NPmode mode; enum NPAFmode afmode; char *p; proto = ntohs(*mtod(m, u_short *)); 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) { p = mtod(m, char *); 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. if (ifp->if_bpf) { M_PREPEND(m, 2, M_WAIT); if (m == 0) { ifp->if_oerrors++; return ENOMEM; } *mtod(m, u_int16_t *) = 0xFF03; bpf_mtap(ifp, m); m_adj(m, 2); } // Update interface statistics. getmicrotime(&ifp->if_lastchange); wan->last_xmit = clock_get_system_value().tv_sec; ifp->if_obytes += m->m_pkthdr.len - 2; // don't count protocol header ifp->if_opackets++; if (wan->sc_flags & SC_LOOP_TRAFFIC) { ppp_proto_input(wan->host, m); return 0; } ppp_if_send(ifp, m); return 0; bad: m_freem(m); ifp->if_oerrors++; return error; } /* ----------------------------------------------------------------------------- * Connect a PPP channel to a PPP interface unit. ----------------------------------------------------------------------------- */ int ppp_if_attachlink(struct ppp_link *link, int unit) { struct ppp_if *wan; if (link->lk_ifnet) // already attached return EINVAL; wan = ppp_if_findunit(unit); if (!wan) return EINVAL; wan->net->if_flags |= IFF_RUNNING; wan->net->if_baudrate += 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; if (!ifp) return EINVAL; // link already detached wan = (struct ppp_if *)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(struct ifnet *ifp, struct mbuf *m) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; u_int16_t proto = *mtod(m, u_int16_t *); // always the 2 first bytes if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); ifp->if_oerrors++; m_freem(m); return ENOBUFS; } switch (proto) { case PPP_IP: // see if we can compress it if ((wan->sc_flags & SC_COMP_TCP) && wan->vjcomp) { struct mbuf *mp = m; struct ip *ip = (struct ip *) (mtod(m, u_char *) + 2); int vjtype, len; // skip mbuf, in case the ppp header and ip header are not in the same mbuf if (mp->m_len <= 2) { mp = mp->m_next; if (!mp) break; ip = mtod(mp, struct ip *); } // 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: *mtod(m, u_int16_t *) = PPP_VJC_UNCOMP; // update protocol break; case TYPE_COMPRESSED_TCP: *mtod(m, u_int16_t *) = PPP_VJC_COMP; // header has moved, update protocol break; } // adjust packet len len = 0; for (mp = m; mp != 0; mp = mp->m_next) len += mp->m_len; m->m_pkthdr.len = len; } } break; case PPP_CCP: m_adj(m, 2); ppp_comp_ccp(wan, m, 0); M_PREPEND(m, 2, M_DONTWAIT); break; } if (wan->sc_flags & SC_COMP_RUN) { if (ppp_comp_compress(wan, &m) == COMP_OK) { M_PREPEND(m, 2, M_DONTWAIT); if (m == 0) { ifp->if_oerrors++; return ENOBUFS; } *mtod(m, u_int16_t *) = PPP_COMP; // update protocol } } if (ifp->if_snd.ifq_len) { IF_ENQUEUE(&ifp->if_snd, m); } else ppp_if_xmit(ifp, m); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_xmit(struct ifnet *ifp, struct mbuf *m) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; struct ppp_link *link; int err, len; if (m == 0) IF_DEQUEUE(&ifp->if_snd, m); while (m) { link = TAILQ_FIRST(&wan->link_head); if (link == 0) { LOGDBG(ifp, (LOGVAL, "ppp%d: Trying to send data with link detached\n", ifp->if_unit)); // just flush everything getmicrotime(&ifp->if_lastchange); while (m) { ifp->if_oerrors++; m_freem(m); IF_DEQUEUE(&ifp->if_snd, m); }; return 1; } if (link->lk_flags & SC_HOLD) { // should try next link m_freem(m); IF_DEQUEUE(&ifp->if_snd, m); continue; } if (link->lk_flags & (SC_XMIT_BUSY | SC_XMIT_FULL)) { // should try next link IF_PREPEND(&ifp->if_snd, m); return 0; } // get the len before we send the packet, // we can not assume the state of the mbuf when we return len = m->m_pkthdr.len; // 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; } IF_DEQUEUE(&ifp->if_snd, m); } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_if_error(struct ifnet *ifp) { struct ppp_if *wan = (struct ppp_if *)ifp->if_softc; // reset vj compression if (wan->vjcomp) { sl_uncompress_tcp(NULL, 0, TYPE_ERROR, wan->vjcomp); } }