xref: /plan9/sys/src/cmd/ip/ppp/thw.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <auth.h>
5 #include "ppp.h"
6 #include "thwack.h"
7 
8 typedef struct Cstate Cstate;
9 struct Cstate
10 {
11 	ulong		seq;
12 	Thwack		th;
13 	ulong		stats[ThwStats];
14 };
15 
16 typedef struct Uncstate Uncstate;
17 struct Uncstate
18 {
19 	QLock		ackl;			/* lock for acks sent back to compressor */
20 	int		doack;			/* send an ack? */
21 	int		badpacks;		/* bad packets seen in a row */
22 	ulong		ackseq;			/* packets to ack */
23 	int		ackmask;
24 
25 	int		active;			/* 0 => waiting for resetack */
26 	int		resetid;		/* id of most recent reset */
27 	Unthwack	ut;
28 };
29 
30 enum
31 {
32 	ThwAcked	= 1UL << 23,
33 	ThwCompMask	= 3UL << 21,
34 	ThwCompressed	= 0UL << 21,
35 	ThwUncomp	= 1UL << 21,
36 	ThwUncompAdd	= 2UL << 21,		/* uncompressed, but add to decompression buffer */
37 	ThwSeqMask	= 0x0fffff,
38 	ThwSmallPack	= 96,
39 };
40 
41 static	void		*compinit(PPP*);
42 static	Block*		comp(PPP*, ushort, Block*, int*);
43 static	Block		*compresetreq(void*, Block*);
44 static	void		compcompack(void*, Block*);
45 static	void		compfini(void*);
46 
47 static	void		*uncinit(PPP*);
48 static	Block*		uncomp(PPP*, Block*, int *protop, Block**);
49 static	void		uncfini(void*);
50 static	void		uncresetack(void*, Block*);
51 
52 Comptype cthwack = {
53 	compinit,
54 	comp,
55 	compresetreq,
56 	compfini
57 };
58 
59 Uncomptype uncthwack = {
60 	uncinit,
61 	uncomp,
62 	uncresetack,
63 	uncfini
64 };
65 
66 static void *
compinit(PPP *)67 compinit(PPP *)
68 {
69 	Cstate *cs;
70 
71 	cs = mallocz(sizeof(Cstate), 1);
72 	thwackinit(&cs->th);
73 	return cs;
74 }
75 
76 static void
compfini(void * as)77 compfini(void *as)
78 {
79 	Cstate *cs;
80 
81 	cs = as;
82 	thwackcleanup(&cs->th);
83 	free(cs);
84 }
85 
86 
87 static Block *
compresetreq(void * as,Block * b)88 compresetreq(void *as, Block *b)
89 {
90 	Cstate *cs;
91 	Lcpmsg *m;
92 	int id;
93 
94 	cs = as;
95 	m = (Lcpmsg*)b->rptr;
96 	id = m->id;
97 
98 	thwackinit(&cs->th);
99 
100 	freeb(b);
101 
102 	netlog("thwack resetreq id=%d \n", id);
103 
104 	b = alloclcp(Lresetack, id, 4, &m);
105 	hnputs(m->len, 4);
106 
107 	return b;
108 }
109 
110 static Block*
comp(PPP * ppp,ushort proto,Block * b,int * protop)111 comp(PPP *ppp, ushort proto, Block *b, int *protop)
112 {
113 	Uncstate *uncs;
114 	Cstate *cs;
115 	Block *bb;
116 	ulong seq, acked;
117 	int n, nn, mustadd;
118 
119 	cs = ppp->cstate;
120 	*protop = 0;
121 
122 	/* put ack and protocol into b */
123 	n = BLEN(b);
124 	if(b->rptr - (2+4) < b->base)
125 		sysfatal("thwack: not enough header in block");
126 	acked = 0;
127 	if(ppp->unctype == &uncthwack){
128 		uncs = ppp->uncstate;
129 		qlock(&uncs->ackl);
130 		if(uncs->doack){
131 			uncs->doack = 0;
132 			b->rptr -= 4;
133 			b->rptr[0] = uncs->ackseq >> 16;
134 			b->rptr[1] = uncs->ackseq >> 8;
135 			b->rptr[2] = uncs->ackseq;
136 			b->rptr[3] = uncs->ackmask;
137 			acked = ThwAcked;
138 		}
139 		qunlock(&uncs->ackl);
140 	}
141 	if(proto > 0xff){
142 		b->rptr -= 2;
143 		b->rptr[0] = proto >> 8;
144 		b->rptr[1] = proto;
145 	}else{
146 		b->rptr--;
147 		b->rptr[0] = proto;
148 	}
149 
150 	bb = allocb(BLEN(b) + 3);
151 
152 	seq = cs->seq;
153 	if(n <= 3){
154 		mustadd = 0;
155 		nn = -1;
156 	}else{
157 		mustadd = n < ThwSmallPack;
158 		nn = thwack(&cs->th, mustadd, bb->wptr + 3, n - 3, b, seq, cs->stats);
159 	}
160 	if(nn < 0 && !mustadd){
161 		if(!acked || BLEN(b) + 1 > ppp->mtu){
162 			freeb(bb);
163 			if(acked)
164 				b->rptr += 4;
165 			if(proto > 0xff)
166 				b->rptr += 2;
167 			else
168 				b->rptr++;
169 			*protop = proto;
170 			return b;
171 		}
172 		bb->wptr[0] = (ThwUncomp | ThwAcked) >> 16;
173 
174 		memmove(bb->wptr + 1, b->rptr, BLEN(b));
175 
176 		bb->wptr += BLEN(b) + 1;
177 		freeb(b);
178 	}else{
179 		cs->seq = (seq + 1) & ThwSeqMask;
180 		if(nn < 0){
181 			nn = BLEN(b);
182 			memmove(bb->wptr + 3, b->rptr, nn);
183 			seq |= ThwUncompAdd;
184 		}else
185 			seq |= ThwCompressed;
186 		seq |= acked;
187 		bb->wptr[0] = seq>>16;
188 		bb->wptr[1] = seq>>8;
189 		bb->wptr[2] = seq;
190 
191 		bb->wptr += nn + 3;
192 	}
193 
194 	*protop = Pcdata;
195 	return bb;
196 }
197 
198 static	void *
uncinit(PPP *)199 uncinit(PPP *)
200 {
201 	Uncstate *s;
202 
203 	s = mallocz(sizeof(Uncstate), 1);
204 
205 	s->active = 1;
206 
207 	unthwackinit(&s->ut);
208 
209 	return s;
210 }
211 
212 static	void
uncfini(void * as)213 uncfini(void *as)
214 {
215 	free(as);
216 }
217 
218 static	void
uncresetack(void * as,Block * b)219 uncresetack(void *as, Block *b)
220 {
221 	Uncstate *s;
222 	Lcpmsg *m;
223 
224 	s = as;
225 	m = (Lcpmsg*)b->rptr;
226 
227 	/*
228 	 * rfc 1962 says we must reset every message
229 	 * we don't since we may have acked some messages
230 	 * which the compressor will use in the future.
231 	 */
232 	netlog("unthwack resetack id=%d resetid=%d active=%d\n", m->id, s->resetid, s->active);
233 	if(m->id == (uchar)s->resetid && !s->active){
234 		s->active = 1;
235 		unthwackinit(&s->ut);
236 	}
237 }
238 
239 static	Block*
uncomp(PPP * ppp,Block * bb,int * protop,Block ** reply)240 uncomp(PPP *ppp, Block *bb, int *protop, Block **reply)
241 {
242 	Lcpmsg *m;
243 	Cstate *cs;
244 	Uncstate *uncs;
245 	Block *b, *r;
246 	ulong seq, mseq;
247 	ushort proto;
248 	uchar mask;
249 	int n;
250 
251 	*reply = nil;
252 	*protop = 0;
253 	uncs = ppp->uncstate;
254 
255 	if(BLEN(bb) < 4){
256 		syslog(0, "ppp", ": thwack: short packet\n");
257 		freeb(bb);
258 		return nil;
259 	}
260 
261 	if(!uncs->active){
262 		netlog("unthwack: inactive, killing packet\n");
263 		freeb(bb);
264 		r = alloclcp(Lresetreq, uncs->resetid, 4, &m);
265 		hnputs(m->len, 4);
266 		*reply = r;
267 		return nil;
268 	}
269 
270 	seq = bb->rptr[0] << 16;
271 	if((seq & ThwCompMask) == ThwUncomp){
272 		bb->rptr++;
273 		b = bb;
274 	}else{
275 		seq |= (bb->rptr[1]<<8) | bb->rptr[2];
276 		bb->rptr += 3;
277 		if((seq & ThwCompMask) == ThwCompressed){
278 			b = allocb(ThwMaxBlock);
279 			n = unthwack(&uncs->ut, b->wptr, ThwMaxBlock, bb->rptr, BLEN(bb), seq & ThwSeqMask);
280 			freeb(bb);
281 			if(n < 2){
282 				syslog(0, "ppp", ": unthwack: short or corrupted packet %d seq=%ld\n", n, seq);
283 				netlog("unthwack: short or corrupted packet n=%d seq=%ld: %s\n", n, seq, uncs->ut.err);
284 				freeb(b);
285 
286 				r = alloclcp(Lresetreq, ++uncs->resetid, 4, &m);
287 				hnputs(m->len, 4);
288 				*reply = r;
289 				uncs->active = 0;
290 				return nil;
291 			}
292 			b->wptr += n;
293 		}else{
294 			unthwackadd(&uncs->ut, bb->rptr, BLEN(bb), seq & ThwSeqMask);
295 			b = bb;
296 		}
297 
298 		/*
299 		 * update ack state
300 		 */
301 		mseq = unthwackstate(&uncs->ut, &mask);
302 		qlock(&uncs->ackl);
303 		uncs->ackseq = mseq;
304 		uncs->ackmask = mask;
305 		uncs->doack = 1;
306 		qunlock(&uncs->ackl);
307 	}
308 
309 	/*
310 	 * grab the compressed protocol field
311 	 */
312 	proto = *b->rptr++;
313 	if((proto & 1) == 0)
314 		proto = (proto << 8) | *b->rptr++;
315 	*protop = proto;
316 
317 	/*
318 	 * decode the ack, and forward to compressor
319 	 */
320 	if(seq & ThwAcked){
321 		if(ppp->ctype == &cthwack){
322 			cs = ppp->cstate;
323 			mseq = (b->rptr[0]<<16) | (b->rptr[1]<<8) | b->rptr[2];
324 			mask = b->rptr[3];
325 			thwackack(&cs->th, mseq, mask);
326 		}
327 		b->rptr += 4;
328 	}
329 	return b;
330 }
331