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