]> git.ozlabs.org Git - ppp.git/blob - svr4/ppp.c
Initial revision
[ppp.git] / svr4 / ppp.c
1 #include <sys/types.h>
2 #include <sys/param.h>
3 #include <sys/stat.h>
4 #include <sys/stream.h>
5 #include <sys/stropts.h>
6 #include <sys/errno.h>
7 #include <sys/cmn_err.h>
8 #include <sys/modctl.h>
9 #include <sys/conf.h>
10 #include <sys/ddi.h>
11 #include <sys/sunddi.h>
12 #include <sys/dlpi.h>
13 #include <sys/ioccom.h>
14 #include <net/ppp_defs.h>
15 #include <net/pppio.h>
16
17 #ifdef __STDC__
18 #define __P(x)  x
19 #else
20 #define __P(x)  ()
21 #endif
22
23 /*
24  * The IP module uses this SAP value for IP packets.
25  */
26 #ifndef ETHERTYPE_IP
27 #define ETHERTYPE_IP    0x800
28 #endif
29
30 #ifndef PPP_MAXMTU
31 #define PPP_MAXMTU      65536
32 #endif
33
34 /*
35  * Private information; one per upper stream.
36  */
37 struct upperstr {
38     minor_t mn;                 /* minor device number */
39     queue_t *q;                 /* read q associated with this upper stream */
40     int flags;                  /* flag bits, see below */
41     int state;                  /* current DLPI state */
42     int sap;                    /* service access point */
43     struct upperstr *ppa;       /* control stream for our ppa */
44     struct upperstr *next;      /* next stream for this ppa */
45     /*
46      * There is exactly one control stream for each PPA.
47      * The following fields are only used for control streams.
48      */
49     int ppa_id;
50     queue_t *lowerq;            /* write queue attached below this PPA */
51     struct upperstr *nextppa;   /* next control stream */
52 };
53
54 /* Values for flags */
55 #define US_PRIV         1       /* stream was opened by superuser */
56 #define US_CONTROL      2       /* stream is a control stream */
57 #define US_BLOCKED      4       /* flow ctrl has blocked lower stream */
58
59 static void *upper_states;
60 static struct upperstr *ppas;
61
62 static int ppp_identify __P((dev_info_t *));
63 static int ppp_attach __P((dev_info_t *, ddi_attach_cmd_t));
64 static int ppp_detach __P((dev_info_t *, ddi_detach_cmd_t));
65 static int ppp_devinfo __P((dev_info_t *, ddi_info_cmd_t, void *, void **));
66 static int pppopen __P((queue_t *, dev_t *, int, int, cred_t *));
67 static int pppclose __P((queue_t *, int, cred_t *));
68 static int pppuwput __P((queue_t *, mblk_t *));
69 static int pppursrv __P((queue_t *));
70 static int pppuwsrv __P((queue_t *));
71 static int ppplrput __P((queue_t *, mblk_t *));
72 static int ppplrsrv __P((queue_t *));
73 static int ppplwsrv __P((queue_t *));
74 static void dlpi_request __P((queue_t *, mblk_t *, struct upperstr *));
75 static void dlpi_error __P((queue_t *, int, int, int));
76 static void dlpi_ok __P((queue_t *, int));
77 static int send_data __P((mblk_t *, struct upperstr *));
78 static void new_ppa __P((queue_t *, mblk_t *));
79 static struct upperstr *find_dest __P((struct upperstr *, int));
80
81 static struct module_info ppp_info = {
82     0xb1a6, "ppp", 0, 512, 512, 128
83 };
84
85 static struct qinit pppurint = {
86     NULL, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL
87 };
88
89 static struct qinit pppuwint = {
90     pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL
91 };
92
93 static struct qinit ppplrint = {
94     ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL
95 };
96
97 static struct qinit ppplwint = {
98     NULL, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL
99 };
100
101 static struct streamtab pppinfo = {
102     &pppurint, &pppuwint,
103     &ppplrint, &ppplwint
104 };
105
106 static dev_info_t *ppp_dip;
107
108 static struct cb_ops cb_ppp_ops = {
109     nulldev, nulldev, nodev, nodev,     /* cb_open, ... */
110     nodev, nodev, nodev, nodev,         /* cb_dump, ... */
111     nodev, nodev, nodev, nochpoll,      /* cb_devmap, ... */
112     ddi_prop_op,                        /* cb_prop_op */
113     &pppinfo,                           /* cb_stream */
114     D_NEW|D_MP|D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL        /* cb_flag */
115 };
116
117 static struct dev_ops ppp_ops = {
118     DEVO_REV,                           /* devo_rev */
119     0,                                  /* devo_refcnt */
120     ppp_devinfo,                        /* devo_getinfo */
121     ppp_identify,                       /* devo_identify */
122     nulldev,                            /* devo_probe */
123     ppp_attach,                         /* devo_attach */
124     ppp_detach,                         /* devo_detach */
125     nodev,                              /* devo_reset */
126     &cb_ppp_ops,                        /* devo_cb_ops */
127     NULL                                /* devo_bus_ops */
128 };
129
130 /*
131  * Module linkage information
132  */
133
134 static struct modldrv modldrv = {
135     &mod_driverops,                     /* says this is a pseudo driver */
136     "PPP-2.2 multiplexing driver",
137     &ppp_ops                            /* driver ops */
138 };
139
140 static struct modlinkage modlinkage = {
141     MODREV_1,
142     (void *) &modldrv,
143     NULL
144 };
145
146 int
147 _init(void)
148 {
149     int error;
150
151     error = ddi_soft_state_init(&upper_states, sizeof(struct upperstr), 4);
152     if (!error) {
153         error = mod_install(&modlinkage);
154         if (!error)
155             return 0;
156         ddi_soft_state_fini(&upper_states);
157     }
158     return error;
159 }
160
161 int
162 _fini(void)
163 {
164     int error;
165
166     error = mod_remove(&modlinkage);
167     if (error)
168         return error;
169     ddi_soft_state_fini(&upper_states);
170     return 0;
171 }
172
173 int
174 _info(mip)
175     struct modinfo *mip;
176 {
177     return mod_info(&modlinkage, mip);
178 }
179
180 static int
181 ppp_identify(dip)
182     dev_info_t *dip;
183 {
184     return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED:
185         DDI_NOT_IDENTIFIED;
186 }
187
188 static int
189 ppp_attach(dip, cmd)
190     dev_info_t *dip;
191     ddi_attach_cmd_t cmd;
192 {
193
194     if (cmd != DDI_ATTACH)
195         return DDI_FAILURE;
196     if (ddi_create_minor_node(dip, "ppp", S_IFCHR, 0, DDI_PSEUDO, CLONE_DEV)
197         == DDI_FAILURE) {
198         ddi_remove_minor_node(dip, NULL);
199         return DDI_FAILURE;
200     }
201     return DDI_SUCCESS;
202 }
203
204 static int
205 ppp_detach(dip, cmd)
206     dev_info_t *dip;
207     ddi_detach_cmd_t cmd;
208 {
209     ddi_remove_minor_node(dip, NULL);
210     return DDI_SUCCESS;
211 }
212
213 static int
214 ppp_devinfo(dip, cmd, arg, result)
215     dev_info_t *dip;
216     ddi_info_cmd_t cmd;
217     void *arg;
218     void **result;
219 {
220     int error;
221
222     error = DDI_SUCCESS;
223     switch (cmd) {
224     case DDI_INFO_DEVT2DEVINFO:
225         if (ppp_dip == NULL)
226             error = DDI_FAILURE;
227         else
228             *result = (void *) ppp_dip;
229         break;
230     case DDI_INFO_DEVT2INSTANCE:
231         *result = NULL;
232         break;
233     default:
234         error = DDI_FAILURE;
235     }
236     return error;
237 }
238
239 static int
240 pppopen(q, devp, oflag, sflag, credp)
241     queue_t *q;
242     dev_t *devp;
243     int oflag, sflag;
244     cred_t *credp;
245 {
246     struct upperstr *up;
247     minor_t mn;
248
249     if (q->q_ptr)
250         return 0;               /* device is already open */
251
252     if (sflag == CLONEOPEN) {
253         for (mn = 0; ddi_get_soft_state(upper_states, mn) != NULL; ++mn)
254             ;
255     } else {
256         mn = getminor(*devp);
257     }
258     cmn_err(CE_CONT, "pppopen: minor %d\n", mn);
259
260     /*
261      * Construct a new minor node.
262      */
263     if (ddi_soft_state_zalloc(upper_states, mn) != DDI_SUCCESS)
264         return ENXIO;
265     up = ddi_get_soft_state(upper_states, mn);
266     *devp = makedevice(getmajor(*devp), mn);
267     up->q = q;
268     up->mn = mn;
269     up->flags = 0;
270     if (drv_priv(credp) == 0)
271         up->flags |= US_PRIV;
272     up->state = DL_UNATTACHED;
273     up->sap = -1;
274     up->ppa = 0;
275     up->next = 0;
276     up->lowerq = 0;
277     q->q_ptr = up;
278     WR(q)->q_ptr = up;
279     noenable(WR(q));
280
281     qprocson(q);
282     return 0;
283 }
284
285 static int
286 pppclose(q, flag, credp)
287     queue_t *q;
288     int flag;
289     cred_t *credp;
290 {
291     struct upperstr *up, **upp;
292     struct upperstr *as, *asnext;
293     struct lowerstr *ls;
294
295     qprocsoff(q);
296
297     up = (struct upperstr *) q->q_ptr;
298     cmn_err(CE_CONT, "pppclose: minor %d\n", up->mn);
299     if (up->flags & US_CONTROL) {
300         /*
301          * This stream represents a PPA:
302          * For all streams attached to the PPA, clear their
303          * references to this PPA.
304          * Then remove this PPA from the list of PPAs.
305          */
306         for (as = up->next; as != 0; as = asnext) {
307             asnext = as->next;
308             as->next = 0;
309             as->ppa = 0;
310             if (as->flags & US_BLOCKED) {
311                 as->flags &= ~US_BLOCKED;
312                 flushq(WR(as->q), FLUSHDATA);
313             }
314         }
315         for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa)
316             if (*upp == up) {
317                 *upp = up->nextppa;
318                 break;
319             }
320
321     } else {
322         /*
323          * If this stream is attached to a PPA,
324          * remove it from the PPA's list.
325          */
326         if ((as = up->ppa) != 0) {
327             for (; as->next != 0; as = as->next)
328                 if (as->next == up) {
329                     as->next = up->next;
330                     break;
331                 }
332         }
333     }
334
335     q->q_ptr = NULL;
336     WR(q)->q_ptr = NULL;
337     ddi_soft_state_free(upper_states, up->mn);
338
339     return 0;
340 }
341
342 /*
343  * A message from on high.  We do one of three things:
344  *      - qreply()
345  *      - put the message on the lower write stream
346  *      - queue it for our service routine
347  */
348 static int
349 pppuwput(q, mp)
350     queue_t *q;
351     mblk_t *mp;
352 {
353     struct upperstr *us, *usnext;
354     struct iocblk *iop;
355     struct linkblk *lb;
356     queue_t *lq;
357     int error;
358     mblk_t *mq;
359
360     us = (struct upperstr *) q->q_ptr;
361     switch (mp->b_datap->db_type) {
362     case M_PCPROTO:
363     case M_PROTO:
364         dlpi_request(q, mp, us);
365         break;
366
367     case M_DATA:
368         if (!send_data(mp, us))
369             putq(q, mp);
370         break;
371
372     case M_IOCTL:
373         iop = (struct iocblk *) mp->b_rptr;
374         cmn_err(CE_CONT, "ppp%d ioctl %x count=%d\n", us->mn, iop->ioc_cmd,
375                 iop->ioc_count);
376         switch (iop->ioc_cmd) {
377         case I_LINK:
378             if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0)
379                 goto iocnak;
380             lb = (struct linkblk *) mp->b_cont->b_rptr;
381             us->lowerq = lq = lb->l_qbot;
382             lq->q_ptr = us;
383             WR(lq)->q_ptr = us;
384             iop->ioc_count = 0;
385             mp->b_datap->db_type = M_IOCACK;
386             qreply(q, mp);
387             /* Unblock upper streams which now feed this lower stream. */
388             qenable(lq);
389             break;
390
391         case I_UNLINK:
392             lb = (struct linkblk *) mp->b_cont->b_rptr;
393             if (us->lowerq != lb->l_qbot)
394                 cmn_err(CE_CONT, "ppp unlink: lowerq=%x qbot=%x\n",
395                         us->lowerq, lb->l_qbot);
396             us->lowerq = 0;
397             iop->ioc_count = 0;
398             mp->b_datap->db_type = M_IOCACK;
399             qreply(q, mp);
400             /* Unblock streams which now feed back up the control stream. */
401             qenable(us->q);
402             break;
403
404         case PPPIO_NEWPPA:
405             if (us->flags & US_CONTROL)
406                 goto iocnak;
407             if ((us->flags & US_PRIV) == 0) {
408                 iop->ioc_error = EPERM;
409                 goto iocnak;
410             }
411             /* Arrange to return an int */
412             if ((mq = mp->b_cont) == 0
413                 || mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) {
414                 mq = allocb(sizeof(int), BPRI_HI);
415                 if (mq == 0) {
416                     iop->ioc_error = ENOSR;
417                     goto iocnak;
418                 }
419                 if (mp->b_cont != 0)
420                     freemsg(mp->b_cont);
421                 mp->b_cont = mq;
422                 mq->b_cont = 0;
423             }
424             iop->ioc_count = sizeof(int);
425             mq->b_wptr = mq->b_rptr + sizeof(int);
426             qwriter(q, mp, new_ppa, PERIM_OUTER);
427             break;
428
429         default:
430             if (us->ppa == 0 || us->ppa->lowerq == 0)
431                 goto iocnak;
432             switch (iop->ioc_cmd) {
433             case PPPIO_GETSTAT:
434             case PPPIO_GETCSTAT:
435                 break;
436             default:
437                 if ((us->flags & US_PRIV) == 0) {
438                     iop->ioc_error = EPERM;
439                     goto iocnak;
440                 }
441             }
442             putnext(us->ppa->lowerq, mp);
443             break;
444
445         iocnak:
446             mp->b_datap->db_type = M_IOCNAK;
447             qreply(q, mp);
448             break;
449         }
450         break;
451
452     case M_FLUSH:
453         if (*mp->b_rptr & FLUSHW)
454             flushq(q, FLUSHDATA);
455         if (*mp->b_rptr & FLUSHR) {
456             *mp->b_rptr &= ~FLUSHW;
457             qreply(q, mp);
458         } else
459             freemsg(mp);
460         break;
461
462     default:
463         freemsg(mp);
464         break;
465     }
466     return 0;
467 }
468
469 static void
470 dlpi_request(q, mp, us)
471     queue_t *q;
472     mblk_t *mp;
473     struct upperstr *us;
474 {
475     union DL_primitives *d = (union DL_primitives *) mp->b_rptr;
476     int size = mp->b_wptr - mp->b_rptr;
477     mblk_t *reply, *np;
478     struct upperstr *t, *ppa;
479     int sap;
480     dl_info_ack_t *info;
481     dl_bind_ack_t *ackp;
482     dl_phys_addr_ack_t *adrp;
483
484     cmn_err(CE_CONT, "ppp%d dlpi prim %x, state=%x\n", us->mn,
485             d->dl_primitive, us->state);
486     switch (d->dl_primitive) {
487     case DL_INFO_REQ:
488         if (size < sizeof(dl_info_req_t))
489             goto badprim;
490         if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0)
491             break;              /* should do bufcall */
492         reply->b_datap->db_type = M_PCPROTO;
493         info = (dl_info_ack_t *) reply->b_wptr;
494         reply->b_wptr += sizeof(dl_info_ack_t);
495         bzero((caddr_t) info, sizeof(dl_info_ack_t));
496         info->dl_primitive = DL_INFO_ACK;
497         info->dl_max_sdu = PPP_MAXMTU;
498         info->dl_min_sdu = 1;
499         info->dl_addr_length = sizeof(ulong);
500         info->dl_mac_type = DL_OTHER;
501         info->dl_current_state = us->state;
502         info->dl_sap_length = sizeof(ulong);
503         info->dl_service_mode = DL_CLDLS;
504         info->dl_provider_style = DL_STYLE2;
505         info->dl_version = DL_CURRENT_VERSION;
506         qreply(q, reply);
507         break;
508
509     case DL_ATTACH_REQ:
510         if (size < sizeof(dl_attach_req_t))
511             goto badprim;
512         if (us->state != DL_UNATTACHED || us->ppa != 0) {
513             dlpi_error(q, DL_ATTACH_REQ, DL_OUTSTATE, 0);
514             break;
515         }
516         for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
517             if (ppa->ppa_id == d->attach_req.dl_ppa)
518                 break;
519         if (ppa == 0) {
520             dlpi_error(q, DL_ATTACH_REQ, DL_BADPPA, 0);
521             break;
522         }
523         us->ppa = ppa;
524         us->state = DL_UNBOUND;
525         for (t = ppa; t->next != 0; t = t->next)
526             ;
527         t->next = us;
528         us->next = 0;
529         dlpi_ok(q, DL_ATTACH_REQ);
530         break;
531
532     case DL_DETACH_REQ:
533         if (size < sizeof(dl_detach_req_t))
534             goto badprim;
535         if (us->state != DL_UNBOUND || us->ppa == 0) {
536             dlpi_error(q, DL_DETACH_REQ, DL_OUTSTATE, 0);
537             break;
538         }
539         for (t = us->ppa; t->next != 0; t = t->next)
540             if (t->next == us) {
541                 t->next = us->next;
542                 break;
543             }
544         us->next = 0;
545         us->ppa = 0;
546         us->state = DL_UNATTACHED;
547         dlpi_ok(q, DL_DETACH_REQ);
548         break;
549
550     case DL_BIND_REQ:
551         if (size < sizeof(dl_bind_req_t))
552             goto badprim;
553         if (us->state != DL_UNBOUND) {
554             dlpi_error(q, DL_BIND_REQ, DL_OUTSTATE, 0);
555             break;
556         }
557         if (d->bind_req.dl_service_mode != DL_CLDLS) {
558             dlpi_error(q, DL_BIND_REQ, DL_UNSUPPORTED, 0);
559             break;
560         }
561         /* saps must be valid PPP network protocol numbers */
562         sap = d->bind_req.dl_sap;
563         cmn_err(CE_CONT, "ppp bind %x\n", sap);
564         if (sap == ETHERTYPE_IP)
565             sap = PPP_IP;
566         if (sap < 0x21 || sap > 0x3fff
567             || (sap & 1) == 0 || (sap & 0x100) != 0) {
568             dlpi_error(q, DL_BIND_REQ, DL_BADADDR, 0);
569             break;
570         }
571         us->sap = sap;
572         us->state = DL_IDLE;
573         if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(ulong),
574                             BPRI_HI)) == 0)
575             break;              /* should do bufcall */
576         ackp = (dl_bind_ack_t *) reply->b_wptr;
577         reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(ulong);
578         reply->b_datap->db_type = M_PCPROTO;
579         bzero((caddr_t) ackp, sizeof(dl_bind_ack_t));
580         ackp->dl_primitive = DL_BIND_ACK;
581         ackp->dl_sap = sap;
582         ackp->dl_addr_length = sizeof(ulong);
583         ackp->dl_addr_offset = sizeof(dl_bind_ack_t);
584         *(ulong *)(ackp+1) = sap;
585         qreply(q, reply);
586         break;
587
588     case DL_UNBIND_REQ:
589         if (size < sizeof(dl_unbind_req_t))
590             goto badprim;
591         if (us->state != DL_IDLE) {
592             dlpi_error(q, DL_UNBIND_REQ, DL_OUTSTATE, 0);
593             break;
594         }
595         us->sap = -1;
596         us->state = DL_UNBOUND;
597         dlpi_ok(q, DL_UNBIND_REQ);
598         break;
599
600     case DL_UNITDATA_REQ:
601         if (size < sizeof(dl_unitdata_req_t))
602             goto badprim;
603         if (us->state != DL_IDLE) {
604             dlpi_error(q, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
605             break;
606         }
607         /* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */
608         if (mp->b_datap->db_ref > 1) {
609             np = allocb(PPP_HDRLEN, BPRI_HI);
610             if (np == 0)
611                 break;          /* gak! */
612             np->b_cont = mp->b_cont;
613             mp->b_cont = 0;
614             freeb(mp);
615             mp = np;
616         }
617         /* XXX should use dl_dest_addr_offset/length here */
618         mp->b_wptr = mp->b_rptr + PPP_HDRLEN;
619         mp->b_rptr[0] = PPP_ALLSTATIONS;
620         mp->b_rptr[1] = PPP_UI;
621         mp->b_rptr[2] = us->sap >> 8;
622         mp->b_rptr[3] = us->sap;
623         if (!send_data(mp, us))
624             putq(q, mp);
625         return;
626
627     case DL_SUBS_BIND_REQ:
628     case DL_SUBS_UNBIND_REQ:
629     case DL_ENABMULTI_REQ:
630     case DL_DISABMULTI_REQ:
631     case DL_PROMISCON_REQ:
632     case DL_PROMISCOFF_REQ:
633     case DL_PHYS_ADDR_REQ:
634     case DL_SET_PHYS_ADDR_REQ:
635     case DL_XID_REQ:
636     case DL_TEST_REQ:
637     case DL_CONNECT_REQ:
638     case DL_TOKEN_REQ:
639     case DL_REPLY_UPDATE_REQ:
640     case DL_GET_STATISTICS_REQ:
641     case DL_REPLY_REQ:
642     case DL_DATA_ACK_REQ:
643         dlpi_error(q, d->dl_primitive, DL_NOTSUPPORTED, 0);
644         break;
645
646     case DL_CONNECT_RES:
647     case DL_DISCONNECT_REQ:
648     case DL_RESET_REQ:
649     case DL_RESET_RES:
650         dlpi_error(q, d->dl_primitive, DL_OUTSTATE, 0);
651         break;
652
653     case DL_UDQOS_REQ:
654         dlpi_error(q, d->dl_primitive, DL_BADQOSTYPE, 0);
655         break;
656
657     case DL_TEST_RES:
658     case DL_XID_RES:
659         break;
660
661     default:
662     badprim:
663         dlpi_error(q, d->dl_primitive, DL_BADPRIM, 0);
664         break;
665     }
666     freemsg(mp);
667 }
668
669 static void
670 dlpi_error(q, prim, err, uerr)
671     queue_t *q;
672     int prim, err, uerr;
673 {
674     mblk_t *reply;
675     dl_error_ack_t *errp;
676
677     reply = allocb(sizeof(dl_error_ack_t), BPRI_HI);
678     if (reply == 0)
679         return;                 /* XXX should do bufcall */
680     reply->b_datap->db_type = M_PCPROTO;
681     errp = (dl_error_ack_t *) reply->b_wptr;
682     reply->b_wptr += sizeof(dl_error_ack_t);
683     errp->dl_primitive = DL_ERROR_ACK;
684     errp->dl_error_primitive = prim;
685     errp->dl_errno = err;
686     errp->dl_unix_errno = uerr;
687     qreply(q, reply);
688 }
689
690 static void
691 dlpi_ok(q, prim)
692     queue_t *q;
693     int prim;
694 {
695     mblk_t *reply;
696     dl_ok_ack_t *okp;
697
698     reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI);
699     if (reply == 0)
700         return;                 /* XXX should do bufcall */
701     reply->b_datap->db_type = M_PCPROTO;
702     okp = (dl_ok_ack_t *) reply->b_wptr;
703     reply->b_wptr += sizeof(dl_ok_ack_t);
704     okp->dl_primitive = DL_OK_ACK;
705     okp->dl_correct_primitive = prim;
706     qreply(q, reply);
707 }
708
709 static int
710 send_data(mp, us)
711     mblk_t *mp;
712     struct upperstr *us;
713 {
714     queue_t *q;
715     struct upperstr *ppa;
716
717     if (us->flags & US_BLOCKED)
718         return 0;
719     ppa = us->ppa;
720     if (ppa == 0) {
721         freemsg(mp);
722         return 1;
723     }
724     if ((q = ppa->lowerq) == 0) {
725         /* try to send it up the control stream */
726         q = ppa->q;
727     }
728     if (canputnext(q)) {
729         putnext(q, mp);
730         return 1;
731     }
732     us->flags |= US_BLOCKED;
733     return 0;
734 }
735
736 static void
737 new_ppa(q, mp)
738     queue_t *q;
739     mblk_t *mp;
740 {
741     struct upperstr *us, **usp;
742     int ppa_id;
743
744     /*
745      * Allocate a new PPA id and link this stream into
746      * the list of PPAs.
747      */
748     usp = &ppas;
749     ppa_id = 0;
750     while ((us = *usp) != 0 && ppa_id == us->ppa_id) {
751         ++ppa_id;
752         usp = &us->nextppa;
753     }
754     us = (struct upperstr *) q->q_ptr;
755     us->ppa_id = ppa_id;
756     us->ppa = us;
757     us->next = 0;
758     us->nextppa = *usp;
759     *usp = us;
760     us->flags |= US_CONTROL;
761
762     *(int *)mp->b_cont->b_rptr = ppa_id;
763     mp->b_datap->db_type = M_IOCACK;
764     qreply(q, mp);
765 }
766
767 static int
768 pppuwsrv(q)
769     queue_t *q;
770 {
771     struct upperstr *us;
772     struct lowerstr *ls;
773     queue_t *lwq;
774     mblk_t *mp;
775
776     us = (struct upperstr *) q->q_ptr;
777     while ((mp = getq(q)) != 0) {
778         if (!send_data(mp, us)) {
779             putbq(q, mp);
780             break;
781         }
782     }
783     return 0;
784 }
785
786 static int
787 ppplwsrv(q)
788     queue_t *q;
789 {
790     struct upperstr *us;
791
792     /*
793      * Flow control has back-enabled this stream:
794      * enable the write service procedures of all upper
795      * streams feeding this lower stream.
796      */
797     for (us = (struct upperstr *) q->q_ptr; us != NULL; us = us->next)
798         if (us->flags & US_BLOCKED)
799             qenable(WR(us->q));
800     return 0;
801 }
802
803 static int
804 pppursrv(q)
805     queue_t *q;
806 {
807     struct upperstr *us, *as;
808     mblk_t *mp, *hdr;
809     dl_unitdata_ind_t *ud;
810     int proto;
811
812     /*
813      * If this is a control stream and we don't have a lower queue attached,
814      * run the write service routines of other streams attached to this PPA.
815      */
816     us = (struct upperstr *) q->q_ptr;
817     if (us->flags & US_CONTROL) {
818         /*
819          * A control stream.
820          * If there is no lower queue attached, run the write service
821          * routines of other upper streams attached to this PPA.
822          */
823         if (us->lowerq == 0) {
824             as = us;
825             do {
826                 if (as->flags & US_BLOCKED)
827                     qenable(WR(as->q));
828                 as = as->next;
829             } while (as != 0);
830         }
831     } else {
832         /*
833          * A network protocol stream.  Put a DLPI header on each
834          * packet and send it on.
835          */
836         while ((mp = getq(q)) != 0) {
837             if (!canputnext(q)) {
838                 putbq(q, mp);
839                 break;
840             }
841             proto = PPP_PROTOCOL(mp->b_rptr);
842             mp->b_rptr += PPP_HDRLEN;
843             hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(ulong),
844                          BPRI_MED);
845             if (hdr == 0) {
846                 /* XXX should put it back and use bufcall */
847                 freemsg(mp);
848                 continue;
849             }
850             ud = (dl_unitdata_ind_t *) hdr->b_wptr;
851             hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(ulong);
852             hdr->b_cont = mp;
853             ud->dl_primitive = DL_UNITDATA_IND;
854             ud->dl_dest_addr_length = sizeof(ulong);
855             ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
856             ud->dl_src_addr_length = sizeof(ulong);
857             ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(ulong);
858             ud->dl_group_address = 0;
859             ((ulong *)(ud + 1))[0] = proto;     /* dest SAP */
860             ((ulong *)(ud + 1))[1] = proto;     /* src SAP */
861             putnext(q, mp);
862         }
863     }
864     return 0;
865 }
866
867 static struct upperstr *
868 find_dest(ppa, proto)
869     struct upperstr *ppa;
870     int proto;
871 {
872     struct upperstr *us;
873
874     for (us = ppa->next; us != 0; us = us->next)
875         if (proto == us->sap)
876             return us;
877     return 0;
878 }
879
880 static int
881 ppplrput(q, mp)
882     queue_t *q;
883     mblk_t *mp;
884 {
885     struct upperstr *ppa, *us;
886     queue_t *uq;
887     int proto;
888
889     ppa = (struct upperstr *) q->q_ptr;
890     switch (mp->b_datap->db_type) {
891     case M_FLUSH:
892         if (*mp->b_rptr & FLUSHW) {
893             *mp->b_rptr &= ~FLUSHR;
894             qreply(q, mp);
895         } else
896             freemsg(mp);
897         break;
898
899     default:
900         if (mp->b_datap->db_type == M_DATA
901             && (proto = PPP_PROTOCOL(mp->b_rptr)) < 0x8000
902             && (us = find_dest(ppa, proto)) != 0) {
903             /*
904              * A data packet for some network protocol.
905              * Queue it on the upper stream for that protocol.
906              */
907             if (canput(us->q))
908                 putq(us->q, mp);
909             else
910                 putq(q, mp);
911             break;
912         } else {
913             /*
914              * A control frame, a frame for an unknown protocol,
915              * or some other message type.
916              * Send it up to pppd via the control stream.
917              */
918             if (mp->b_datap->db_type >= QPCTL || canputnext(ppa->q))
919                 putnext(ppa->q, mp);
920             else
921                 putq(q, mp);
922         }
923         break;
924     }
925
926     return 0;
927 }
928
929 static int
930 ppplrsrv(q)
931     queue_t *q;
932 {
933     mblk_t *mp;
934     struct upperstr *ppa, *us;
935     int proto;
936
937     /*
938      * Packets only get queued here for flow control reasons.
939      */
940     ppa = (struct upperstr *) q->q_ptr;
941     while ((mp = getq(q)) != 0) {
942         if (mp->b_datap->db_type == M_DATA
943             && (proto = PPP_PROTOCOL(mp->b_rptr)) < 0x8000
944             && (us = find_dest(ppa, proto)) != 0) {
945             if (canput(us->q))
946                 putq(us->q, mp);
947             else {
948                 putbq(q, mp);
949                 break;
950             }
951         } else {
952             if (canputnext(ppa->q))
953                 putnext(ppa->q, mp);
954             else {
955                 putbq(q, mp);
956                 break;
957             }
958         }
959     }
960     return 0;
961 }