1/*
2 * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
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 HAS 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: ppp_comp.c,v 1.1.1.1 2008/10/15 03:30:13 james26_jang Exp $
28 */
29
30/*
31 * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
32 */
33
34#include <sys/types.h>
35#include <sys/param.h>
36#include <sys/errno.h>
37#include <sys/stream.h>
38
39#ifdef SVR4
40#include <sys/conf.h>
41#include <sys/cmn_err.h>
42#include <sys/ddi.h>
43#else
44#include <sys/user.h>
45#ifdef __osf__
46#include <sys/cmn_err.h>
47#endif
48#endif /* SVR4 */
49
50#include <net/ppp_defs.h>
51#include <net/pppio.h>
52#include "ppp_mod.h"
53
54#ifdef __osf__
55#include <sys/mbuf.h>
56#include <sys/protosw.h>
57#endif
58
59#include <netinet/in.h>
60#include <netinet/in_systm.h>
61#include <netinet/ip.h>
62#include <net/vjcompress.h>
63
64#define PACKETPTR	mblk_t *
65#include <net/ppp-comp.h>
66
67MOD_OPEN_DECL(ppp_comp_open);
68MOD_CLOSE_DECL(ppp_comp_close);
69static int ppp_comp_rput __P((queue_t *, mblk_t *));
70static int ppp_comp_rsrv __P((queue_t *));
71static int ppp_comp_wput __P((queue_t *, mblk_t *));
72static int ppp_comp_wsrv __P((queue_t *));
73static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
74static int msg_byte __P((mblk_t *, unsigned int));
75
76/* Extract byte i of message mp. */
77#define MSG_BYTE(mp, i)	((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
78			 msg_byte((mp), (i)))
79
80/* Is this LCP packet one we have to transmit using LCP defaults? */
81#define LCP_USE_DFLT(mp)	(1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
82
83#define PPP_COMP_ID 0xbadf
84static struct module_info minfo = {
85#ifdef PRIOQ
86    PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384,
87#else
88    PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096,
89#endif
90};
91
92static struct qinit r_init = {
93    ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close,
94    NULL, &minfo, NULL
95};
96
97static struct qinit w_init = {
98    ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL
99};
100
101#if defined(SVR4) && !defined(SOL2)
102int pcmpdevflag = 0;
103#define ppp_compinfo pcmpinfo
104#endif
105struct streamtab ppp_compinfo = {
106    &r_init, &w_init, NULL, NULL
107};
108
109int ppp_comp_count;		/* number of module instances in use */
110
111#ifdef __osf__
112
113static void ppp_comp_alloc __P((comp_state_t *));
114typedef struct memreq {
115    unsigned char comp_opts[20];
116    int cmd;
117    int thread_status;
118    char *returned_mem;
119} memreq_t;
120
121#endif
122
123typedef struct comp_state {
124    int		flags;
125    int		mru;
126    int		mtu;
127    int		unit;
128    struct compressor *xcomp;
129    void	*xstate;
130    struct compressor *rcomp;
131    void	*rstate;
132    struct vjcompress vj_comp;
133    int		vj_last_ierrors;
134    struct pppstat stats;
135#ifdef __osf__
136    memreq_t	memreq;
137    thread_t	thread;
138#endif
139} comp_state_t;
140
141
142#ifdef __osf__
143extern task_t first_task;
144#endif
145
146/* Bits in flags are as defined in pppio.h. */
147#define CCP_ERR		(CCP_ERROR | CCP_FATALERROR)
148#define LAST_MOD	0x1000000	/* no ppp modules below us */
149#define DBGLOG		0x2000000	/* log debugging stuff */
150
151#define MAX_IPHDR	128	/* max TCP/IP header size */
152#define MAX_VJHDR	20	/* max VJ compressed header size (?) */
153
154#undef MIN		/* just in case */
155#define MIN(a, b)	((a) < (b)? (a): (b))
156
157/*
158 * List of compressors we know about.
159 */
160
161#if DO_BSD_COMPRESS
162extern struct compressor ppp_bsd_compress;
163#endif
164#if DO_DEFLATE
165extern struct compressor ppp_deflate, ppp_deflate_draft;
166#endif
167
168struct compressor *ppp_compressors[] = {
169#if DO_BSD_COMPRESS
170    &ppp_bsd_compress,
171#endif
172#if DO_DEFLATE
173    &ppp_deflate,
174    &ppp_deflate_draft,
175#endif
176    NULL
177};
178
179/*
180 * STREAMS module entry points.
181 */
182MOD_OPEN(ppp_comp_open)
183{
184    comp_state_t *cp;
185#ifdef __osf__
186    thread_t thread;
187#endif
188
189    if (q->q_ptr == NULL) {
190	cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t));
191	if (cp == NULL)
192	    OPEN_ERROR(ENOSR);
193	bzero((caddr_t)cp, sizeof(comp_state_t));
194	WR(q)->q_ptr = q->q_ptr = (caddr_t) cp;
195	cp->mru = PPP_MRU;
196	cp->mtu = PPP_MTU;
197	cp->xstate = NULL;
198	cp->rstate = NULL;
199	vj_compress_init(&cp->vj_comp, -1);
200#ifdef __osf__
201	if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp)))
202		OPEN_ERROR(ENOSR);
203	cp->thread = thread;
204#endif
205	++ppp_comp_count;
206	qprocson(q);
207    }
208    return 0;
209}
210
211MOD_CLOSE(ppp_comp_close)
212{
213    comp_state_t *cp;
214
215    qprocsoff(q);
216    cp = (comp_state_t *) q->q_ptr;
217    if (cp != NULL) {
218	if (cp->xstate != NULL)
219	    (*cp->xcomp->comp_free)(cp->xstate);
220	if (cp->rstate != NULL)
221	    (*cp->rcomp->decomp_free)(cp->rstate);
222#ifdef __osf__
223	if (!cp->thread)
224	    printf("ppp_comp_close: NULL thread!\n");
225	else
226	    thread_terminate(cp->thread);
227#endif
228	FREE(cp, sizeof(comp_state_t));
229	q->q_ptr = NULL;
230	OTHERQ(q)->q_ptr = NULL;
231	--ppp_comp_count;
232    }
233    return 0;
234}
235
236#ifdef __osf__
237
238/* thread for calling back to a compressor's memory allocator
239 * Needed for Digital UNIX since it's VM can't handle requests
240 * for large amounts of memory without blocking.  The thread
241 * provides a context in which we can call a memory allocator
242 * that may block.
243 */
244static void
245ppp_comp_alloc(comp_state_t *cp)
246{
247    int len, cmd;
248    unsigned char *compressor_options;
249    thread_t thread;
250    void *(*comp_allocator)();
251
252
253#if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2)
254
255    /* In 2.x and earlier the argument gets passed
256     * in the thread structure itself.  Yuck.
257     */
258    thread = current_thread();
259    cp = thread->reply_port;
260    thread->reply_port = PORT_NULL;
261
262#endif
263
264    for (;;) {
265	assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE);
266	thread_block();
267
268	if (thread_should_halt(current_thread()))
269	    thread_halt_self();
270	cmd = cp->memreq.cmd;
271	compressor_options = &cp->memreq.comp_opts[0];
272	len = compressor_options[1];
273	if (cmd == PPPIO_XCOMP) {
274	    cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len);
275	    if (!cp->memreq.returned_mem) {
276		cp->memreq.thread_status = ENOSR;
277	    } else {
278		cp->memreq.thread_status = 0;
279	    }
280	} else {
281	    cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len);
282	    if (!cp->memreq.returned_mem) {
283	        cp->memreq.thread_status = ENOSR;
284	    } else {
285		cp->memreq.thread_status = 0;
286	    }
287	}
288    }
289}
290
291#endif /* __osf__ */
292
293/* here's the deal with memory allocation under Digital UNIX.
294 * Some other may also benefit from this...
295 * We can't ask for huge chunks of memory in a context where
296 * the caller can't be put to sleep (like, here.)  The alloc
297 * is likely to fail.  Instead we do this: the first time we
298 * get called, kick off a thread to do the allocation.  Return
299 * immediately to the caller with EAGAIN, as an indication that
300 * they should send down the ioctl again.  By the time the
301 * second call comes in it's likely that the memory allocation
302 * thread will have returned with the requested memory.  We will
303 * continue to return EAGAIN however until the thread has completed.
304 * When it has, we return zero (and the memory) if the allocator
305 * was successful and ENOSR otherwise.
306 *
307 * Callers of the RCOMP and XCOMP ioctls are encouraged (but not
308 * required) to loop for some number of iterations with a small
309 * delay in the loop body (for instance a 1/10-th second "sleep"
310 * via select.)
311 */
312static int
313ppp_comp_wput(q, mp)
314    queue_t *q;
315    mblk_t *mp;
316{
317    struct iocblk *iop;
318    comp_state_t *cp;
319    int error, len, n;
320    int flags, mask;
321    mblk_t *np;
322    struct compressor **comp;
323    struct ppp_stats *psp;
324    struct ppp_comp_stats *csp;
325    unsigned char *opt_data;
326    int nxslots, nrslots;
327
328    cp = (comp_state_t *) q->q_ptr;
329    if (cp == 0) {
330	DPRINT("cp == 0 in ppp_comp_wput\n");
331	freemsg(mp);
332	return 0;
333    }
334
335    switch (mp->b_datap->db_type) {
336
337    case M_DATA:
338	putq(q, mp);
339	break;
340
341    case M_IOCTL:
342	iop = (struct iocblk *) mp->b_rptr;
343	error = EINVAL;
344	switch (iop->ioc_cmd) {
345
346	case PPPIO_CFLAGS:
347	    /* set/get CCP state */
348	    if (iop->ioc_count != 2 * sizeof(int))
349		break;
350	    if (mp->b_cont == 0) {
351		DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit);
352		break;
353	    }
354	    flags = ((int *) mp->b_cont->b_rptr)[0];
355	    mask = ((int *) mp->b_cont->b_rptr)[1];
356	    cp->flags = (cp->flags & ~mask) | (flags & mask);
357	    if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) {
358		if (cp->xstate != NULL) {
359		    (*cp->xcomp->comp_free)(cp->xstate);
360		    cp->xstate = NULL;
361		}
362		if (cp->rstate != NULL) {
363		    (*cp->rcomp->decomp_free)(cp->rstate);
364		    cp->rstate = NULL;
365		}
366		cp->flags &= ~CCP_ISUP;
367	    }
368	    error = 0;
369	    iop->ioc_count = sizeof(int);
370	    ((int *) mp->b_cont->b_rptr)[0] = cp->flags;
371	    mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
372	    break;
373
374	case PPPIO_VJINIT:
375	    /*
376	     * Initialize VJ compressor/decompressor
377	     */
378	    if (iop->ioc_count != 2)
379		break;
380	    if (mp->b_cont == 0) {
381		DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit);
382		break;
383	    }
384	    nxslots = mp->b_cont->b_rptr[0] + 1;
385	    nrslots = mp->b_cont->b_rptr[1] + 1;
386	    if (nxslots > MAX_STATES || nrslots > MAX_STATES)
387		break;
388	    vj_compress_init(&cp->vj_comp, nxslots);
389	    cp->vj_last_ierrors = cp->stats.ppp_ierrors;
390	    error = 0;
391	    iop->ioc_count = 0;
392	    break;
393
394	case PPPIO_XCOMP:
395	case PPPIO_RCOMP:
396	    if (iop->ioc_count <= 0)
397		break;
398	    if (mp->b_cont == 0) {
399		DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit);
400		break;
401	    }
402	    opt_data = mp->b_cont->b_rptr;
403	    len = mp->b_cont->b_wptr - opt_data;
404	    if (len > iop->ioc_count)
405		len = iop->ioc_count;
406	    if (opt_data[1] < 2 || opt_data[1] > len)
407		break;
408	    for (comp = ppp_compressors; *comp != NULL; ++comp)
409		if ((*comp)->compress_proto == opt_data[0]) {
410		    /* here's the handler! */
411		    error = 0;
412#ifndef __osf__
413		    if (iop->ioc_cmd == PPPIO_XCOMP) {
414			/* A previous call may have fetched memory for a compressor
415			 * that's now being retired or reset.  Free it using it's
416			 * mechanism for freeing stuff.
417			 */
418			if (cp->xstate != NULL) {
419			    (*cp->xcomp->comp_free)(cp->xstate);
420			    cp->xstate = NULL;
421			}
422			cp->xcomp = *comp;
423			cp->xstate = (*comp)->comp_alloc(opt_data, len);
424			if (cp->xstate == NULL)
425			    error = ENOSR;
426		    } else {
427			if (cp->rstate != NULL) {
428			    (*cp->rcomp->decomp_free)(cp->rstate);
429			    cp->rstate = NULL;
430			}
431			cp->rcomp = *comp;
432			cp->rstate = (*comp)->decomp_alloc(opt_data, len);
433			if (cp->rstate == NULL)
434			    error = ENOSR;
435		    }
436#else
437		    if ((error = cp->memreq.thread_status) != EAGAIN)
438		    if (iop->ioc_cmd == PPPIO_XCOMP) {
439			if (cp->xstate) {
440			    (*cp->xcomp->comp_free)(cp->xstate);
441			    cp->xstate = 0;
442			}
443			/* sanity check for compressor options
444			 */
445			if (sizeof (cp->memreq.comp_opts) < len) {
446			    printf("can't handle options for compressor %d (%d)\n", opt_data[0],
447				opt_data[1]);
448			    cp->memreq.thread_status = ENOSR;
449			    cp->memreq.returned_mem = 0;
450			}
451			/* fill in request for the thread and kick it off
452			 */
453			if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
454			    bcopy(opt_data, cp->memreq.comp_opts, len);
455			    cp->memreq.cmd = PPPIO_XCOMP;
456			    cp->xcomp = *comp;
457			    error = cp->memreq.thread_status = EAGAIN;
458			    thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
459			} else {
460			    cp->xstate = cp->memreq.returned_mem;
461			    cp->memreq.returned_mem = 0;
462			    cp->memreq.thread_status = 0;
463			}
464		    } else {
465			if (cp->rstate) {
466			    (*cp->rcomp->decomp_free)(cp->rstate);
467			    cp->rstate = NULL;
468			}
469			if (sizeof (cp->memreq.comp_opts) < len) {
470			    printf("can't handle options for compressor %d (%d)\n", opt_data[0],
471				opt_data[1]);
472			    cp->memreq.thread_status = ENOSR;
473			    cp->memreq.returned_mem = 0;
474			}
475			if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
476			    bcopy(opt_data, cp->memreq.comp_opts, len);
477			    cp->memreq.cmd = PPPIO_RCOMP;
478			    cp->rcomp = *comp;
479			    error = cp->memreq.thread_status = EAGAIN;
480			    thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
481			} else {
482			    cp->rstate = cp->memreq.returned_mem;
483			    cp->memreq.returned_mem = 0;
484			    cp->memreq.thread_status = 0;
485			}
486		    }
487#endif
488		    break;
489		}
490	    iop->ioc_count = 0;
491	    break;
492
493	case PPPIO_GETSTAT:
494	    if ((cp->flags & LAST_MOD) == 0) {
495		error = -1;	/* let the ppp_ahdl module handle it */
496		break;
497	    }
498	    np = allocb(sizeof(struct ppp_stats), BPRI_HI);
499	    if (np == 0) {
500		error = ENOSR;
501		break;
502	    }
503	    if (mp->b_cont != 0)
504		freemsg(mp->b_cont);
505	    mp->b_cont = np;
506	    psp = (struct ppp_stats *) np->b_wptr;
507	    np->b_wptr += sizeof(struct ppp_stats);
508	    iop->ioc_count = sizeof(struct ppp_stats);
509	    psp->p = cp->stats;
510	    psp->vj = cp->vj_comp.stats;
511	    error = 0;
512	    break;
513
514	case PPPIO_GETCSTAT:
515	    np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI);
516	    if (np == 0) {
517		error = ENOSR;
518		break;
519	    }
520	    if (mp->b_cont != 0)
521		freemsg(mp->b_cont);
522	    mp->b_cont = np;
523	    csp = (struct ppp_comp_stats *) np->b_wptr;
524	    np->b_wptr += sizeof(struct ppp_comp_stats);
525	    iop->ioc_count = sizeof(struct ppp_comp_stats);
526	    bzero((caddr_t)csp, sizeof(struct ppp_comp_stats));
527	    if (cp->xstate != 0)
528		(*cp->xcomp->comp_stat)(cp->xstate, &csp->c);
529	    if (cp->rstate != 0)
530		(*cp->rcomp->decomp_stat)(cp->rstate, &csp->d);
531	    error = 0;
532	    break;
533
534	case PPPIO_DEBUG:
535	    if (iop->ioc_count != sizeof(int))
536		break;
537	    if (mp->b_cont == 0) {
538		DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit);
539		break;
540	    }
541	    n = *(int *)mp->b_cont->b_rptr;
542	    if (n == PPPDBG_LOG + PPPDBG_COMP) {
543		DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit);
544		cp->flags |= DBGLOG;
545		error = 0;
546		iop->ioc_count = 0;
547	    } else {
548		error = -1;
549	    }
550	    break;
551
552	case PPPIO_LASTMOD:
553	    cp->flags |= LAST_MOD;
554	    error = 0;
555	    break;
556
557	default:
558	    error = -1;
559	    break;
560	}
561
562	if (error < 0)
563	    putnext(q, mp);
564	else if (error == 0) {
565	    mp->b_datap->db_type = M_IOCACK;
566	    qreply(q, mp);
567	} else {
568	    mp->b_datap->db_type = M_IOCNAK;
569	    iop->ioc_error = error;
570	    iop->ioc_count = 0;
571	    qreply(q, mp);
572	}
573	break;
574
575    case M_CTL:
576	switch (*mp->b_rptr) {
577	case PPPCTL_MTU:
578	    cp->mtu = ((unsigned short *)mp->b_rptr)[1];
579	    break;
580	case PPPCTL_MRU:
581	    cp->mru = ((unsigned short *)mp->b_rptr)[1];
582	    break;
583	case PPPCTL_UNIT:
584	    cp->unit = mp->b_rptr[1];
585	    break;
586	}
587	putnext(q, mp);
588	break;
589
590    default:
591	putnext(q, mp);
592    }
593
594    return 0;
595}
596
597static int
598ppp_comp_wsrv(q)
599    queue_t *q;
600{
601    mblk_t *mp, *cmp = NULL;
602    comp_state_t *cp;
603    int len, proto, type, hlen, code;
604    struct ip *ip;
605    unsigned char *vjhdr, *dp;
606
607    cp = (comp_state_t *) q->q_ptr;
608    if (cp == 0) {
609	DPRINT("cp == 0 in ppp_comp_wsrv\n");
610	return 0;
611    }
612
613    while ((mp = getq(q)) != 0) {
614	/* assert(mp->b_datap->db_type == M_DATA) */
615#ifdef PRIOQ
616        if (!bcanputnext(q,mp->b_band))
617#else
618        if (!canputnext(q))
619#endif PRIOQ
620	{
621	    putbq(q, mp);
622	    break;
623	}
624
625	/*
626	 * First check the packet length and work out what the protocol is.
627	 */
628	len = msgdsize(mp);
629	if (len < PPP_HDRLEN) {
630	    DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len);
631	    freemsg(mp);
632	    cp->stats.ppp_oerrors++;
633	    putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
634	    continue;
635	}
636	proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3);
637
638	/*
639	 * Make sure we've got enough data in the first mblk
640	 * and that we are its only user.
641	 */
642	if (proto == PPP_CCP)
643	    hlen = len;
644	else if (proto == PPP_IP)
645	    hlen = PPP_HDRLEN + MAX_IPHDR;
646	else
647	    hlen = PPP_HDRLEN;
648	if (hlen > len)
649	    hlen = len;
650	if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) {
651	    PULLUP(mp, hlen);
652	    if (mp == 0) {
653		DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen);
654		cp->stats.ppp_oerrors++;
655		putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
656		continue;
657	    }
658	}
659
660	/*
661	 * Do VJ compression if requested.
662	 */
663	if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
664	    ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
665	    if (ip->ip_p == IPPROTO_TCP) {
666		type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp,
667				       (cp->flags & COMP_VJCCID), &vjhdr);
668		switch (type) {
669		case TYPE_UNCOMPRESSED_TCP:
670		    mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
671		    break;
672		case TYPE_COMPRESSED_TCP:
673		    dp = vjhdr - PPP_HDRLEN;
674		    dp[1] = mp->b_rptr[1]; /* copy control field */
675		    dp[0] = mp->b_rptr[0]; /* copy address field */
676		    dp[2] = 0;		   /* set protocol field */
677		    dp[3] = proto = PPP_VJC_COMP;
678		    mp->b_rptr = dp;
679		    break;
680		}
681	    }
682	}
683
684	/*
685	 * Do packet compression if enabled.
686	 */
687	if (proto == PPP_CCP)
688	    ppp_comp_ccp(q, mp, 0);
689	else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
690		 && cp->xstate != NULL) {
691	    len = msgdsize(mp);
692	    (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
693			(cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0));
694	    if (cmp != NULL) {
695#ifdef PRIOQ
696		cmp->b_band=mp->b_band;
697#endif PRIOQ
698		freemsg(mp);
699		mp = cmp;
700	    }
701	}
702
703	/*
704	 * Do address/control and protocol compression if enabled.
705	 */
706	if ((cp->flags & COMP_AC)
707	    && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) {
708	    mp->b_rptr += 2;	/* drop the address & ctrl fields */
709	    if (proto < 0x100 && (cp->flags & COMP_PROT))
710		++mp->b_rptr;	/* drop the high protocol byte */
711	} else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
712	    /* shuffle up the address & ctrl fields */
713	    mp->b_rptr[2] = mp->b_rptr[1];
714	    mp->b_rptr[1] = mp->b_rptr[0];
715	    ++mp->b_rptr;
716	}
717
718	cp->stats.ppp_opackets++;
719	cp->stats.ppp_obytes += msgdsize(mp);
720	putnext(q, mp);
721    }
722
723    return 0;
724}
725
726static int
727ppp_comp_rput(q, mp)
728    queue_t *q;
729    mblk_t *mp;
730{
731    comp_state_t *cp;
732    struct iocblk *iop;
733    struct ppp_stats *psp;
734
735    cp = (comp_state_t *) q->q_ptr;
736    if (cp == 0) {
737	DPRINT("cp == 0 in ppp_comp_rput\n");
738	freemsg(mp);
739	return 0;
740    }
741
742    switch (mp->b_datap->db_type) {
743
744    case M_DATA:
745	putq(q, mp);
746	break;
747
748    case M_IOCACK:
749	iop = (struct iocblk *) mp->b_rptr;
750	switch (iop->ioc_cmd) {
751	case PPPIO_GETSTAT:
752	    /*
753	     * Catch this on the way back from the ppp_ahdl module
754	     * so we can fill in the VJ stats.
755	     */
756	    if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats))
757		break;
758	    psp = (struct ppp_stats *) mp->b_cont->b_rptr;
759	    psp->vj = cp->vj_comp.stats;
760	    break;
761	}
762	putnext(q, mp);
763	break;
764
765    case M_CTL:
766	switch (mp->b_rptr[0]) {
767	case PPPCTL_IERROR:
768	    ++cp->stats.ppp_ierrors;
769	    break;
770	case PPPCTL_OERROR:
771	    ++cp->stats.ppp_oerrors;
772	    break;
773	}
774	putnext(q, mp);
775	break;
776
777    default:
778	putnext(q, mp);
779    }
780
781    return 0;
782}
783
784static int
785ppp_comp_rsrv(q)
786    queue_t *q;
787{
788    int proto, rv, i;
789    mblk_t *mp, *dmp = NULL, *np;
790    uchar_t *dp, *iphdr;
791    comp_state_t *cp;
792    int len, hlen, vjlen;
793    u_int iphlen;
794
795    cp = (comp_state_t *) q->q_ptr;
796    if (cp == 0) {
797	DPRINT("cp == 0 in ppp_comp_rsrv\n");
798	return 0;
799    }
800
801    while ((mp = getq(q)) != 0) {
802	/* assert(mp->b_datap->db_type == M_DATA) */
803	if (!canputnext(q)) {
804	    putbq(q, mp);
805	    break;
806	}
807
808	len = msgdsize(mp);
809	cp->stats.ppp_ibytes += len;
810	cp->stats.ppp_ipackets++;
811
812	/*
813	 * First work out the protocol and where the PPP header ends.
814	 */
815	i = 0;
816	proto = MSG_BYTE(mp, 0);
817	if (proto == PPP_ALLSTATIONS) {
818	    i = 2;
819	    proto = MSG_BYTE(mp, 2);
820	}
821	if ((proto & 1) == 0) {
822	    ++i;
823	    proto = (proto << 8) + MSG_BYTE(mp, i);
824	}
825	hlen = i + 1;
826
827	/*
828	 * Now reconstruct a complete, contiguous PPP header at the
829	 * start of the packet.
830	 */
831	if (hlen < ((cp->flags & DECOMP_AC)? 0: 2)
832	           + ((cp->flags & DECOMP_PROT)? 1: 2)) {
833	    /* count these? */
834	    goto bad;
835	}
836	if (mp->b_rptr + hlen > mp->b_wptr) {
837	    adjmsg(mp, hlen);
838	    hlen = 0;
839	}
840	if (hlen != PPP_HDRLEN) {
841	    dp = mp->b_rptr + hlen - PPP_HDRLEN;
842	    if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) {
843		np = allocb(PPP_HDRLEN, BPRI_MED);
844		if (np == 0)
845		    goto bad;
846		np->b_cont = mp;
847		mp->b_rptr += hlen;
848		mp = np;
849		dp = mp->b_wptr;
850		mp->b_wptr += PPP_HDRLEN;
851	    } else
852		mp->b_rptr = dp;
853
854	    dp[0] = PPP_ALLSTATIONS;
855	    dp[1] = PPP_UI;
856	    dp[2] = proto >> 8;
857	    dp[3] = proto;
858	}
859
860	/*
861	 * Now see if we have a compressed packet to decompress,
862	 * or a CCP packet to take notice of.
863	 */
864	proto = PPP_PROTOCOL(mp->b_rptr);
865	if (proto == PPP_CCP) {
866	    len = msgdsize(mp);
867	    if (mp->b_wptr < mp->b_rptr + len) {
868		PULLUP(mp, len);
869		if (mp == 0)
870		    goto bad;
871	    }
872	    ppp_comp_ccp(q, mp, 1);
873	} else if (proto == PPP_COMP) {
874	    if ((cp->flags & CCP_ISUP)
875		&& (cp->flags & CCP_DECOMP_RUN) && cp->rstate
876		&& (cp->flags & CCP_ERR) == 0) {
877		rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
878		switch (rv) {
879		case DECOMP_OK:
880		    freemsg(mp);
881		    mp = dmp;
882		    if (mp == NULL) {
883			/* no error, but no packet returned either. */
884			continue;
885		    }
886		    break;
887		case DECOMP_ERROR:
888		    cp->flags |= CCP_ERROR;
889		    ++cp->stats.ppp_ierrors;
890		    putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
891		    break;
892		case DECOMP_FATALERROR:
893		    cp->flags |= CCP_FATALERROR;
894		    ++cp->stats.ppp_ierrors;
895		    putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
896		    break;
897		}
898	    }
899	} else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
900	    (*cp->rcomp->incomp)(cp->rstate, mp);
901	}
902
903	/*
904	 * Now do VJ decompression.
905	 */
906	proto = PPP_PROTOCOL(mp->b_rptr);
907	if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
908	    len = msgdsize(mp) - PPP_HDRLEN;
909	    if ((cp->flags & DECOMP_VJC) == 0 || len <= 0)
910		goto bad;
911
912	    /*
913	     * Advance past the ppp header.
914	     * Here we assume that the whole PPP header is in the first mblk.
915	     */
916	    np = mp;
917	    dp = np->b_rptr + PPP_HDRLEN;
918	    if (dp >= mp->b_wptr) {
919		np = np->b_cont;
920		dp = np->b_rptr;
921	    }
922
923	    /*
924	     * Make sure we have sufficient contiguous data at this point.
925	     */
926	    hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR;
927	    if (hlen > len)
928		hlen = len;
929	    if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) {
930		PULLUP(mp, hlen + PPP_HDRLEN);
931		if (mp == 0)
932		    goto bad;
933		np = mp;
934		dp = np->b_rptr + PPP_HDRLEN;
935	    }
936
937	    if (proto == PPP_VJC_COMP) {
938		/*
939		 * Decompress VJ-compressed packet.
940		 * First reset compressor if an input error has occurred.
941		 */
942		if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) {
943		    if (cp->flags & DBGLOG)
944			DPRINT1("ppp%d: resetting VJ\n", cp->unit);
945		    vj_uncompress_err(&cp->vj_comp);
946		    cp->vj_last_ierrors = cp->stats.ppp_ierrors;
947		}
948
949		vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len,
950					  &cp->vj_comp, &iphdr, &iphlen);
951		if (vjlen < 0) {
952		    if (cp->flags & DBGLOG)
953			DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n",
954				cp->unit, len);
955		    ++cp->vj_last_ierrors;  /* so we don't reset next time */
956		    goto bad;
957		}
958
959		/* drop ppp and vj headers off */
960		if (mp != np) {
961		    freeb(mp);
962		    mp = np;
963		}
964		mp->b_rptr = dp + vjlen;
965
966		/* allocate a new mblk for the ppp and ip headers */
967		if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0)
968		    goto bad;
969		dp = np->b_rptr;	/* prepend mblk with TCP/IP hdr */
970		dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */
971		dp[1] = PPP_UI;
972		dp[2] = PPP_IP >> 8;
973		dp[3] = PPP_IP;
974		bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen);
975		np->b_wptr = dp + iphlen + PPP_HDRLEN;
976		np->b_cont = mp;
977
978		if (mp->b_wptr - mp->b_rptr > 4) {
979		    bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4);
980		    mp->b_rptr += 4;
981		    np->b_wptr += 4;
982		} else {
983		    bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr,
984			  mp->b_wptr - mp->b_rptr);
985		    np->b_wptr += mp->b_wptr - mp->b_rptr;
986		    np->b_cont = mp->b_cont;
987		    freeb(mp);
988		}
989
990		mp = np;
991
992	    } else {
993		/*
994		 * "Decompress" a VJ-uncompressed packet.
995		 */
996		cp->vj_last_ierrors = cp->stats.ppp_ierrors;
997		if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) {
998		    if (cp->flags & DBGLOG)
999			DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n",
1000				cp->unit, len);
1001		    ++cp->vj_last_ierrors;  /* don't need to reset next time */
1002		    goto bad;
1003		}
1004		mp->b_rptr[3] = PPP_IP;	/* fix up the PPP protocol field */
1005	    }
1006	}
1007
1008	putnext(q, mp);
1009	continue;
1010
1011    bad:
1012	if (mp != 0)
1013	    freemsg(mp);
1014	cp->stats.ppp_ierrors++;
1015	putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
1016    }
1017
1018    return 0;
1019}
1020
1021/*
1022 * Handle a CCP packet being sent or received.
1023 * Here all the data in the packet is in a single mbuf.
1024 */
1025static void
1026ppp_comp_ccp(q, mp, rcvd)
1027    queue_t *q;
1028    mblk_t *mp;
1029    int rcvd;
1030{
1031    int len, clen;
1032    comp_state_t *cp;
1033    unsigned char *dp;
1034
1035    len = msgdsize(mp);
1036    if (len < PPP_HDRLEN + CCP_HDRLEN)
1037	return;
1038
1039    cp = (comp_state_t *) q->q_ptr;
1040    dp = mp->b_rptr + PPP_HDRLEN;
1041    len -= PPP_HDRLEN;
1042    clen = CCP_LENGTH(dp);
1043    if (clen > len)
1044	return;
1045
1046    switch (CCP_CODE(dp)) {
1047    case CCP_CONFREQ:
1048    case CCP_TERMREQ:
1049    case CCP_TERMACK:
1050	cp->flags &= ~CCP_ISUP;
1051	break;
1052
1053    case CCP_CONFACK:
1054	if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
1055	    && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
1056	    && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
1057	    if (!rcvd) {
1058		if (cp->xstate != NULL
1059		    && (*cp->xcomp->comp_init)
1060		        (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
1061			 cp->unit, 0, ((cp->flags & DBGLOG) != 0)))
1062		    cp->flags |= CCP_COMP_RUN;
1063	    } else {
1064		if (cp->rstate != NULL
1065		    && (*cp->rcomp->decomp_init)
1066		        (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
1067			 cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0)))
1068		    cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
1069	    }
1070	}
1071	break;
1072
1073    case CCP_RESETACK:
1074	if (cp->flags & CCP_ISUP) {
1075	    if (!rcvd) {
1076		if (cp->xstate && (cp->flags & CCP_COMP_RUN))
1077		    (*cp->xcomp->comp_reset)(cp->xstate);
1078	    } else {
1079		if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
1080		    (*cp->rcomp->decomp_reset)(cp->rstate);
1081		    cp->flags &= ~CCP_ERROR;
1082		}
1083	    }
1084	}
1085	break;
1086    }
1087}
1088
1089
1090static int
1091msg_byte(mp, i)
1092    mblk_t *mp;
1093    unsigned int i;
1094{
1095    while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
1096	mp = mp->b_cont;
1097    if (mp == 0)
1098	return -1;
1099    return mp->b_rptr[i];
1100}
1101