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