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