2 ppp_if.c - Streams PPP interface module
4 top level module handles if_ and packetizing PPP packets.
6 Copyright (C) 1990 Brad K. Clements, All Rights Reserved
7 See copyright notice in NOTES
9 $Id: ppp_if.c,v 1.1 1995/12/18 23:45:11 paulus Exp $
13 * This file is used under SunOS 4.x, and OSF/1 on DEC Alpha.
15 * Beware that under OSF/1, the ioctl constants (SIOC*) end up
16 * as 64-bit (long) values, so an ioctl constant should be cast to
17 * int (32 bits) before being compared with the ioc_cmd field of
18 * an iocblk structure.
22 #include <sys/types.h>
30 #define PPP_STATS 1 /* keep statistics */
31 #define DEBUGS 1 /* include debug code */
35 #define NOTSUSER (suser(u.u_procp->p_rcred, &u.u_acflag))
36 #define IFA_ADDR(ifa) (*(ifa)->ifa_addr)
37 #define ifr_mtu ifr_metric
38 #define SIOCSIFMTU SIOCSIPMTU
39 #define SIOCGIFMTU SIOCRIPMTU
43 #define PPP_SNIT 1 /* support Streams Network Interface Tap */
44 #define NOTSUSER (!suser())
47 #define m_data m_off /* well almost */
48 #define IFA_ADDR(ifa) ((ifa)->ifa_addr)
51 #include <sys/param.h>
52 #include <sys/stream.h>
53 #include <sys/stropts.h>
56 #include <sys/systm.h>
58 #include <sys/socket.h>
59 #include <sys/errno.h>
60 #include <sys/ioctl.h>
64 #include <net/route.h>
65 #include <net/netisr.h>
66 #include <netinet/in.h>
67 #include <netinet/in_systm.h>
68 #include <netinet/in_var.h>
69 #include <netinet/ip.h>
71 #include <net/ppp_defs.h>
72 #include <net/ppp_str.h>
75 #include <net/vjcompress.h>
79 #define INCR(comp) ++p->pii_stats.comp
84 #define MAX_PKTSIZE 4096 /* max packet size including framing */
85 #define PPP_FRAMING 6 /* 4-byte header + 2-byte FCS */
86 #define MAX_IPHDR 128 /* max TCP/IP header size */
87 #define MAX_VJHDR 20 /* max VJ compressed header size (?) */
90 * Network protocols we support.
93 #define NUM_NP 1 /* # protocols supported */
96 * Structure used within the ppp_if streams module.
100 struct ifnet pii_ifnet;
101 queue_t *pii_writeq; /* used by ppp_output */
102 enum NPmode pii_npmode[NUM_NP];
103 mblk_t *pii_npq; /* list of packets queued up */
104 mblk_t **pii_npq_tail;
106 struct vjcompress pii_sc_comp; /* vjc control buffer */
109 struct pppstat pii_stats;
111 struct ppp_comp_stats pii_cstats;
115 * Values for pii_flags.
117 #define PII_FLAGS_INUSE 0x1 /* in use by a stream */
118 #define PII_FLAGS_ATTACHED 0x8 /* already if_attached */
119 #define PII_FLAGS_VJC_ON 0x10 /* VJ TCP header compression enabled */
120 #define PII_FLAGS_VJC_NOCCID 0x20 /* VJ: don't compress conn. id */
121 #define PII_FLAGS_VJC_REJ 0x40 /* receive: reject VJ comp */
122 #define PII_FLAGS_DEBUG 0x80 /* enable debug printout */
125 #include <sys/syslog.h>
126 #define DLOG(s,a) if (p->pii_flags&PII_FLAGS_DEBUG) log(LOG_DEBUG, s, a)
132 #include <net/nit_if.h>
133 #include <netinet/if_ether.h>
134 /* Use a fake link level header to make etherfind and tcpdump happy. */
135 static struct ether_header header = {{1}, {2}, ETHERTYPE_IP};
136 static struct nit_if nif = {(caddr_t)&header, sizeof(header), 0, 0};
139 static int ppp_if_open(), ppp_if_close(), ppp_if_rput(), ppp_if_wput(),
140 ppp_if_wsrv(), ppp_if_rsrv();
142 static struct module_info minfo ={
143 0xbad,"ppp_if",0, INFPSZ, 16384, 4096
146 static struct qinit r_init = {
147 ppp_if_rput, ppp_if_rsrv, ppp_if_open, ppp_if_close, NULL, &minfo, NULL
149 static struct qinit w_init = {
150 ppp_if_wput, ppp_if_wsrv, ppp_if_open, ppp_if_close, NULL, &minfo, NULL
152 struct streamtab ppp_ifinfo = {
153 &r_init, &w_init, NULL, NULL,
156 typedef struct ppp_if_info PII;
160 int ppp_output(), ppp_ioctl();
161 static void if_release_addrs(), if_delete_route();
167 register struct ifnet *ifp;
169 for (unit = 0; unit < NPPP; ++unit) {
170 if (pii[unit].pii_flags & PII_FLAGS_ATTACHED)
172 ifp = &pii[unit].pii_ifnet;
174 ifp->if_name = "ppp";
175 ifp->if_mtu = PPP_MTU;
176 ifp->if_flags = IFF_POINTOPOINT;
178 ifp->if_ioctl = ppp_ioctl;
179 ifp->if_output = ppp_output;
181 ifp->if_version = "2.2";
182 ifp->if_mediamtu = PPP_MTU;
183 ifp->if_start = NULL;
185 ifp->if_type = 0x1d; /* IFT_PPP snagged from BSD -- is this ok? */
186 ifp->if_hdrlen = PPP_HDRLEN;
188 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
190 pii[unit].pii_flags |= PII_FLAGS_ATTACHED;
199 register struct ifnet *ifp;
200 register struct ifnet **p;
203 for (unit = 0; unit < NPPP; ++unit) {
204 if (!(pii[unit].pii_flags & PII_FLAGS_ATTACHED))
206 ifp = &pii[unit].pii_ifnet;
208 /* remove interface from interface list */
210 for (p = &ifnet; *p; p = &((*p)->if_next)) {
214 /* mark it down and flush it's que */
217 /* free any addresses hanging off the intf */
218 if_release_addrs(ifp);
220 pii[unit].pii_flags &= ~PII_FLAGS_ATTACHED;
231 if_release_addrs(ifp)
232 register struct ifnet *ifp;
234 register struct in_ifaddr **addr;
235 register struct ifaddr *ifa, *ifanxt;
238 if_delete_route(ifp);
240 for (addr = &in_ifaddr; *addr; ) {
241 if ((*addr)->ia_ifp == ifp)
242 *addr = (*addr)->ia_next;
244 addr = &((*addr)->ia_next);
248 * Free all mbufs holding down this interface's address(es).
250 for (ifa = ifp->if_addrlist; ifa; ifa = ifanxt) {
251 ifanxt = ifa->ifa_next;
254 ifp->if_addrlist = 0;
258 * Delete routes to the specified interface.
259 * Hacked from rtrequest().
265 extern int rttrash; /* routes not in table but not freed */
266 register struct mbuf **mprev, *m;
267 register struct rtentry *route;
270 /* search host rt tbl */
271 for (i = 0; i < RTHASHSIZ; i++) {
274 route = mtod(m, struct rtentry *);
275 if (route->rt_ifp == ifp) {
277 if (route->rt_refcnt > 0) {
278 route->rt_flags &= ~RTF_UP;
289 /* search net rt tbl */
290 for (i = 0; i < RTHASHSIZ; i++) {
293 route = mtod(m, struct rtentry *);
294 if (route->rt_ifp == ifp) {
296 if (route->rt_refcnt > 0) {
297 route->rt_flags &= ~RTF_UP;
314 for (x = 0; x < NPPP; x++) {
315 if (pii[x].pii_flags & PII_FLAGS_INUSE)
328 for (x = 0; x < NPPP; x++)
329 if (!(pii[x].pii_flags & PII_FLAGS_INUSE))
331 if (x == NPPP) { /* all buffers in use */
332 splx(s); /* restore processor state */
336 p->pii_flags |= PII_FLAGS_INUSE;
351 vj_compress_init(&p->pii_sc_comp, -1);
354 bzero(&p->pii_stats, sizeof(p->pii_stats));
356 if (!(p->pii_flags & PII_FLAGS_ATTACHED))
357 ppp_attach(); /* attach it */
359 p->pii_ifnet.if_mtu = PPP_MTU;
360 p->pii_writeq = WR(q);
361 /* set write Q and read Q to point here */
362 WR(q)->q_ptr = q->q_ptr = (caddr_t) p;
363 p->pii_ifnet.if_flags |= IFF_RUNNING;
364 p->pii_flags &= PII_FLAGS_INUSE | PII_FLAGS_ATTACHED | PII_FLAGS_DEBUG;
365 for (n = 0; n < NUM_NP; ++n)
366 p->pii_npmode[n] = NPMODE_ERROR;
367 p->pii_npmode[NP_IP] = NPMODE_PASS; /* for backwards compatibility */
369 p->pii_npq_tail = &p->pii_npq;
372 DLOG("ppp_if%d: init\n", p - pii);
376 ppp_if_open(q, dev, flag, sflag)
392 queue_t *q; /* queue info */
394 PII *p = (PII *) q->q_ptr;
400 if_down(&p->pii_ifnet);
401 p->pii_ifnet.if_flags &= ~IFF_RUNNING;
402 p->pii_flags &= ~PII_FLAGS_INUSE;
405 for (mp = p->pii_npq; mp != NULL; mp = mq) {
410 p->pii_npq_tail = &p->pii_npq;
411 p->pii_writeq = NULL;
412 DLOG("ppp_if%d: closed\n", p - pii);
414 return(0); /* no work to be done */
423 register struct iocblk *i;
425 int bits, flags, error, unit, s;
429 mblk_t *mq, **mqnext;
430 struct ppp_stats *psp;
432 switch (mp->b_datap->db_type) {
435 if (*mp->b_rptr & FLUSHW)
436 flushq(q, FLUSHDATA);
437 putnext(q, mp); /* send it along too */
441 putq(q, mp); /* queue it for my service routine */
445 i = (struct iocblk *) mp->b_rptr;
446 p = (PII *) q->q_ptr;
447 switch (i->ioc_cmd) {
449 case SIOCSIFVJCOMP: /* enable or disable VJ compression */
451 if (i->ioc_count == sizeof(u_char) && p != NULL) {
452 bits = *(u_char *) mp->b_cont->b_rptr;
453 DLOG("ppp_if: SIFVJCOMP %d\n", bits);
455 p->pii_flags |= PII_FLAGS_VJC_ON;
457 p->pii_flags &= ~PII_FLAGS_VJC_ON;
459 p->pii_flags |= PII_FLAGS_VJC_NOCCID;
461 p->pii_flags &= ~PII_FLAGS_VJC_NOCCID;
463 p->pii_flags |= PII_FLAGS_VJC_REJ;
465 p->pii_flags &= ~PII_FLAGS_VJC_REJ;
466 bits >>= 4; /* now max conn id. */
468 vj_compress_init(&p->pii_sc_comp, bits);
469 mp->b_datap->db_type = M_IOCACK;
478 case SIOCGETU: /* get unit number */
480 * Allocate a unit if we don't already have one.
488 ppp_if_init(RD(q), p);
491 && (mp->b_cont = allocb(sizeof(int), BPRI_MED)) == NULL)
494 *(int *) mp->b_cont->b_wptr = p->pii_ifnet.if_unit;
495 mp->b_cont->b_wptr += i->ioc_count = sizeof(int);
496 mp->b_datap->db_type = M_IOCACK;
498 i->ioc_error = error;
500 mp->b_datap->db_type = M_IOCNAK;
505 case SIOCSETU: /* set unit number */
506 if (i->ioc_count == sizeof(int)) {
507 unit = *(int *)mp->b_cont->b_rptr;
508 if (p != NULL || (unsigned) unit > NPPP) {
509 mp->b_datap->db_type = M_IOCNAK;
510 i->ioc_error = EINVAL;
515 if (p->pii_flags & PII_FLAGS_INUSE) {
517 oq->q_ptr = RD(oq)->q_ptr = NULL;
518 q->q_ptr = RD(q)->q_ptr = (caddr_t) p;
521 ppp_if_init(RD(q), p);
523 mp->b_datap->db_type = M_IOCACK;
532 /* catch it on the way past to set our debug flag as well */
533 if (i->ioc_count == sizeof(int)) {
534 flags = *(int *)mp->b_cont->b_rptr;
536 p->pii_flags |= PII_FLAGS_DEBUG;
538 p->pii_flags &= ~PII_FLAGS_DEBUG;
545 if (i->ioc_count == sizeof(struct npioctl) && p != NULL) {
546 npi = (struct npioctl *) mp->b_cont->b_rptr;
547 switch (npi->protocol) {
555 i->ioc_error = EAFNOSUPPORT;
557 mp->b_datap->db_type = M_IOCNAK;
561 if (i->ioc_cmd == (int) SIOCSETNPMODE) {
562 if (p->pii_npmode[npix] == NPMODE_QUEUE
563 && npi->mode != NPMODE_QUEUE) {
564 for (mqnext = &p->pii_npq; (mq = *mqnext) != NULL; ) {
565 if (PPP_PROTOCOL(mq->b_rptr) != npi->protocol){
566 mqnext = &mq->b_next;
569 *mqnext = mq->b_next;
570 if (npi->mode == NPMODE_PASS) {
571 putq(q, mq); /* q it for service routine */
576 p->pii_npq_tail = mqnext;
578 p->pii_npmode[npix] = npi->mode;
581 npi->mode = p->pii_npmode[npix];
582 mp->b_datap->db_type = M_IOCACK;
589 default: /* unknown IOCTL call */
590 putnext(q, mp); /* pass it along */
595 putnext(q, mp); /* don't know what to do with this, so send it along*/
606 p = (PII *) q->q_ptr;
608 while ((mp = getq(q)) != NULL) {
610 * we can only get M_DATA types into our Queue,
611 * due to our Put function
613 if (!canput(q->q_next)) {
618 /* increment count of outgoing packets */
622 /* just pass it along, nothing to do in this direction */
635 switch (mp->b_datap->db_type) {
638 if (*mp->b_rptr & FLUSHR)
639 flushq(q, FLUSHDATA);
640 putnext(q, mp); /* send it along too */
644 putq(q, mp); /* queue it for my service routine */
648 p = (PII *) q->q_ptr;
650 switch (*(u_char *) mp->b_rptr) {
652 p->pii_ifnet.if_ierrors++;
654 DLOG("ppp_if: input error inc to %d\n",
655 p->pii_ifnet.if_ierrors);
658 case IF_OUTPUT_ERROR:
659 p->pii_ifnet.if_oerrors++;
661 DLOG("ppp_if: output error inc to %d\n",
662 p->pii_ifnet.if_oerrors);
666 bcopy(mp->b_rptr + sizeof(u_long), &p->pii_cstats,
667 sizeof(struct ppp_comp_stats));
671 putnext(q, mp); /* send it up to pppd */
678 putnext(q, mp); /* send along other message types */
686 register mblk_t *mp,*m0;
688 register mblk_t *mvjc;
689 unsigned char *cp, *iphdr;
693 struct mbuf *mb1, *mb2, *mbtail;
694 int len, xlen, count, s;
696 int address, control;
699 p = (PII *) q->q_ptr;
701 while ((mp = getq(q)) != NULL) {
703 * we can only get M_DATA types into our Queue,
704 * due to our Put function
708 if (!canput(q->q_next)) {
717 dlen = len - PPP_HDRLEN;
719 p->pii_stats.ppp_ibytes += len;
722 /* make sure ppp_header is completely in first block */
723 if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN
724 && !pullupmsg(mp, PPP_HDRLEN)) {
725 DLOG("pullupmsg failed!\n", 0);
727 p->pii_ifnet.if_ierrors++;
730 m0 = mp; /* remember first message block */
733 switch (PPP_PROTOCOL(mp->b_rptr)) {
735 if ((p->pii_flags & PII_FLAGS_VJC_REJ)
736 || p->pii_npmode[NP_IP] != NPMODE_PASS) {
737 DLOG("VJC rejected\n", 0);
741 address = PPP_ADDRESS(mp->b_rptr);
742 control = PPP_CONTROL(mp->b_rptr);
743 mp->b_rptr += PPP_HDRLEN;
747 * Make sure the VJ header is in one message block.
749 xlen = MIN(len, MAX_VJHDR);
750 if (mp->b_rptr + xlen > mp->b_wptr && !pullupmsg(mp, xlen)) {
751 DLOG("pullupmsg vjc %d failed\n", xlen);
757 * Decompress it, then get a buffer and put the
758 * decompressed header in it.
760 xlen = vj_uncompress_tcp(mp->b_rptr, mp->b_wptr - mp->b_rptr,
761 len, &p->pii_sc_comp, &iphdr, &hlen);
763 DLOG("ppp: vj_uncompress failed on type Compressed\n", 0);
767 if (!(mvjc = allocb(hlen + PPP_HDRLEN, BPRI_MED))) {
768 DLOG("allocb mvjc failed (%d)\n", hlen + PPP_HDRLEN);
772 dlen = len - xlen + hlen;
778 bcopy(iphdr, cp + PPP_HDRLEN, hlen);
779 mvjc->b_wptr = cp + PPP_HDRLEN + hlen;
785 case PPP_VJC_UNCOMP :
786 if ((p->pii_flags & PII_FLAGS_VJC_REJ)
787 || p->pii_npmode[NP_IP] != NPMODE_PASS) {
788 DLOG("VJU rejected\n", 0);
794 * Make sure the IP header is in one message block.
796 xlen = MIN(len, MAX_IPHDR + PPP_HDRLEN);
797 if (mp->b_rptr + xlen > mp->b_wptr && !pullupmsg(mp, xlen)) {
798 DLOG("pullupmsg vju %d failed\n", xlen);
804 * "Uncompress" it. Basically this just copies information
805 * into p->pii_sc_comp and restores the protocol field of
808 if (!vj_uncompress_uncomp(mp->b_rptr + PPP_HDRLEN,
810 DLOG("ppp: vj_uncompress failed on type Uncompresed\n", 0);
814 mp->b_rptr[3] = PPP_IP;
819 switch (PPP_PROTOCOL(mp->b_rptr)) {
821 if (!canput(q->q_next)) {
826 p->pii_ifnet.if_ipackets++;
832 * Don't let packets through until IPCP is up.
835 p->pii_ifnet.if_ipackets++;
837 if (!(p->pii_ifnet.if_flags & IFF_UP)
838 || p->pii_npmode[NP_IP] != NPMODE_PASS) {
839 DLOG("pkt ignored - IP down\n", 0);
845 * Get the first mbuf and put the struct ifnet * in.
847 MGETHDR(mb1, M_DONTWAIT, MT_DATA);
849 p->pii_ifnet.if_ierrors++;
854 mb1->m_pkthdr.rcvif = &(p->pii_ifnet);
855 mb1->m_pkthdr.len = dlen;
859 *mtod(mb1, struct ifnet **) = &p->pii_ifnet;
860 len = MLEN - sizeof(struct ifnet *);
861 mb1->m_len = sizeof(struct ifnet *);
865 rptr = mp->b_rptr + PPP_HDRLEN;
866 xlen = mp->b_wptr - rptr;
868 if (xlen == 0) { /* move to the next mblk */
872 xlen = mp->b_wptr - (rptr = mp->b_rptr);
876 MGET(mb2, M_DONTWAIT, MT_DATA);
878 /* if we couldn't get a buffer, drop the packet */
879 p->pii_ifnet.if_ierrors++;
880 m_freem(mb1); /* discard what we've used already */
886 mbtail->m_next = mb2;
889 count = MIN(xlen, len);
890 bcopy((char *) rptr, mtod(mb2, char *) + mb2->m_len, count);
902 if (p->pii_ifnet.if_flags & IFF_PROMISC) {
903 struct mbuf *m = mb1;
908 } while (m = m->m_next);
909 nif.nif_bodylen = len - sizeof(struct ifnet *);
910 mb1->m_off += sizeof(struct ifnet *);
911 snit_intr(&p->pii_ifnet, mb1, &nif);
912 mb1->m_off -= sizeof(struct ifnet *);
916 if (IF_QFULL(&ipintrq)) {
918 p->pii_ifnet.if_ierrors++;
922 IF_ENQUEUE(&ipintrq, mb1);
923 schednetisr(NETISR_IP);
930 /* ifp output procedure */
932 ppp_output(ifp, m0, dst)
935 struct sockaddr *dst;
937 register PII *p = &pii[ifp->if_unit];
949 if (!(ifp->if_flags & IFF_UP)) {
954 switch (dst->sa_family) {
958 if (ifp->if_flags & IFF_PROMISC) {
964 } while (m = m->m_next);
965 nif.nif_bodylen = len;
966 snit_intr(ifp, m0, &nif);
970 npmode = p->pii_npmode[NP_IP];
975 DLOG("ppp: af%d not supported\n", dst->sa_family);
976 error = EAFNOSUPPORT;
980 if (!p->pii_writeq) {
981 DLOG("ppp_if%d: no queue\n", p - pii);
982 error = EHOSTUNREACH;
995 if ((protocol == PPP_IP) && (p->pii_flags & PII_FLAGS_VJC_ON)) {
996 register struct ip *ip;
997 ip = mtod(m0, struct ip *);
998 if (ip->ip_p == IPPROTO_TCP) {
999 type = vj_compress_tcp(ip, m0->m_len, &p->pii_sc_comp,
1000 !(p->pii_flags & PII_FLAGS_VJC_NOCCID),
1003 case TYPE_UNCOMPRESSED_TCP :
1004 protocol = PPP_VJC_UNCOMP;
1006 case TYPE_COMPRESSED_TCP :
1007 protocol = PPP_VJC_COMP;
1008 len = vjhdr - (u_char *) ip;
1018 for (m1 = m0; m1; m1 = m1->m_next)
1021 if (!(mp = allocb(len, BPRI_MED))) {
1022 DLOG("ppp_if%d: allocb failed\n", p - pii);
1028 p->pii_stats.ppp_obytes += len;
1031 *mp->b_wptr++ = PPP_ALLSTATIONS;
1032 *mp->b_wptr++ = PPP_UI;
1034 *mp->b_wptr++ = protocol;
1035 for (m1 = m0; m1; m1 = m1->m_next) { /* copy all data */
1036 bcopy(mtod(m1, char *), (char *) mp->b_wptr, m1->m_len);
1037 mp->b_wptr += m1->m_len;
1041 p->pii_ifnet.if_opackets++;
1042 if (npmode == NPMODE_PASS) {
1043 putq(p->pii_writeq, mp);
1046 *p->pii_npq_tail = mp;
1047 p->pii_npq_tail = ∓
1055 p->pii_ifnet.if_oerrors++;
1061 * if_ ioctl requests
1063 ppp_ioctl(ifp, cmd, data)
1064 register struct ifnet *ifp;
1068 register struct ifaddr *ifa = (struct ifaddr *) data;
1069 register struct ifreq *ifr = (struct ifreq *) data;
1070 struct ppp_stats *psp;
1071 struct ppp_comp_stats *pcp;
1074 int s = splimp(), error = 0;
1078 /* This happens every time IFF_PROMISC has been changed. */
1086 /* clear the flags that can be cleared */
1087 ifp->if_flags &= (IFF_CANTCHANGE);
1088 /* or in the flags that can be changed */
1089 ifp->if_flags |= (ifr->ifr_flags & ~IFF_CANTCHANGE);
1093 ifr->ifr_flags = ifp->if_flags;
1097 if (IFA_ADDR(ifa).sa_family != AF_INET)
1098 error = EAFNOSUPPORT;
1101 case SIOCSIFDSTADDR :
1102 if (IFA_ADDR(ifa).sa_family != AF_INET)
1103 error = EAFNOSUPPORT;
1111 if (ifr->ifr_mtu > MAX_PKTSIZE - PPP_FRAMING) {
1115 ifp->if_mtu = ifr->ifr_mtu;
1119 ifr->ifr_mtu = ifp->if_mtu;
1123 p = &pii[ifp->if_unit];
1124 psp = (struct ppp_stats *) &((struct ifpppstatsreq *)data)->stats;
1125 bzero(psp, sizeof(struct ppp_stats));
1127 psp->p = p->pii_stats;
1129 #if defined(VJC) && !defined(VJ_NO_STATS)
1130 psp->vj = p->pii_sc_comp.stats;
1134 case SIOCGPPPCSTATS:
1135 p = &pii[ifp->if_unit];
1136 bzero(&p->pii_cstats, sizeof(struct ppp_comp_stats));
1138 /* Make a message to send on the interface's write stream */
1142 putctl1(q, M_CTL, IF_GET_CSTATS);
1144 * We assume the message gets passed along immediately, so
1145 * by the time the putctl1 returns, the request has been
1146 * processed, the values returned and p->pii_cstats has
1147 * been updated. If not, we just get zeroes.
1152 pcp = (struct ppp_comp_stats *)&((struct ifpppcstatsreq *)data)->stats;
1153 bcopy(&p->pii_cstats, pcp, sizeof(struct ppp_comp_stats));