don't use options.leaf any more
[ppp.git] / sunos4 / 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.1 1995/12/19 00:05:59 paulus Exp $
28  */
29
30 /*
31  * This file is used under SunOS 4.
32  */
33
34 #define INET    1
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/stream.h>
39 #include <sys/errno.h>
40 #include <sys/kmem_alloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/socket.h>
43 #include <sys/sockio.h>
44 #include <net/if.h>
45 #include <net/netisr.h>
46 #include <net/ppp_defs.h>
47 #include <net/pppio.h>
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50
51 #define NOTSUSER()      (suser()? 0: EPERM)
52 #define ifr_mtu         ifr_metric
53
54 #define ZALLOC(n, t, how)       ((t *) kmem_zalloc((n) * sizeof(t), (how)))
55 #define ZFREE(p, n, t)          kmem_free((void *)(p), (n) * sizeof(t))
56
57 #define PPP_MINMTU      64
58 #define PPP_MAXMTU      65536
59
60 typedef unsigned char uchar_t;
61 typedef unsigned short ushort_t;
62
63 static int if_ppp_open __P((queue_t *, int, int, int));
64 static int if_ppp_close __P((queue_t *, int));
65 static int if_ppp_wput __P((queue_t *, mblk_t *));
66 static int if_ppp_rput __P((queue_t *, mblk_t *));
67
68 static struct module_info minfo = {
69     0x8021, "if_ppp", 0, INFPSZ, 4096, 128
70 };
71
72 static struct qinit rinit = {
73     if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL
74 };
75
76 static struct qinit winit = {
77     if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
78 };
79
80 struct streamtab if_pppinfo = {
81     &rinit, &winit, NULL, NULL
82 };
83
84 typedef struct if_ppp_state {
85     int unit;
86     queue_t *q;
87     int flags;
88 } if_ppp_t;
89
90 /* Values for flags */
91 #define DBGLOG          1
92
93 static int if_ppp_count;        /* Number of currently-active streams */
94
95 static int ppp_nalloc;          /* Number of elements of ifs and states */
96 static struct ifnet **ifs;      /* Array of pointers to interface structs */
97 static if_ppp_t **states;       /* Array of pointers to state structs */
98
99 static int if_ppp_output __P((struct ifnet *, struct mbuf *,
100                               struct sockaddr *));
101 static int if_ppp_ioctl __P((struct ifnet *, u_long, caddr_t));
102 static struct mbuf *make_mbufs __P((mblk_t *, int));
103 static mblk_t *make_message __P((struct mbuf *, int));
104 static void ppp_if_detach __P((struct ifnet *));
105
106 /*
107  * Detach all the interfaces before unloading.
108  * Not sure this works.
109  */
110 int
111 if_ppp_unload()
112 {
113     int i;
114
115     if (if_ppp_count > 0)
116         return EBUSY;
117     for (i = 0; i < ppp_nalloc; ++i)
118         if (ifs[i] != 0)
119             ppp_if_detach(ifs[i]);
120     ZFREE(ifs, ppp_nalloc, struct ifnet *);
121     ZFREE(states, ppp_nalloc, if_ppp_t *);
122     ppp_nalloc = 0;
123     return 0;
124 }
125
126 /*
127  * STREAMS module entry points.
128  */
129 static int
130 if_ppp_open(q, dev, flag, sflag)
131     queue_t *q;
132     int dev;
133     int flag, sflag;
134 {
135     if_ppp_t *sp;
136
137     if (q->q_ptr == 0) {
138         sp = ZALLOC(1, if_ppp_t, KMEM_SLEEP);
139         if (sp == 0)
140             return OPENFAIL;
141         q->q_ptr = (caddr_t) sp;
142         WR(q)->q_ptr = (caddr_t) sp;
143         sp->unit = -1;          /* no interface unit attached at present */
144         sp->q = WR(q);
145         sp->flags = 0;
146         ++if_ppp_count;
147     }
148     return 0;
149 }
150
151 static int
152 if_ppp_close(q, flag)
153     queue_t *q;
154     int flag;
155 {
156     if_ppp_t *sp;
157     struct ifnet *ifp;
158
159     sp = (if_ppp_t *) q->q_ptr;
160     if (sp != 0) {
161         if (sp->flags & DBGLOG)
162             printf("if_ppp closed, q=%x sp=%x\n", q, sp);
163         if (sp->unit >= 0) {
164             if (sp->unit < ppp_nalloc) {
165                 states[sp->unit] = 0;
166                 ifp = ifs[sp->unit];
167                 if (ifp != 0)
168                     ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
169 #ifdef DEBUG
170             } else {
171                 printf("if_ppp: unit %d nonexistent!\n", sp->unit);
172 #endif
173             }
174         }
175         ZFREE(sp, 1, if_ppp_t);
176         --if_ppp_count;
177     }
178     return 0;
179 }
180
181 static int
182 if_ppp_wput(q, mp)
183     queue_t *q;
184     mblk_t *mp;
185 {
186     if_ppp_t *sp;
187     struct iocblk *iop;
188     int error, unit;
189     struct ifnet *ifp;
190
191     sp = (if_ppp_t *) q->q_ptr;
192     switch (mp->b_datap->db_type) {
193     case M_DATA:
194         /*
195          * Now why would we be getting data coming in here??
196          */
197         if (sp->flags & DBGLOG)
198             printf("if_ppp: got M_DATA len=%d\n", msgdsize(mp));
199         freemsg(mp);
200         break;
201
202     case M_IOCTL:
203         iop = (struct iocblk *) mp->b_rptr;
204         error = EINVAL;
205
206         if (sp->flags & DBGLOG)
207             printf("if_ppp: got ioctl cmd=%x count=%d\n",
208                    iop->ioc_cmd, iop->ioc_count);
209
210         switch (iop->ioc_cmd) {
211         case PPPIO_NEWPPA:              /* well almost */
212             if (iop->ioc_count != sizeof(int) || sp->unit >= 0)
213                 break;
214             if ((error = NOTSUSER()) != 0)
215                 break;
216             unit = *(int *)mp->b_cont->b_rptr;
217
218             /* Check that this unit isn't already in use */
219             if (unit < ppp_nalloc && states[unit] != 0) {
220                 error = EADDRINUSE;
221                 break;
222             }
223
224             /* Extend ifs and states arrays if necessary. */
225             error = ENOSR;
226             if (unit >= ppp_nalloc) {
227                 int newn;
228                 struct ifnet **newifs;
229                 if_ppp_t **newstates;
230
231                 newn = unit + 4;
232                 if (sp->flags & DBGLOG)
233                     printf("if_ppp: extending ifs to %d\n", newn);
234                 newifs = ZALLOC(newn, struct ifnet *, KMEM_NOSLEEP);
235                 if (newifs == 0)
236                     break;
237                 newstates = ZALLOC(newn, if_ppp_t *, KMEM_NOSLEEP);
238                 if (newstates == 0) {
239                     ZFREE(newifs, newn, struct ifnet *);
240                     break;
241                 }
242                 bcopy(ifs, newifs, ppp_nalloc * sizeof(struct ifnet *));
243                 bcopy(states, newstates, ppp_nalloc * sizeof(if_ppp_t *));
244                 ifs = newifs;
245                 states = newstates;
246                 ppp_nalloc = newn;
247             }
248
249             /* Allocate a new ifnet struct if necessary. */
250             ifp = ifs[unit];
251             if (ifp == 0) {
252                 ifp = ZALLOC(1, struct ifnet, KMEM_NOSLEEP);
253                 if (ifp == 0)
254                     break;
255                 ifs[unit] = ifp;
256                 ifp->if_name = "ppp";
257                 ifp->if_unit = unit;
258                 ifp->if_mtu = PPP_MRU;
259                 ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
260 #ifdef IFF_MULTICAST
261                 ifp->if_flags |= IFF_MULTICAST;
262 #endif
263                 ifp->if_output = if_ppp_output;
264                 ifp->if_ioctl = if_ppp_ioctl;
265                 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
266                 if_attach(ifp);
267                 if (sp->flags & DBGLOG)
268                     printf("if_ppp: created unit %d\n", unit);
269             } else {
270                 ifp->if_mtu = PPP_MRU;
271                 ifp->if_flags |= IFF_RUNNING;
272             }
273
274             states[unit] = sp;
275             sp->unit = unit;
276
277             error = 0;
278             iop->ioc_count = 0;
279             if (sp->flags & DBGLOG)
280                 printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit,
281                        sp, sp->q);
282             break;
283
284         case PPPIO_DEBUG:
285             error = -1;
286             if (iop->ioc_count == sizeof(int)) {
287                 if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) {
288                     printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp);
289                     sp->flags |= DBGLOG;
290                     error = 0;
291                     iop->ioc_count = 0;
292                 }
293             }
294             break;
295
296         default:
297             error = -1;
298             break;
299         }
300
301         if (sp->flags & DBGLOG)
302             printf("if_ppp: ioctl result %d\n", error);
303         if (error < 0)
304             putnext(q, mp);
305         else if (error == 0) {
306             mp->b_datap->db_type = M_IOCACK;
307             qreply(q, mp);
308         } else {
309             mp->b_datap->db_type = M_IOCNAK;
310             iop->ioc_count = 0;
311             iop->ioc_error = error;
312             qreply(q, mp);
313         }
314         break;
315
316     default:
317         putnext(q, mp);
318     }
319     return 0;
320 }
321
322 static int
323 if_ppp_rput(q, mp)
324     queue_t *q;
325     mblk_t *mp;
326 {
327     if_ppp_t *sp;
328     int proto, s;
329     struct mbuf *mb;
330     struct ifqueue *inq;
331     struct ifnet *ifp;
332
333     sp = (if_ppp_t *) q->q_ptr;
334     switch (mp->b_datap->db_type) {
335     case M_DATA:
336         /*
337          * Convert the message into an mbuf chain
338          * and inject it into the network code.
339          */
340         if (sp->flags & DBGLOG)
341             printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n",
342                    msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2],
343                    mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6],
344                    mp->b_rptr[7]);
345
346         if (sp->unit < 0) {
347             freemsg(mp);
348             break;
349         }
350         if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) {
351 #ifdef DEBUG
352             printf("if_ppp: no unit %d!\n", sp->unit);
353 #endif
354             freemsg(mp);
355             break;
356         }
357
358         if ((ifp->if_flags & IFF_UP) == 0) {
359             freemsg(mp);
360             break;
361         }
362         ++ifp->if_ipackets;
363
364         proto = PPP_PROTOCOL(mp->b_rptr);
365         adjmsg(mp, PPP_HDRLEN);
366         mb = make_mbufs(mp, sizeof(struct ifnet *));
367         freemsg(mp);
368         if (mb == NULL) {
369             if (sp->flags & DBGLOG)
370                 printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit);
371             ++ifp->if_ierrors;
372             break;
373         }
374         mb->m_off -= sizeof(struct ifnet *);
375         mb->m_len += sizeof(struct ifnet *);
376         *mtod(mb, struct ifnet **) = ifp;
377
378         inq = 0;
379         switch (proto) {
380         case PPP_IP:
381             inq = &ipintrq;
382             schednetisr(NETISR_IP);
383         }
384
385         if (inq != 0) {
386             s = splhigh();
387             if (IF_QFULL(inq)) {
388                 IF_DROP(inq);
389                 ++ifp->if_ierrors;
390                 if (sp->flags & DBGLOG)
391                     printf("if_ppp: inq full, proto=%x\n", proto);
392                 m_freem(mb);
393             } else {
394                 IF_ENQUEUE(inq, mb);
395             }
396             splx(s);
397         } else {
398             if (sp->flags & DBGLOG)
399                 printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto);
400             ++ifp->if_ierrors;
401             m_freem(mb);
402         }
403         break;
404
405     default:
406         putnext(q, mp);
407     }
408     return 0;
409 }
410
411 /*
412  * Network code wants to output a packet.
413  * Turn it into a STREAMS message and send it down.
414  */
415 static int
416 if_ppp_output(ifp, m0, dst)
417     struct ifnet *ifp;
418     struct mbuf *m0;
419     struct sockaddr *dst;
420 {
421     mblk_t *mp;
422     int proto, s;
423     if_ppp_t *sp;
424     u_char *p;
425
426     if ((ifp->if_flags & IFF_UP) == 0) {
427         m_freem(m0);
428         return ENETDOWN;
429     }
430
431     if ((unsigned)ifp->if_unit >= ppp_nalloc) {
432 #ifdef DEBUG
433         printf("if_ppp_output: unit %d?\n", ifp->if_unit);
434 #endif
435         m_freem(m0);
436         return EINVAL;
437     }
438     sp = states[ifp->if_unit];
439     if (sp == 0) {
440 #ifdef DEBUG
441         printf("if_ppp_output: no queue?\n");
442 #endif
443         m_freem(m0);
444         return ENETDOWN;
445     }
446
447     if (sp->flags & DBGLOG) {
448         p = mtod(m0, u_char *);
449         printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n",
450                ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4],
451                p[5], p[6], p[7], sp->q);
452     }
453
454     switch (dst->sa_family) {
455     case AF_INET:
456         proto = PPP_IP;
457         break;
458     default:
459         m_freem(m0);
460         return EAFNOSUPPORT;
461     }
462
463     ++ifp->if_opackets;
464     mp = make_message(m0, PPP_HDRLEN);
465     m_freem(m0);
466     if (mp == 0) {
467         ++ifp->if_oerrors;
468         return ENOBUFS;
469     }
470     mp->b_rptr -= PPP_HDRLEN;
471     mp->b_rptr[0] = PPP_ALLSTATIONS;
472     mp->b_rptr[1] = PPP_UI;
473     mp->b_rptr[2] = proto >> 8;
474     mp->b_rptr[3] = proto;
475
476     s = splstr();
477     if (sp->flags & DBGLOG)
478         printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n",
479                sp->q, mp, mp->b_rptr, mp->b_wptr, proto);
480     putnext(sp->q, mp);
481     splx(s);
482
483     return 0;
484 }
485
486 /*
487  * Socket ioctl routine for ppp interfaces.
488  */
489 static int
490 if_ppp_ioctl(ifp, cmd, data)
491     struct ifnet *ifp;
492     u_long cmd;
493     caddr_t data;
494 {
495     int s, error;
496     struct ifreq *ifr = (struct ifreq *) data;
497     struct ifaddr *ifa = (struct ifaddr *) data;
498
499     error = 0;
500     s = splimp();
501     switch (cmd) {
502     case SIOCSIFFLAGS:
503         if ((ifp->if_flags & IFF_RUNNING) == 0)
504             ifp->if_flags &= ~IFF_UP;
505         break;
506
507     case SIOCSIFADDR:
508         if (ifa->ifa_addr.sa_family != AF_INET)
509             error = EAFNOSUPPORT;
510         break;
511
512     case SIOCSIFDSTADDR:
513         if (ifa->ifa_addr.sa_family != AF_INET)
514             error = EAFNOSUPPORT;
515         break;
516
517     case SIOCSIFMTU:
518         if ((error = NOTSUSER()) != 0)
519             break;
520         if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) {
521             error = EINVAL;
522             break;
523         }
524         ifp->if_mtu = ifr->ifr_mtu;
525         break;
526
527     case SIOCGIFMTU:
528         ifr->ifr_mtu = ifp->if_mtu;
529         break;
530
531     case SIOCADDMULTI:
532     case SIOCDELMULTI:
533         switch(ifr->ifr_addr.sa_family) {
534         case AF_INET:
535             break;
536         default:
537             error = EAFNOSUPPORT;
538             break;
539         }
540         break;
541
542     default:
543         error = EINVAL;
544     }
545     splx(s);
546     return (error);
547 }
548
549 /*
550  * Turn a STREAMS message into an mbuf chain.
551  */
552 static struct mbuf *
553 make_mbufs(mp, off)
554     mblk_t *mp;
555     int off;
556 {
557     struct mbuf *head, **prevp, *m;
558     int len, space, n;
559     unsigned char *cp, *dp;
560
561     len = msgdsize(mp);
562     if (len == 0)
563         return 0;
564     prevp = &head;
565     space = 0;
566     cp = mp->b_rptr;
567     for (;;) {
568         while (cp >= mp->b_wptr) {
569             mp = mp->b_cont;
570             if (mp == 0) {
571                 *prevp = 0;
572                 return head;
573             }
574             cp = mp->b_rptr;
575         }
576         n = mp->b_wptr - cp;
577         if (space == 0) {
578             MGET(m, M_DONTWAIT, MT_DATA);
579             *prevp = m;
580             if (m == 0) {
581                 if (head != 0)
582                     m_freem(head);
583                 return 0;
584             }
585             if (len + off > 2 * MLEN) {
586                 MCLGET(m);
587             }
588             space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off;
589             m->m_off += off;
590             m->m_len = 0;
591             len -= space;
592             dp = mtod(m, unsigned char *);
593             off = 0;
594             prevp = &m->m_next;
595         }
596         if (n > space)
597             n = space;
598         bcopy(cp, dp, n);
599         cp += n;
600         dp += n;
601         space -= n;
602         m->m_len += n;
603     }
604 }
605
606 /*
607  * Turn an mbuf chain into a STREAMS message.
608  */
609 #define ALLOCB_MAX      4096
610
611 static mblk_t *
612 make_message(m, off)
613     struct mbuf *m;
614     int off;
615 {
616     mblk_t *head, **prevp, *mp;
617     int len, space, n, nb;
618     unsigned char *cp, *dp;
619     struct mbuf *nm;
620
621     len = 0;
622     for (nm = m; nm != 0; nm = nm->m_next)
623         len += nm->m_len;
624     prevp = &head;
625     space = 0;
626     cp = mtod(m, unsigned char *);
627     nb = m->m_len;
628     for (;;) {
629         while (nb <= 0) {
630             m = m->m_next;
631             if (m == 0) {
632                 *prevp = 0;
633                 return head;
634             }
635             cp = mtod(m, unsigned char *);
636             nb = m->m_len;
637         }
638         if (space == 0) {
639             space = len + off;
640             if (space > ALLOCB_MAX)
641                 space = ALLOCB_MAX;
642             mp = allocb(space, BPRI_LO);
643             *prevp = mp;
644             if (mp == 0) {
645                 if (head != 0)
646                     freemsg(head);
647                 return 0;
648             }
649             dp = mp->b_rptr += off;
650             space -= off;
651             len -= space;
652             off = 0;
653             prevp = &mp->b_cont;
654         }
655         n = nb < space? nb: space;
656         bcopy(cp, dp, n);
657         cp += n;
658         dp += n;
659         nb -= n;
660         space -= n;
661         mp->b_wptr = dp;
662     }
663 }
664
665 /*
666  * Remove an interface from the system.
667  * This routine contains magic.
668  */
669 #include <net/route.h>
670 #include <netinet/in_pcb.h>
671 #include <netinet/ip_var.h>
672 #include <netinet/tcp.h>
673 #include <netinet/tcp_timer.h>
674 #include <netinet/tcp_var.h>
675 #include <netinet/udp.h>
676 #include <netinet/udp_var.h>
677
678 static void
679 ppp_if_detach(ifp)
680     struct ifnet *ifp;
681 {
682     int s;
683     struct inpcb *pcb;
684     struct ifaddr *ifa;
685     struct in_ifaddr **inap;
686     struct ifnet **ifpp;
687
688     s = splhigh();
689
690     /*
691      * Clear the interface from any routes currently cached in
692      * TCP or UDP protocol control blocks.
693      */
694     for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next)
695         if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
696             in_losing(pcb);
697     for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next)
698         if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
699             in_losing(pcb);
700
701     /*
702      * Delete routes through all addresses of the interface.
703      */
704     for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) {
705         rtinit(ifa, ifa, SIOCDELRT, RTF_HOST);
706         rtinit(ifa, ifa, SIOCDELRT, 0);
707     }
708
709     /*
710      * Unlink the interface's address(es) from the in_ifaddr list.
711      */
712     for (inap = &in_ifaddr; *inap != 0; ) {
713         if ((*inap)->ia_ifa.ifa_ifp == ifp)
714             *inap = (*inap)->ia_next;
715         else
716             inap = &(*inap)->ia_next;
717     }
718
719     /*
720      * Delete the interface from the ifnet list.
721      */
722     for (ifpp = &ifnet; (*ifpp) != 0; ) {
723         if (*ifpp == ifp)
724             break;
725         ifpp = &(*ifpp)->if_next;
726     }
727     if (*ifpp == 0)
728         printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit);
729     else
730         *ifpp = ifp->if_next;
731
732     splx(s);
733 }