]> git.ozlabs.org Git - ppp.git/blob - modules/if_ppp.c
use common version
[ppp.git] / modules / if_ppp.c
1 /*
2  * if_ppp.c - a network interface connected to a STREAMS module.
3  *
4  * Copyright (c) 1994 The Australian National University.
5  * All rights reserved.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation is hereby granted, provided that the above copyright
9  * notice appears in all copies.  This software is provided without any
10  * warranty, express or implied. The Australian National University
11  * makes no representations about the suitability of this software for
12  * any purpose.
13  *
14  * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
15  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
17  * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
18  * OF SUCH DAMAGE.
19  *
20  * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23  * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
24  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
25  * OR MODIFICATIONS.
26  *
27  * $Id: if_ppp.c,v 1.3 1996/09/14 05:11:13 paulus Exp $
28  */
29
30 /*
31  * This file is used under SunOS 4 and Digital UNIX.
32  *
33  * This file provides the glue between PPP and IP.
34  */
35
36 #define INET    1
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/stream.h>
41 #include <sys/errno.h>
42 #include <sys/mbuf.h>
43 #include <sys/socket.h>
44 #include <net/if.h>
45 #include <net/netisr.h>
46 #include <net/ppp_defs.h>
47 #include <net/pppio.h>
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50 #ifdef __osf__
51 #include <sys/ioctl.h>
52 #include <net/if_types.h>
53 #include "ppp_mod.h"
54 #else
55 #include <sys/sockio.h>
56 #include <modules/ppp_mod.h>
57 #endif
58
59 #ifdef SNIT_SUPPORT
60 #include <sys/time.h>
61 #include <net/nit_if.h>
62 #include <netinet/if_ether.h>
63 #endif
64
65 #ifdef __osf__
66 #define SIOCSIFMTU SIOCSIPMTU
67 #define SIOCGIFMTU SIOCRIPMTU
68 #define IFA_ADDR(ifa)   (*(ifa)->ifa_addr)
69 #else
70 #define IFA_ADDR(ifa)   ((ifa)->ifa_addr)
71 #endif
72
73 #define ifr_mtu         ifr_metric
74
75 #define PPP_MINMTU      64
76 #define PPP_MAXMTU      65536
77
78 static int if_ppp_open __P((queue_t *, int, int, int));
79 static int if_ppp_close __P((queue_t *, int));
80 static int if_ppp_wput __P((queue_t *, mblk_t *));
81 static int if_ppp_rput __P((queue_t *, mblk_t *));
82
83 #define PPP_IF_ID 0x8021
84 static struct module_info minfo = {
85     PPP_IF_ID, "if_ppp", 0, INFPSZ, 4096, 128
86 };
87
88 static struct qinit rinit = {
89     if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL
90 };
91
92 static struct qinit winit = {
93     if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
94 };
95
96 struct streamtab if_pppinfo = {
97     &rinit, &winit, NULL, NULL
98 };
99
100 typedef struct if_ppp_state {
101     int unit;
102     queue_t *q;
103     int flags;
104 } if_ppp_t;
105
106 /* Values for flags */
107 #define DBGLOG          1
108
109 static int if_ppp_count;        /* Number of currently-active streams */
110
111 static int ppp_nalloc;          /* Number of elements of ifs and states */
112 static struct ifnet **ifs;      /* Array of pointers to interface structs */
113 static if_ppp_t **states;       /* Array of pointers to state structs */
114
115 static int if_ppp_output __P((struct ifnet *, struct mbuf *,
116                               struct sockaddr *));
117 static int if_ppp_ioctl __P((struct ifnet *, u_int, caddr_t));
118 static struct mbuf *make_mbufs __P((mblk_t *, int));
119 static mblk_t *make_message __P((struct mbuf *, int));
120
121 #ifdef SNIT_SUPPORT
122 /* Fake ether header for SNIT */
123 static struct ether_header snit_ehdr = {{0}, {0}, ETHERTYPE_IP};
124 #endif
125
126 #ifndef __osf__
127 static void ppp_if_detach __P((struct ifnet *));
128
129 /*
130  * Detach all the interfaces before unloading.
131  * Not sure this works.
132  */
133 int
134 if_ppp_unload()
135 {
136     int i;
137
138     if (if_ppp_count > 0)
139         return EBUSY;
140     for (i = 0; i < ppp_nalloc; ++i)
141         if (ifs[i] != 0)
142             ppp_if_detach(ifs[i]);
143     FREE(ifs, ppp_nalloc * sizeof (struct ifnet *));
144     FREE(states, ppp_nalloc * sizeof (struct if_ppp_t *));
145     ppp_nalloc = 0;
146     return 0;
147 }
148 #endif /* __osf__ */
149
150 /*
151  * STREAMS module entry points.
152  */
153 static int
154 if_ppp_open(q, dev, flag, sflag)
155     queue_t *q;
156     int dev;
157     int flag, sflag;
158 {
159     if_ppp_t *sp;
160
161     if (q->q_ptr == 0) {
162         sp = (if_ppp_t *) ALLOC_SLEEP(sizeof (if_ppp_t));
163         if (sp == 0)
164             return OPENFAIL;
165         bzero(sp, sizeof (if_ppp_t));
166         q->q_ptr = (caddr_t) sp;
167         WR(q)->q_ptr = (caddr_t) sp;
168         sp->unit = -1;          /* no interface unit attached at present */
169         sp->q = WR(q);
170         sp->flags = 0;
171         ++if_ppp_count;
172     }
173     return 0;
174 }
175
176 static int
177 if_ppp_close(q, flag)
178     queue_t *q;
179     int flag;
180 {
181     if_ppp_t *sp;
182     struct ifnet *ifp;
183
184     sp = (if_ppp_t *) q->q_ptr;
185     if (sp != 0) {
186         if (sp->flags & DBGLOG)
187             printf("if_ppp closed, q=%x sp=%x\n", q, sp);
188         if (sp->unit >= 0) {
189             if (sp->unit < ppp_nalloc) {
190                 states[sp->unit] = 0;
191                 ifp = ifs[sp->unit];
192                 if (ifp != 0)
193                     ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
194 #ifdef DEBUG
195             } else {
196                 printf("if_ppp: unit %d nonexistent!\n", sp->unit);
197 #endif
198             }
199         }
200         FREE(sp, sizeof (if_ppp_t));
201         --if_ppp_count;
202     }
203     return 0;
204 }
205
206 static int
207 if_ppp_wput(q, mp)
208     queue_t *q;
209     mblk_t *mp;
210 {
211     if_ppp_t *sp;
212     struct iocblk *iop;
213     int error, unit;
214     struct ifnet *ifp;
215
216     sp = (if_ppp_t *) q->q_ptr;
217     switch (mp->b_datap->db_type) {
218     case M_DATA:
219         /*
220          * Now why would we be getting data coming in here??
221          */
222         if (sp->flags & DBGLOG)
223             printf("if_ppp: got M_DATA len=%d\n", msgdsize(mp));
224         freemsg(mp);
225         break;
226
227     case M_IOCTL:
228         iop = (struct iocblk *) mp->b_rptr;
229         error = EINVAL;
230
231         if (sp->flags & DBGLOG)
232             printf("if_ppp: got ioctl cmd=%x count=%d\n",
233                    iop->ioc_cmd, iop->ioc_count);
234
235         switch (iop->ioc_cmd) {
236         case PPPIO_NEWPPA:              /* well almost */
237             if (iop->ioc_count != sizeof(int) || sp->unit >= 0)
238                 break;
239             if ((error = NOTSUSER()) != 0)
240                 break;
241             unit = *(int *)mp->b_cont->b_rptr;
242
243             /* Check that this unit isn't already in use */
244             if (unit < ppp_nalloc && states[unit] != 0) {
245                 error = EADDRINUSE;
246                 break;
247             }
248
249             /* Extend ifs and states arrays if necessary. */
250             error = ENOSR;
251             if (unit >= ppp_nalloc) {
252                 int newn;
253                 struct ifnet **newifs;
254                 if_ppp_t **newstates;
255
256                 newn = unit + 4;
257                 if (sp->flags & DBGLOG)
258                     printf("if_ppp: extending ifs to %d\n", newn);
259                 newifs = (struct ifnet **)
260                     ALLOC_NOSLEEP(newn * sizeof (struct ifnet *));
261                 if (newifs == 0)
262                     break;
263                 bzero(newifs, newn * sizeof (struct ifnet *));
264                 newstates = (if_ppp_t **)
265                     ALLOC_NOSLEEP(newn * sizeof (struct if_ppp_t *));
266                 if (newstates == 0) {
267                     FREE(newifs, newn * sizeof (struct ifnet *));
268                     break;
269                 }
270                 bzero(newstates, newn * sizeof (struct if_ppp_t *));
271                 bcopy(ifs, newifs, ppp_nalloc * sizeof(struct ifnet *));
272                 bcopy(states, newstates, ppp_nalloc * sizeof(if_ppp_t *));
273                 FREE(ifs, ppp_nalloc * sizeof(struct ifnet *));
274                 FREE(states, ppp_nalloc * sizeof(if_ppp_t *));
275                 ifs = newifs;
276                 states = newstates;
277                 ppp_nalloc = newn;
278             }
279
280             /* Allocate a new ifnet struct if necessary. */
281             ifp = ifs[unit];
282             if (ifp == 0) {
283                 ifp = (struct ifnet *) ALLOC_NOSLEEP(sizeof (struct ifnet));
284                 if (ifp == 0)
285                     break;
286                 bzero(ifp, sizeof (struct ifnet));
287                 ifs[unit] = ifp;
288                 ifp->if_name = "ppp";
289                 ifp->if_unit = unit;
290                 ifp->if_mtu = PPP_MRU;
291                 ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
292 #ifdef IFF_MULTICAST
293                 ifp->if_flags |= IFF_MULTICAST;
294 #endif
295                 ifp->if_output = if_ppp_output;
296 #ifdef __osf__
297                 ifp->if_version = "Point-to-Point Protocol, version 2.3";
298                 ifp->if_mediamtu = 1500;
299                 ifp->if_type = IFT_PPP;
300                 ifp->if_hdrlen = PPP_HDRLEN;
301                 ifp->if_addrlen = 0;
302 #ifdef NETMASTERCPU
303                 ifp->if_affinity = NETMASTERCPU;
304 #endif
305 #endif
306                 ifp->if_ioctl = if_ppp_ioctl;
307                 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
308                 if_attach(ifp);
309                 if (sp->flags & DBGLOG)
310                     printf("if_ppp: created unit %d\n", unit);
311             } else {
312                 ifp->if_mtu = PPP_MRU;
313                 ifp->if_flags |= IFF_RUNNING;
314             }
315
316             states[unit] = sp;
317             sp->unit = unit;
318
319             error = 0;
320             iop->ioc_count = 0;
321             if (sp->flags & DBGLOG)
322                 printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit,
323                        sp, sp->q);
324             break;
325
326         case PPPIO_DEBUG:
327             error = -1;
328             if (iop->ioc_count == sizeof(int)) {
329                 if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) {
330                     printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp);
331                     sp->flags |= DBGLOG;
332                     error = 0;
333                     iop->ioc_count = 0;
334                 }
335             }
336             break;
337
338         default:
339             error = -1;
340             break;
341         }
342
343         if (sp->flags & DBGLOG)
344             printf("if_ppp: ioctl result %d\n", error);
345         if (error < 0)
346             putnext(q, mp);
347         else if (error == 0) {
348             mp->b_datap->db_type = M_IOCACK;
349             qreply(q, mp);
350         } else {
351             mp->b_datap->db_type = M_IOCNAK;
352             iop->ioc_count = 0;
353             iop->ioc_error = error;
354             qreply(q, mp);
355         }
356         break;
357
358     default:
359         putnext(q, mp);
360     }
361     return 0;
362 }
363
364 static int
365 if_ppp_rput(q, mp)
366     queue_t *q;
367     mblk_t *mp;
368 {
369     if_ppp_t *sp;
370     int proto, s;
371     struct mbuf *mb;
372     struct ifqueue *inq;
373     struct ifnet *ifp;
374     int len;
375
376     sp = (if_ppp_t *) q->q_ptr;
377     switch (mp->b_datap->db_type) {
378     case M_DATA:
379         /*
380          * Convert the message into an mbuf chain
381          * and inject it into the network code.
382          */
383         if (sp->flags & DBGLOG)
384             printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n",
385                    msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2],
386                    mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6],
387                    mp->b_rptr[7]);
388
389         if (sp->unit < 0) {
390             freemsg(mp);
391             break;
392         }
393         if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) {
394 #ifdef DEBUG
395             printf("if_ppp: no unit %d!\n", sp->unit);
396 #endif
397             freemsg(mp);
398             break;
399         }
400
401         if ((ifp->if_flags & IFF_UP) == 0) {
402             freemsg(mp);
403             break;
404         }
405         ++ifp->if_ipackets;
406
407         proto = PPP_PROTOCOL(mp->b_rptr);
408         adjmsg(mp, PPP_HDRLEN);
409         len = msgdsize(mp);
410         mb = make_mbufs(mp, sizeof(struct ifnet *));
411         freemsg(mp);
412         if (mb == NULL) {
413             if (sp->flags & DBGLOG)
414                 printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit);
415             ++ifp->if_ierrors;
416             break;
417         }
418
419 #ifdef SNIT_SUPPORT
420         if (proto == PPP_IP && (ifp->if_flags & IFF_PROMISC)) {
421             struct nit_if nif;
422
423             nif.nif_header = (caddr_t) &snit_ehdr;
424             nif.nif_hdrlen = sizeof(snit_ehdr);
425             nif.nif_bodylen = len;
426             nif.nif_promisc = 0;
427             snit_intr(ifp, mb, &nif);
428         }
429 #endif
430
431 /*
432  * For Digital UNIX, there's space set aside in the header mbuf
433  * for the interface info.
434  *
435  * For Sun it's smuggled around via a pointer at the front of the mbuf.
436  */
437 #ifdef __osf__
438         mb->m_pkthdr.rcvif = ifp;
439         mb->m_pkthdr.len = len;
440 #else
441         mb->m_off -= sizeof(struct ifnet *);
442         mb->m_len += sizeof(struct ifnet *);
443         *mtod(mb, struct ifnet **) = ifp;
444 #endif
445
446         inq = 0;
447         switch (proto) {
448         case PPP_IP:
449             inq = &ipintrq;
450             schednetisr(NETISR_IP);
451         }
452
453         if (inq != 0) {
454             s = splhigh();
455             if (IF_QFULL(inq)) {
456                 IF_DROP(inq);
457                 ++ifp->if_ierrors;
458                 if (sp->flags & DBGLOG)
459                     printf("if_ppp: inq full, proto=%x\n", proto);
460                 m_freem(mb);
461             } else {
462                 IF_ENQUEUE(inq, mb);
463             }
464             splx(s);
465         } else {
466             if (sp->flags & DBGLOG)
467                 printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto);
468             ++ifp->if_ierrors;
469             m_freem(mb);
470         }
471         break;
472
473     default:
474         putnext(q, mp);
475     }
476     return 0;
477 }
478
479 /*
480  * Network code wants to output a packet.
481  * Turn it into a STREAMS message and send it down.
482  */
483 static int
484 if_ppp_output(ifp, m0, dst)
485     struct ifnet *ifp;
486     struct mbuf *m0;
487     struct sockaddr *dst;
488 {
489     mblk_t *mp;
490     int proto, s;
491     if_ppp_t *sp;
492     u_char *p;
493
494     if ((ifp->if_flags & IFF_UP) == 0) {
495         m_freem(m0);
496         return ENETDOWN;
497     }
498
499     if ((unsigned)ifp->if_unit >= ppp_nalloc) {
500 #ifdef DEBUG
501         printf("if_ppp_output: unit %d?\n", ifp->if_unit);
502 #endif
503         m_freem(m0);
504         return EINVAL;
505     }
506     sp = states[ifp->if_unit];
507     if (sp == 0) {
508 #ifdef DEBUG
509         printf("if_ppp_output: no queue?\n");
510 #endif
511         m_freem(m0);
512         return ENETDOWN;
513     }
514
515     if (sp->flags & DBGLOG) {
516         p = mtod(m0, u_char *);
517         printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n",
518                ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4],
519                p[5], p[6], p[7], sp->q);
520     }
521
522     switch (dst->sa_family) {
523     case AF_INET:
524         proto = PPP_IP;
525 #ifdef SNIT_SUPPORT
526         if (ifp->if_flags & IFF_PROMISC) {
527             struct nit_if nif;
528             struct mbuf *m;
529             int len;
530
531             for (len = 0, m = m0; m != NULL; m = m->m_next)
532                 len += m->m_len;
533             nif.nif_header = (caddr_t) &snit_ehdr;
534             nif.nif_hdrlen = sizeof(snit_ehdr);
535             nif.nif_bodylen = len;
536             nif.nif_promisc = 0;
537             snit_intr(ifp, m0, &nif);
538         }
539 #endif
540         break;
541
542     default:
543         m_freem(m0);
544         return EAFNOSUPPORT;
545     }
546
547     ++ifp->if_opackets;
548     mp = make_message(m0, PPP_HDRLEN);
549     m_freem(m0);
550     if (mp == 0) {
551         ++ifp->if_oerrors;
552         return ENOBUFS;
553     }
554     mp->b_rptr -= PPP_HDRLEN;
555     mp->b_rptr[0] = PPP_ALLSTATIONS;
556     mp->b_rptr[1] = PPP_UI;
557     mp->b_rptr[2] = proto >> 8;
558     mp->b_rptr[3] = proto;
559
560     s = splstr();
561     if (sp->flags & DBGLOG)
562         printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n",
563                sp->q, mp, mp->b_rptr, mp->b_wptr, proto);
564     putnext(sp->q, mp);
565     splx(s);
566
567     return 0;
568 }
569
570 /*
571  * Socket ioctl routine for ppp interfaces.
572  */
573 static int
574 if_ppp_ioctl(ifp, cmd, data)
575     struct ifnet *ifp;
576     u_int cmd;
577     caddr_t data;
578 {
579     int s, error;
580     struct ifreq *ifr = (struct ifreq *) data;
581     struct ifaddr *ifa = (struct ifaddr *) data;
582
583     error = 0;
584     s = splimp();
585     switch (cmd) {
586     case SIOCSIFFLAGS:
587         if ((ifp->if_flags & IFF_RUNNING) == 0)
588             ifp->if_flags &= ~IFF_UP;
589         break;
590
591     case SIOCSIFADDR:
592         if (IFA_ADDR(ifa).sa_family != AF_INET)
593             error = EAFNOSUPPORT;
594         break;
595
596     case SIOCSIFDSTADDR:
597         if (IFA_ADDR(ifa).sa_family != AF_INET)
598             error = EAFNOSUPPORT;
599         break;
600
601     case SIOCSIFMTU:
602         if ((error = NOTSUSER()) != 0)
603             break;
604         if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) {
605             error = EINVAL;
606             break;
607         }
608         ifp->if_mtu = ifr->ifr_mtu;
609         break;
610
611     case SIOCGIFMTU:
612         ifr->ifr_mtu = ifp->if_mtu;
613         break;
614
615     case SIOCADDMULTI:
616     case SIOCDELMULTI:
617         switch(ifr->ifr_addr.sa_family) {
618         case AF_INET:
619             break;
620         default:
621             error = EAFNOSUPPORT;
622             break;
623         }
624         break;
625
626     default:
627         error = EINVAL;
628     }
629     splx(s);
630     return (error);
631 }
632
633 /*
634  * Turn a STREAMS message into an mbuf chain.
635  */
636 static struct mbuf *
637 make_mbufs(mp, off)
638     mblk_t *mp;
639     int off;
640 {
641     struct mbuf *head, **prevp, *m;
642     int len, space, n;
643     unsigned char *cp, *dp;
644
645     len = msgdsize(mp);
646     if (len == 0)
647         return 0;
648     prevp = &head;
649     space = 0;
650     cp = mp->b_rptr;
651 #ifdef __osf__
652     MGETHDR(m, M_DONTWAIT, MT_DATA);
653     m->m_len = 0;
654     space = MHLEN;
655     *prevp = m;
656     prevp = &m->m_next;
657     dp = mtod(m, unsigned char *);
658     len -= space;
659     off = 0;
660 #endif
661     for (;;) {
662         while (cp >= mp->b_wptr) {
663             mp = mp->b_cont;
664             if (mp == 0) {
665                 *prevp = 0;
666                 return head;
667             }
668             cp = mp->b_rptr;
669         }
670         n = mp->b_wptr - cp;
671         if (space == 0) {
672             MGET(m, M_DONTWAIT, MT_DATA);
673             *prevp = m;
674             if (m == 0) {
675                 if (head != 0)
676                     m_freem(head);
677                 return 0;
678             }
679             if (len + off > 2 * MLEN) {
680 #ifdef __osf__
681                 MCLGET(m, M_DONTWAIT);
682 #else
683                 MCLGET(m);
684 #endif
685             }
686 #ifdef __osf__
687             space = ((m->m_flags & M_EXT) ? MCLBYTES : MLEN);
688 #else
689             space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off;
690             m->m_off += off;
691 #endif
692             m->m_len = 0;
693             len -= space;
694             dp = mtod(m, unsigned char *);
695             off = 0;
696             prevp = &m->m_next;
697         }
698         if (n > space)
699             n = space;
700         bcopy(cp, dp, n);
701         cp += n;
702         dp += n;
703         space -= n;
704         m->m_len += n;
705     }
706 }
707
708 /*
709  * Turn an mbuf chain into a STREAMS message.
710  */
711 #define ALLOCB_MAX      4096
712
713 static mblk_t *
714 make_message(m, off)
715     struct mbuf *m;
716     int off;
717 {
718     mblk_t *head, **prevp, *mp;
719     int len, space, n, nb;
720     unsigned char *cp, *dp;
721     struct mbuf *nm;
722
723     len = 0;
724     for (nm = m; nm != 0; nm = nm->m_next)
725         len += nm->m_len;
726     prevp = &head;
727     space = 0;
728     cp = mtod(m, unsigned char *);
729     nb = m->m_len;
730     for (;;) {
731         while (nb <= 0) {
732             m = m->m_next;
733             if (m == 0) {
734                 *prevp = 0;
735                 return head;
736             }
737             cp = mtod(m, unsigned char *);
738             nb = m->m_len;
739         }
740         if (space == 0) {
741             space = len + off;
742             if (space > ALLOCB_MAX)
743                 space = ALLOCB_MAX;
744             mp = allocb(space, BPRI_LO);
745             *prevp = mp;
746             if (mp == 0) {
747                 if (head != 0)
748                     freemsg(head);
749                 return 0;
750             }
751             dp = mp->b_rptr += off;
752             space -= off;
753             len -= space;
754             off = 0;
755             prevp = &mp->b_cont;
756         }
757         n = nb < space? nb: space;
758         bcopy(cp, dp, n);
759         cp += n;
760         dp += n;
761         nb -= n;
762         space -= n;
763         mp->b_wptr = dp;
764     }
765 }
766
767 /*
768  * Digital UNIX doesn't allow for removing ifnet structures
769  * from the list.  But then we're not using this as a loadable
770  * module anyway, so that's OK.
771  *
772  * Under SunOS, this should allow the module to be unloaded.
773  * Unfortunately, it doesn't seem to detach all the references,
774  * so your system may well crash after you unload this module :-(
775  */
776 #ifndef __osf__
777
778 /*
779  * Remove an interface from the system.
780  * This routine contains magic.
781  */
782 #include <net/route.h>
783 #include <netinet/in_pcb.h>
784 #include <netinet/ip_var.h>
785 #include <netinet/tcp.h>
786 #include <netinet/tcp_timer.h>
787 #include <netinet/tcp_var.h>
788 #include <netinet/udp.h>
789 #include <netinet/udp_var.h>
790
791 static void
792 ppp_if_detach(ifp)
793     struct ifnet *ifp;
794 {
795     int s;
796     struct inpcb *pcb;
797     struct ifaddr *ifa;
798     struct in_ifaddr **inap;
799     struct ifnet **ifpp;
800
801     s = splhigh();
802
803     /*
804      * Clear the interface from any routes currently cached in
805      * TCP or UDP protocol control blocks.
806      */
807     for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next)
808         if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
809             in_losing(pcb);
810     for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next)
811         if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
812             in_losing(pcb);
813
814     /*
815      * Delete routes through all addresses of the interface.
816      */
817     for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) {
818         rtinit(ifa, ifa, SIOCDELRT, RTF_HOST);
819         rtinit(ifa, ifa, SIOCDELRT, 0);
820     }
821
822     /*
823      * Unlink the interface's address(es) from the in_ifaddr list.
824      */
825     for (inap = &in_ifaddr; *inap != 0; ) {
826         if ((*inap)->ia_ifa.ifa_ifp == ifp)
827             *inap = (*inap)->ia_next;
828         else
829             inap = &(*inap)->ia_next;
830     }
831
832     /*
833      * Delete the interface from the ifnet list.
834      */
835     for (ifpp = &ifnet; (*ifpp) != 0; ) {
836         if (*ifpp == ifp)
837             break;
838         ifpp = &(*ifpp)->if_next;
839     }
840     if (*ifpp == 0)
841         printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit);
842     else
843         *ifpp = ifp->if_next;
844
845     splx(s);
846 }
847
848 #endif /* __osf__ */