fixes from Farrell
[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.5 1997/04/30 05:43:54 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/errno.h>
41 #include <sys/mbuf.h>
42 #include <sys/socket.h>
43 #include <net/if.h>
44 #include <net/netisr.h>
45 #include <net/ppp_defs.h>
46 #include <net/pppio.h>
47 #include <netinet/in.h>
48 #include <netinet/in_var.h>
49 #ifdef __osf__
50 #include <sys/ioctl.h>
51 #include <net/if_types.h>
52 #include "ppp_mod.h"
53 #else
54 #include <sys/sockio.h>
55 #include <modules/ppp_mod.h>
56 #endif
57
58 #include <sys/stream.h>
59
60 #ifdef SNIT_SUPPORT
61 #include <sys/time.h>
62 #include <net/nit_if.h>
63 #include <netinet/if_ether.h>
64 #endif
65
66 #ifdef __osf__
67 #define SIOCSIFMTU SIOCSIPMTU
68 #define SIOCGIFMTU SIOCRIPMTU
69 #define IFA_ADDR(ifa)   (*(ifa)->ifa_addr)
70 #else
71 #define IFA_ADDR(ifa)   ((ifa)->ifa_addr)
72 #endif
73
74 #define ifr_mtu         ifr_metric
75
76 static int if_ppp_open __P((queue_t *, int, int, int));
77 static int if_ppp_close __P((queue_t *, int));
78 static int if_ppp_wput __P((queue_t *, mblk_t *));
79 static int if_ppp_rput __P((queue_t *, mblk_t *));
80
81 #define PPP_IF_ID 0x8021
82 static struct module_info minfo = {
83     PPP_IF_ID, "if_ppp", 0, INFPSZ, 4096, 128
84 };
85
86 static struct qinit rinit = {
87     if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL
88 };
89
90 static struct qinit winit = {
91     if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
92 };
93
94 struct streamtab if_pppinfo = {
95     &rinit, &winit, NULL, NULL
96 };
97
98 typedef struct if_ppp_state {
99     int unit;
100     queue_t *q;
101     int flags;
102 } if_ppp_t;
103
104 /* Values for flags */
105 #define DBGLOG          1
106
107 static int if_ppp_count;        /* Number of currently-active streams */
108
109 static int ppp_nalloc;          /* Number of elements of ifs and states */
110 static struct ifnet **ifs;      /* Array of pointers to interface structs */
111 static if_ppp_t **states;       /* Array of pointers to state structs */
112
113 static int if_ppp_output __P((struct ifnet *, struct mbuf *,
114                               struct sockaddr *));
115 static int if_ppp_ioctl __P((struct ifnet *, u_int, caddr_t));
116 static struct mbuf *make_mbufs __P((mblk_t *, int));
117 static mblk_t *make_message __P((struct mbuf *, int));
118
119 #ifdef SNIT_SUPPORT
120 /* Fake ether header for SNIT */
121 static struct ether_header snit_ehdr = {{0}, {0}, ETHERTYPE_IP};
122 #endif
123
124 #ifndef __osf__
125 static void ppp_if_detach __P((struct ifnet *));
126
127 /*
128  * Detach all the interfaces before unloading.
129  * Not sure this works.
130  */
131 int
132 if_ppp_unload()
133 {
134     int i;
135
136     if (if_ppp_count > 0)
137         return EBUSY;
138     for (i = 0; i < ppp_nalloc; ++i)
139         if (ifs[i] != 0)
140             ppp_if_detach(ifs[i]);
141     if (ifs) {
142         FREE(ifs, ppp_nalloc * sizeof (struct ifnet *));
143         FREE(states, ppp_nalloc * sizeof (struct if_ppp_t *));
144     }
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                 if (ifs) {
274                     FREE(ifs, ppp_nalloc * sizeof(struct ifnet *));
275                     FREE(states, ppp_nalloc * sizeof(if_ppp_t *));
276                 }
277                 ifs = newifs;
278                 states = newstates;
279                 ppp_nalloc = newn;
280             }
281
282             /* Allocate a new ifnet struct if necessary. */
283             ifp = ifs[unit];
284             if (ifp == 0) {
285                 ifp = (struct ifnet *) ALLOC_NOSLEEP(sizeof (struct ifnet));
286                 if (ifp == 0)
287                     break;
288                 bzero(ifp, sizeof (struct ifnet));
289                 ifs[unit] = ifp;
290                 ifp->if_name = "ppp";
291                 ifp->if_unit = unit;
292                 ifp->if_mtu = PPP_MTU;
293                 ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
294 #ifdef IFF_MULTICAST
295                 ifp->if_flags |= IFF_MULTICAST;
296 #endif
297                 ifp->if_output = if_ppp_output;
298 #ifdef __osf__
299                 ifp->if_version = "Point-to-Point Protocol, version 2.3";
300                 ifp->if_mediamtu = PPP_MTU;
301                 ifp->if_type = IFT_PPP;
302                 ifp->if_hdrlen = PPP_HDRLEN;
303                 ifp->if_addrlen = 0;
304                 ifp->if_flags |= IFF_NOARP | IFF_SIMPLEX | IFF_NOTRAILERS;
305 #ifdef IFF_VAR_MTU
306                 ifp->if_flags |= IFF_VAR_MTU;
307 #endif
308 #ifdef NETMASTERCPU
309                 ifp->if_affinity = NETMASTERCPU;
310 #endif
311 #endif
312                 ifp->if_ioctl = if_ppp_ioctl;
313                 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
314                 if_attach(ifp);
315                 if (sp->flags & DBGLOG)
316                     printf("if_ppp: created unit %d\n", unit);
317             } else {
318                 ifp->if_mtu = PPP_MTU;
319                 ifp->if_flags |= IFF_RUNNING;
320             }
321
322             states[unit] = sp;
323             sp->unit = unit;
324
325             error = 0;
326             iop->ioc_count = 0;
327             if (sp->flags & DBGLOG)
328                 printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit,
329                        sp, sp->q);
330             break;
331
332         case PPPIO_DEBUG:
333             error = -1;
334             if (iop->ioc_count == sizeof(int)) {
335                 if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) {
336                     printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp);
337                     sp->flags |= DBGLOG;
338                     error = 0;
339                     iop->ioc_count = 0;
340                 }
341             }
342             break;
343
344         default:
345             error = -1;
346             break;
347         }
348
349         if (sp->flags & DBGLOG)
350             printf("if_ppp: ioctl result %d\n", error);
351         if (error < 0)
352             putnext(q, mp);
353         else if (error == 0) {
354             mp->b_datap->db_type = M_IOCACK;
355             qreply(q, mp);
356         } else {
357             mp->b_datap->db_type = M_IOCNAK;
358             iop->ioc_count = 0;
359             iop->ioc_error = error;
360             qreply(q, mp);
361         }
362         break;
363
364     default:
365         putnext(q, mp);
366     }
367     return 0;
368 }
369
370 static int
371 if_ppp_rput(q, mp)
372     queue_t *q;
373     mblk_t *mp;
374 {
375     if_ppp_t *sp;
376     int proto, s;
377     struct mbuf *mb;
378     struct ifqueue *inq;
379     struct ifnet *ifp;
380     int len;
381
382     sp = (if_ppp_t *) q->q_ptr;
383     switch (mp->b_datap->db_type) {
384     case M_DATA:
385         /*
386          * Convert the message into an mbuf chain
387          * and inject it into the network code.
388          */
389         if (sp->flags & DBGLOG)
390             printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n",
391                    msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2],
392                    mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6],
393                    mp->b_rptr[7]);
394
395         if (sp->unit < 0) {
396             freemsg(mp);
397             break;
398         }
399         if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) {
400 #ifdef DEBUG
401             printf("if_ppp: no unit %d!\n", sp->unit);
402 #endif
403             freemsg(mp);
404             break;
405         }
406
407         if ((ifp->if_flags & IFF_UP) == 0) {
408             freemsg(mp);
409             break;
410         }
411         ++ifp->if_ipackets;
412
413         proto = PPP_PROTOCOL(mp->b_rptr);
414         adjmsg(mp, PPP_HDRLEN);
415         len = msgdsize(mp);
416         mb = make_mbufs(mp, sizeof(struct ifnet *));
417         freemsg(mp);
418         if (mb == NULL) {
419             if (sp->flags & DBGLOG)
420                 printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit);
421             ++ifp->if_ierrors;
422             break;
423         }
424
425 #ifdef SNIT_SUPPORT
426         if (proto == PPP_IP && (ifp->if_flags & IFF_PROMISC)) {
427             struct nit_if nif;
428
429             nif.nif_header = (caddr_t) &snit_ehdr;
430             nif.nif_hdrlen = sizeof(snit_ehdr);
431             nif.nif_bodylen = len;
432             nif.nif_promisc = 0;
433             snit_intr(ifp, mb, &nif);
434         }
435 #endif
436
437 /*
438  * For Digital UNIX, there's space set aside in the header mbuf
439  * for the interface info.
440  *
441  * For Sun it's smuggled around via a pointer at the front of the mbuf.
442  */
443 #ifdef __osf__
444         mb->m_pkthdr.rcvif = ifp;
445         mb->m_pkthdr.len = len;
446 #else
447         mb->m_off -= sizeof(struct ifnet *);
448         mb->m_len += sizeof(struct ifnet *);
449         *mtod(mb, struct ifnet **) = ifp;
450 #endif
451
452         inq = 0;
453         switch (proto) {
454         case PPP_IP:
455             inq = &ipintrq;
456             schednetisr(NETISR_IP);
457         }
458
459         if (inq != 0) {
460             s = splhigh();
461             if (IF_QFULL(inq)) {
462                 IF_DROP(inq);
463                 ++ifp->if_ierrors;
464                 if (sp->flags & DBGLOG)
465                     printf("if_ppp: inq full, proto=%x\n", proto);
466                 m_freem(mb);
467             } else {
468                 IF_ENQUEUE(inq, mb);
469             }
470             splx(s);
471         } else {
472             if (sp->flags & DBGLOG)
473                 printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto);
474             ++ifp->if_ierrors;
475             m_freem(mb);
476         }
477         break;
478
479     default:
480         putnext(q, mp);
481     }
482     return 0;
483 }
484
485 /*
486  * Network code wants to output a packet.
487  * Turn it into a STREAMS message and send it down.
488  */
489 static int
490 if_ppp_output(ifp, m0, dst)
491     struct ifnet *ifp;
492     struct mbuf *m0;
493     struct sockaddr *dst;
494 {
495     mblk_t *mp;
496     int proto, s;
497     if_ppp_t *sp;
498     u_char *p;
499
500     if ((ifp->if_flags & IFF_UP) == 0) {
501         m_freem(m0);
502         return ENETDOWN;
503     }
504
505     if ((unsigned)ifp->if_unit >= ppp_nalloc) {
506 #ifdef DEBUG
507         printf("if_ppp_output: unit %d?\n", ifp->if_unit);
508 #endif
509         m_freem(m0);
510         return EINVAL;
511     }
512     sp = states[ifp->if_unit];
513     if (sp == 0) {
514 #ifdef DEBUG
515         printf("if_ppp_output: no queue?\n");
516 #endif
517         m_freem(m0);
518         return ENETDOWN;
519     }
520
521     if (sp->flags & DBGLOG) {
522         p = mtod(m0, u_char *);
523         printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n",
524                ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4],
525                p[5], p[6], p[7], sp->q);
526     }
527
528     switch (dst->sa_family) {
529     case AF_INET:
530         proto = PPP_IP;
531 #ifdef SNIT_SUPPORT
532         if (ifp->if_flags & IFF_PROMISC) {
533             struct nit_if nif;
534             struct mbuf *m;
535             int len;
536
537             for (len = 0, m = m0; m != NULL; m = m->m_next)
538                 len += m->m_len;
539             nif.nif_header = (caddr_t) &snit_ehdr;
540             nif.nif_hdrlen = sizeof(snit_ehdr);
541             nif.nif_bodylen = len;
542             nif.nif_promisc = 0;
543             snit_intr(ifp, m0, &nif);
544         }
545 #endif
546         break;
547
548     default:
549         m_freem(m0);
550         return EAFNOSUPPORT;
551     }
552
553     ++ifp->if_opackets;
554     mp = make_message(m0, PPP_HDRLEN);
555     m_freem(m0);
556     if (mp == 0) {
557         ++ifp->if_oerrors;
558         return ENOBUFS;
559     }
560     mp->b_rptr -= PPP_HDRLEN;
561     mp->b_rptr[0] = PPP_ALLSTATIONS;
562     mp->b_rptr[1] = PPP_UI;
563     mp->b_rptr[2] = proto >> 8;
564     mp->b_rptr[3] = proto;
565
566     s = splstr();
567     if (sp->flags & DBGLOG)
568         printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n",
569                sp->q, mp, mp->b_rptr, mp->b_wptr, proto);
570     putnext(sp->q, mp);
571     splx(s);
572
573     return 0;
574 }
575
576 /*
577  * Socket ioctl routine for ppp interfaces.
578  */
579 static int
580 if_ppp_ioctl(ifp, cmd, data)
581     struct ifnet *ifp;
582     u_int cmd;
583     caddr_t data;
584 {
585     int s, error;
586     struct ifreq *ifr = (struct ifreq *) data;
587     struct ifaddr *ifa = (struct ifaddr *) data;
588
589     error = 0;
590     s = splimp();
591     switch (cmd) {
592     case SIOCSIFFLAGS:
593         if ((ifp->if_flags & IFF_RUNNING) == 0)
594             ifp->if_flags &= ~IFF_UP;
595         break;
596
597     case SIOCSIFADDR:
598         if (IFA_ADDR(ifa).sa_family != AF_INET)
599             error = EAFNOSUPPORT;
600         break;
601
602     case SIOCSIFDSTADDR:
603         if (IFA_ADDR(ifa).sa_family != AF_INET)
604             error = EAFNOSUPPORT;
605         break;
606
607     case SIOCSIFMTU:
608         if ((error = NOTSUSER()) != 0)
609             break;
610         if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) {
611             error = EINVAL;
612             break;
613         }
614         ifp->if_mtu = ifr->ifr_mtu;
615         break;
616
617     case SIOCGIFMTU:
618         ifr->ifr_mtu = ifp->if_mtu;
619         break;
620
621     case SIOCADDMULTI:
622     case SIOCDELMULTI:
623         switch(ifr->ifr_addr.sa_family) {
624         case AF_INET:
625             break;
626         default:
627             error = EAFNOSUPPORT;
628             break;
629         }
630         break;
631
632     default:
633         error = EINVAL;
634     }
635     splx(s);
636     return (error);
637 }
638
639 /*
640  * Turn a STREAMS message into an mbuf chain.
641  */
642 static struct mbuf *
643 make_mbufs(mp, off)
644     mblk_t *mp;
645     int off;
646 {
647     struct mbuf *head, **prevp, *m;
648     int len, space, n;
649     unsigned char *cp, *dp;
650
651     len = msgdsize(mp);
652     if (len == 0)
653         return 0;
654     prevp = &head;
655     space = 0;
656     cp = mp->b_rptr;
657 #ifdef __osf__
658     MGETHDR(m, M_DONTWAIT, MT_DATA);
659     m->m_len = 0;
660     space = MHLEN;
661     *prevp = m;
662     prevp = &m->m_next;
663     dp = mtod(m, unsigned char *);
664     len -= space;
665     off = 0;
666 #endif
667     for (;;) {
668         while (cp >= mp->b_wptr) {
669             mp = mp->b_cont;
670             if (mp == 0) {
671                 *prevp = 0;
672                 return head;
673             }
674             cp = mp->b_rptr;
675         }
676         n = mp->b_wptr - cp;
677         if (space == 0) {
678             MGET(m, M_DONTWAIT, MT_DATA);
679             *prevp = m;
680             if (m == 0) {
681                 if (head != 0)
682                     m_freem(head);
683                 return 0;
684             }
685             if (len + off > 2 * MLEN) {
686 #ifdef __osf__
687                 MCLGET(m, M_DONTWAIT);
688 #else
689                 MCLGET(m);
690 #endif
691             }
692 #ifdef __osf__
693             space = ((m->m_flags & M_EXT) ? MCLBYTES : MLEN);
694 #else
695             space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off;
696             m->m_off += off;
697 #endif
698             m->m_len = 0;
699             len -= space;
700             dp = mtod(m, unsigned char *);
701             off = 0;
702             prevp = &m->m_next;
703         }
704         if (n > space)
705             n = space;
706         bcopy(cp, dp, n);
707         cp += n;
708         dp += n;
709         space -= n;
710         m->m_len += n;
711     }
712 }
713
714 /*
715  * Turn an mbuf chain into a STREAMS message.
716  */
717 #define ALLOCB_MAX      4096
718
719 static mblk_t *
720 make_message(m, off)
721     struct mbuf *m;
722     int off;
723 {
724     mblk_t *head, **prevp, *mp;
725     int len, space, n, nb;
726     unsigned char *cp, *dp;
727     struct mbuf *nm;
728
729     len = 0;
730     for (nm = m; nm != 0; nm = nm->m_next)
731         len += nm->m_len;
732     prevp = &head;
733     space = 0;
734     cp = mtod(m, unsigned char *);
735     nb = m->m_len;
736     for (;;) {
737         while (nb <= 0) {
738             m = m->m_next;
739             if (m == 0) {
740                 *prevp = 0;
741                 return head;
742             }
743             cp = mtod(m, unsigned char *);
744             nb = m->m_len;
745         }
746         if (space == 0) {
747             space = len + off;
748             if (space > ALLOCB_MAX)
749                 space = ALLOCB_MAX;
750             mp = allocb(space, BPRI_LO);
751             *prevp = mp;
752             if (mp == 0) {
753                 if (head != 0)
754                     freemsg(head);
755                 return 0;
756             }
757             dp = mp->b_rptr += off;
758             space -= off;
759             len -= space;
760             off = 0;
761             prevp = &mp->b_cont;
762         }
763         n = nb < space? nb: space;
764         bcopy(cp, dp, n);
765         cp += n;
766         dp += n;
767         nb -= n;
768         space -= n;
769         mp->b_wptr = dp;
770     }
771 }
772
773 /*
774  * Digital UNIX doesn't allow for removing ifnet structures
775  * from the list.  But then we're not using this as a loadable
776  * module anyway, so that's OK.
777  *
778  * Under SunOS, this should allow the module to be unloaded.
779  * Unfortunately, it doesn't seem to detach all the references,
780  * so your system may well crash after you unload this module :-(
781  */
782 #ifndef __osf__
783
784 /*
785  * Remove an interface from the system.
786  * This routine contains magic.
787  */
788 #include <net/route.h>
789 #include <netinet/in_pcb.h>
790 #include <netinet/ip_var.h>
791 #include <netinet/tcp.h>
792 #include <netinet/tcp_timer.h>
793 #include <netinet/tcp_var.h>
794 #include <netinet/udp.h>
795 #include <netinet/udp_var.h>
796
797 static void
798 ppp_if_detach(ifp)
799     struct ifnet *ifp;
800 {
801     int s;
802     struct inpcb *pcb;
803     struct ifaddr *ifa;
804     struct in_ifaddr **inap;
805     struct ifnet **ifpp;
806
807     s = splhigh();
808
809     /*
810      * Clear the interface from any routes currently cached in
811      * TCP or UDP protocol control blocks.
812      */
813     for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next)
814         if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
815             in_losing(pcb);
816     for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next)
817         if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
818             in_losing(pcb);
819
820     /*
821      * Delete routes through all addresses of the interface.
822      */
823     for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) {
824         rtinit(ifa, ifa, SIOCDELRT, RTF_HOST);
825         rtinit(ifa, ifa, SIOCDELRT, 0);
826     }
827
828     /*
829      * Unlink the interface's address(es) from the in_ifaddr list.
830      */
831     for (inap = &in_ifaddr; *inap != 0; ) {
832         if ((*inap)->ia_ifa.ifa_ifp == ifp)
833             *inap = (*inap)->ia_next;
834         else
835             inap = &(*inap)->ia_next;
836     }
837
838     /*
839      * Delete the interface from the ifnet list.
840      */
841     for (ifpp = &ifnet; (*ifpp) != 0; ) {
842         if (*ifpp == ifp)
843             break;
844         ifpp = &(*ifpp)->if_next;
845     }
846     if (*ifpp == 0)
847         printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit);
848     else
849         *ifpp = ifp->if_next;
850
851     splx(s);
852 }
853
854 #endif /* __osf__ */