/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 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 "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_link.h" /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ /* * Network protocols we support. */ #define NP_IP 0 /* Internet Protocol V4 */ //#define NP_IPV6 1 /* Internet Protocol V6 */ //#define NP_IPX 2 /* IPX protocol */ //#define NP_AT 3 /* Appletalk protocol */ #define NUM_NP 1 /* Number of NPs. */ struct ppp_if { /* first, the ifnet structure... */ struct ifnet net; /* network-visible interface */ /* administrative info */ TAILQ_ENTRY(ppp_if) next; void *host; /* first client structure */ u_int8_t nbclients; /* nb clients attached */ u_int8_t in_use; /* is this interface currently in use ? */ /* ppp data */ u_int16_t mru; /* max receive unit */ TAILQ_HEAD(, ppp_link) link_head; /* list of links attached to this interface */ u_int8_t nblinks; /* # links currently attached */ struct mbuf *outm; /* mbuf currently being output */ time_t last_xmit; /* last proto packet sent on this interface */ time_t last_recv; /* last proto packet received on this interface */ u_int32_t sc_flags; /* ppp private flags */ struct slcompress *vjcomp; /* vjc control buffer */ enum NPmode npmode[NUM_NP]; /* what to do with each net proto */ }; /* * Bits in sc_flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC, * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP. */ #define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \ |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ \ |SC_COMP_TCP|SC_REJ_COMP_TCP) /* ----------------------------------------------------------------------------- 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_xmit(struct ifnet *ifp); 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() { struct ppp_if *wan; // can't dispose if interface are in use TAILQ_FOREACH(wan, &ppp_if_head, next) if (wan->in_use) return EBUSY; while (wan = TAILQ_FIRST(&ppp_if_head)) { TAILQ_REMOVE(&ppp_if_head, wan, next); FREE(wan, M_DEVBUF); } return 0; } /* ----------------------------------------------------------------------------- find an unused ifnet in the list of interfaces ----------------------------------------------------------------------------- */ struct ppp_if *ppp_if_findunused() { struct ppp_if *wan; TAILQ_FOREACH(wan, &ppp_if_head, next) if (!wan->in_use) return wan; return 0; } /* ----------------------------------------------------------------------------- detach pppoe interface dlil layer ----------------------------------------------------------------------------- */ int ppp_if_attach(u_short *unit) { int ret; struct ppp_if *wan; // check if number requested is already in use if ((*unit != 0xFFFF) && ppp_if_findunit(*unit)) return EINVAL; wan = ppp_if_findunused(); if (wan) // reinit the structure TAILQ_REMOVE(&ppp_if_head, wan, next); else // alloc a new structure MALLOC(wan, struct ppp_if *, sizeof(struct ppp_if), M_DEVBUF, M_WAITOK); if (!wan) return ENOMEM; bzero(wan, sizeof(struct ppp_if)); // it's time now to register our brand new channel wan->net.if_name = APPLE_PPP_NAME; wan->net.if_family = APPLE_IF_FAM_PPP; wan->net.if_mtu = PPP_MTU; wan->net.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; // || IFF_RUNNING wan->net.if_type = IFT_PPP; wan->net.if_hdrlen = PPP_HDRLEN; wan->net.if_ioctl = ppp_if_ioctl; wan->net.if_output = ppp_if_output; wan->net.if_free = ppp_if_if_free; wan->net.if_baudrate = 0; // 10 Mbits/s ??? getmicrotime(&wan->net.if_lastchange); TAILQ_INIT(&wan->link_head); wan->net.if_unit = *unit != 0xFFFF ? *unit : ppp_if_findfreeunit(); wan->in_use = 1; *unit = wan->net.if_unit; ret = dlil_if_attach(&wan->net); TAILQ_INSERT_TAIL(&ppp_if_head, wan, next); bpfattach(&wan->net, DLT_PPP, PPP_HDRLEN); return 0; } /* ----------------------------------------------------------------------------- detach ppp interface from dlil layer ----------------------------------------------------------------------------- */ int ppp_if_detach(struct ifnet *ifp) { struct ppp_if *wan = (struct ppp_if *)ifp; int ret; struct ppp_link *link; // 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_ip_detach(ifp); if (wan->vjcomp) { FREE(wan->vjcomp, M_DEVBUF); 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; } // don't free the interface wan->in_use = 0; // TAILQ_REMOVE(&ppp_if_head, wan, next); // FREE(ifp, M_DEVBUF); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_attachclient(u_short unit, void *host, struct ifnet **ifp) { struct ppp_if *wan; TAILQ_FOREACH(wan, &ppp_if_head, next) { if (wan->in_use && wan->net.if_unit == unit) { *ifp = (struct ifnet *)wan; if (!wan->host) // don't override the first attachment (use a list ?) wan->host = host; wan->nbclients++; return 0; } } return ENODEV; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_if_detachclient(struct ifnet *ifp, void *host) { struct ppp_if *wan = (struct ppp_if *)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 (wan->in_use && 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->in_use && wan->net.if_unit == unit) return wan; } return NULL; } /* ----------------------------------------------------------------------------- called from pppoe_rfc 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; int inlen, vjlen; u_char *iphdr, *p = mtod(m, u_char *); u_int hlen; 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 *); 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)); m_free(m); return 1; } 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)); m_free(m); return 1; } // we must move data in the buffer, to add the uncompressed TCP/IP header... if (M_TRAILINGSPACE(m) < (hlen - vjlen)) { m_free(m); return 1; } 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)); m_free(m); return 1; } } *(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; break; default: goto reject; } // See if bpf wants to look at the packet. if (ifp->if_bpf && (M_LEADINGSPACE(m) >= 4)) { M_PREPEND(m, 4, M_WAIT); p = mtod(m, u_char *); *(u_int16_t *)p = 0xFF03; *(u_int16_t *)(p + 2) = proto; bpf_mtap(ifp, m); m_adj(m, 4); } ifp->if_ipackets++; ifp->if_ibytes += m->m_pkthdr.len; // don't count the protocol bytes in the stats getmicrotime(&ifp->if_lastchange); m->m_pkthdr.rcvif = ifp; 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; } /* ----------------------------------------------------------------------------- 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; int error = 0, npx; u_int16_t mru; u_int32_t flags; time_t t; struct npioctl *npi; //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: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCSFLAGS\n")); flags = *(int *)data & SC_MASK; 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; #if PPP_COMPRESS case PPPIOCSCOMPRESS: err = ppp_set_compress(ppp, arg); break; #endif case PPPIOCGUNIT: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGUNIT\n")); *(int *)data = wan->net.if_unit; break; case PPPIOCGIDLE: LOGDBG(ifp, (LOGVAL, "ppp_if_control: PPPIOCGIDLE\n")); t = time_second; ((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_DEVBUF, M_NOWAIT); 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; 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; 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; struct ifreq *ifr = (struct ifreq *)data; struct ifaddr *ifa = (struct ifaddr *)data; int error = 0; struct ppp_stats *psp; struct in_ifaddr *ia = (struct in_ifaddr *)data; //LOGDBG(ifp, (LOGVAL, "ppp_if_ioctl, cmd = 0x%x\n", cmd)); if (!wan->in_use) return EFAULT; 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")); switch(ifa->ifa_addr->sa_family) { case AF_INET: // plumb now the ip protocol error = ppp_ip_attach(ifp, &ia->ia_ifa.ifa_dlt); break; default: error = EAFNOSUPPORT; break; } 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 = EINVAL; } 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; int error; u_int16_t proto; enum NPmode mode; if (!wan->in_use) { error = EFAULT; goto bad; } proto = *mtod(m, u_short *); switch (proto) { case PPP_IP: mode = wan->npmode[NP_IP]; 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; } // See if bpf wants to look at the packet. if (ifp->if_bpf && (M_LEADINGSPACE(m) >= 2)) { M_PREPEND(m, 2, M_WAIT); *mtod(m, u_int16_t *) = 0xFF03; bpf_mtap(ifp, m); m_adj(m, 2); } 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); 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 = (struct ifnet *)wan; return 0; } /* ----------------------------------------------------------------------------- * Disconnect a channel from its ppp unit. ----------------------------------------------------------------------------- */ int ppp_if_detachlink(struct ppp_link *link) { struct ppp_if *wan = (struct ppp_if *)link->lk_ifnet; if (!wan) return EINVAL; // link already detached // check if this is the last link, when multilink is coded wan->net.if_flags &= ~IFF_RUNNING; wan->net.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; u_int16_t proto = *mtod(m, u_int16_t *); // always the 2 first bytes // if there is already a packet in the queue, just enqueue the new one if (wan->outm) { if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); ifp->if_oerrors++; return ENOBUFS; } IF_ENQUEUE(&ifp->if_snd, m); return 0; } wan->outm = m; 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; // 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; } } } break; } ppp_if_xmit(ifp); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_if_xmit(struct ifnet *ifp) { struct ppp_if *wan = (struct ppp_if *)ifp; struct ppp_link *link; int err; if (!wan->outm) IF_DEQUEUE(&ifp->if_snd, wan->outm); while (wan->outm) { link = TAILQ_FIRST(&wan->link_head); if (link->lk_state & PPP_LINK_STATE_XMIT_FULL) { // should try next link return 0; } // since we tested the lk_state, ppp_link_send should not failed // except if there is a dramatic error err = ppp_link_send(link, wan->outm); if (err) return err; // Update interface statistics. getmicrotime(&ifp->if_lastchange); wan->last_xmit = time_second; ifp->if_opackets++; ifp->if_obytes += wan->outm->m_pkthdr.len - 2; // remove the 2 protocol bytes from the stats IF_DEQUEUE(&ifp->if_snd, wan->outm); } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_if_linkerror(struct ppp_link *link) { struct ppp_if *wan = (struct ppp_if *)link->lk_ifnet; wan->net.if_ierrors++; // reset vj compression if (wan->vjcomp) { sl_uncompress_tcp(NULL, 0, TYPE_ERROR, wan->vjcomp); } }