xref: /386bsd/usr/src/usr.sbin/pppd/lcp.c (revision a2142627)
1 /*
2  * lcp.c - PPP Link Control Protocol.
3  *
4  * Copyright (c) 1989 Carnegie Mellon University.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are permitted
8  * provided that the above copyright notice and this paragraph are
9  * duplicated in all such forms and that any documentation,
10  * advertising materials, and other materials related to such
11  * distribution and use acknowledge that the software was developed
12  * by Carnegie Mellon University.  The name of the
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19 
20 #ifndef lint
21 static char rcsid[] = "$Id: lcp.c,v 1.3 1994/04/18 04:00:25 paulus Exp $";
22 #endif
23 
24 /*
25  * TODO:
26  * Option tracing.
27  * Test restart.
28  */
29 
30 #include <stdio.h>
31 #include <syslog.h>
32 #include <sys/ioctl.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 
37 #include <net/if.h>
38 /*#include <net/if_ppp.h>*/
39 #include <netinet/in.h>
40 
41 #include <string.h>
42 
43 #include "pppd.h"
44 #include "ppp.h"
45 #include "fsm.h"
46 #include "lcp.h"
47 #include "magic.h"
48 #include "chap.h"
49 #include "upap.h"
50 #include "ipcp.h"
51 
52 /* global vars */
53 fsm lcp_fsm[NPPP];			/* LCP fsm structure (global)*/
54 lcp_options lcp_wantoptions[NPPP];	/* Options that we want to request */
55 lcp_options lcp_gotoptions[NPPP];	/* Options that peer ack'd */
56 lcp_options lcp_allowoptions[NPPP];	/* Options we allow peer to request */
57 lcp_options lcp_hisoptions[NPPP];	/* Options that we ack'd */
58 u_long xmit_accm[NPPP][8];		/* extended transmit ACCM */
59 
60 /*
61  * Callbacks for fsm code.  (CI = Configuration Information)
62  */
63 static void lcp_resetci __ARGS((fsm *));	/* Reset our CI */
64 static int  lcp_cilen __ARGS((fsm *));		/* Return length of our CI */
65 static void lcp_addci __ARGS((fsm *, u_char *, int *)); /* Add our CI to pkt */
66 static int  lcp_ackci __ARGS((fsm *, u_char *, int)); /* Peer ack'd our CI */
67 static int  lcp_nakci __ARGS((fsm *, u_char *, int)); /* Peer nak'd our CI */
68 static int  lcp_rejci __ARGS((fsm *, u_char *, int)); /* Peer rej'd our CI */
69 static int  lcp_reqci __ARGS((fsm *, u_char *, int *, int)); /* Rcv peer CI */
70 static void lcp_up __ARGS((fsm *));		/* We're UP */
71 static void lcp_down __ARGS((fsm *));		/* We're DOWN */
72 static void lcp_starting __ARGS((fsm *));	/* We need lower layer up */
73 static void lcp_finished __ARGS((fsm *));	/* We need lower layer down */
74 static int  lcp_extcode __ARGS((fsm *, int, int, u_char *, int));
75 static void lcp_rprotrej __ARGS((fsm *, u_char *, int));
76 
77 static fsm_callbacks lcp_callbacks = {	/* LCP callback routines */
78     lcp_resetci,		/* Reset our Configuration Information */
79     lcp_cilen,			/* Length of our Configuration Information */
80     lcp_addci,			/* Add our Configuration Information */
81     lcp_ackci,			/* ACK our Configuration Information */
82     lcp_nakci,			/* NAK our Configuration Information */
83     lcp_rejci,			/* Reject our Configuration Information */
84     lcp_reqci,			/* Request peer's Configuration Information */
85     lcp_up,			/* Called when fsm reaches OPENED state */
86     lcp_down,			/* Called when fsm leaves OPENED state */
87     lcp_starting,		/* Called when we want the lower layer up */
88     lcp_finished,		/* Called when we want the lower layer down */
89     NULL,			/* Called when Protocol-Reject received */
90     NULL,			/* Retransmission is necessary */
91     lcp_extcode,		/* Called to handle LCP-specific codes */
92     "LCP"			/* String name of protocol */
93 };
94 
95 int lcp_warnloops = DEFWARNLOOPS; /* Warn about a loopback this often */
96 
97 /*
98  * Length of each type of configuration option (in octets)
99  */
100 #define CILEN_VOID	2
101 #define CILEN_SHORT	4	/* CILEN_VOID + sizeof(short) */
102 #define CILEN_CHAP	5	/* CILEN_VOID + sizeof(short) + 1 */
103 #define CILEN_LONG	6	/* CILEN_VOID + sizeof(long) */
104 #define CILEN_LQR	8	/* CILEN_VOID + sizeof(short) + sizeof(long) */
105 
106 #define CODENAME(x)	((x) == CONFACK ? "ACK" : \
107 			 (x) == CONFNAK ? "NAK" : "REJ")
108 
109 
110 /*
111  * lcp_init - Initialize LCP.
112  */
113 void
lcp_init(unit)114 lcp_init(unit)
115     int unit;
116 {
117     fsm *f = &lcp_fsm[unit];
118     lcp_options *wo = &lcp_wantoptions[unit];
119     lcp_options *ao = &lcp_allowoptions[unit];
120 
121     f->unit = unit;
122     f->protocol = LCP;
123     f->callbacks = &lcp_callbacks;
124 
125     fsm_init(f);
126 
127     wo->passive = 0;
128     wo->silent = 0;
129     wo->restart = 0;			/* Set to 1 in kernels or multi-line
130 					   implementations */
131     wo->neg_mru = 1;
132     wo->mru = DEFMRU;
133     wo->neg_asyncmap = 0;
134     wo->asyncmap = 0;
135     wo->neg_chap = 0;			/* Set to 1 on server */
136     wo->neg_upap = 0;			/* Set to 1 on server */
137     wo->chap_mdtype = CHAP_DIGEST_MD5;
138     wo->neg_magicnumber = 1;
139     wo->neg_pcompression = 1;
140     wo->neg_accompression = 1;
141     wo->neg_lqr = 0;			/* no LQR implementation yet */
142 
143     ao->neg_mru = 1;
144     ao->mru = MAXMRU;
145     ao->neg_asyncmap = 1;
146     ao->asyncmap = 0;
147     ao->neg_chap = 1;
148     ao->chap_mdtype = CHAP_DIGEST_MD5;
149     ao->neg_upap = 1;
150     ao->neg_magicnumber = 1;
151     ao->neg_pcompression = 1;
152     ao->neg_accompression = 1;
153     ao->neg_lqr = 0;			/* no LQR implementation yet */
154 
155     memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
156     xmit_accm[unit][3] = 0x60000000;
157 }
158 
159 
160 /*
161  * lcp_open - LCP is allowed to come up.
162  */
163 void
lcp_open(unit)164 lcp_open(unit)
165     int unit;
166 {
167     fsm *f = &lcp_fsm[unit];
168     lcp_options *wo = &lcp_wantoptions[unit];
169 
170     f->flags = 0;
171     if (wo->passive)
172 	f->flags |= OPT_PASSIVE;
173     if (wo->silent)
174 	f->flags |= OPT_SILENT;
175     fsm_open(f);
176 }
177 
178 
179 /*
180  * lcp_close - Take LCP down.
181  */
182 void
lcp_close(unit)183 lcp_close(unit)
184     int unit;
185 {
186     fsm_close(&lcp_fsm[unit]);
187 }
188 
189 
190 /*
191  * lcp_lowerup - The lower layer is up.
192  */
193 void
lcp_lowerup(unit)194 lcp_lowerup(unit)
195     int unit;
196 {
197     sifdown(unit);
198     ppp_set_xaccm(unit, xmit_accm[unit]);
199     ppp_send_config(unit, MTU, 0xffffffff, 0, 0);
200     ppp_recv_config(unit, MTU, 0, 0, 0);
201     peer_mru[unit] = MTU;
202     lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0];
203 
204     fsm_lowerup(&lcp_fsm[unit]);
205 }
206 
207 
208 /*
209  * lcp_lowerdown - The lower layer is down.
210  */
211 void
lcp_lowerdown(unit)212 lcp_lowerdown(unit)
213     int unit;
214 {
215     fsm_lowerdown(&lcp_fsm[unit]);
216 }
217 
218 
219 /*
220  * lcp_input - Input LCP packet.
221  */
222 void
lcp_input(unit,p,len)223 lcp_input(unit, p, len)
224     int unit;
225     u_char *p;
226     int len;
227 {
228     fsm_input(&lcp_fsm[unit], p, len);
229 }
230 
231 
232 /*
233  * lcp_extcode - Handle a LCP-specific code.
234  */
235 static int
lcp_extcode(f,code,id,inp,len)236 lcp_extcode(f, code, id, inp, len)
237     fsm *f;
238     int code, id;
239     u_char *inp;
240     int len;
241 {
242     switch( code ){
243     case PROTREJ:
244 	lcp_rprotrej(f, inp, len);
245 	break;
246 
247     case ECHOREQ:
248 	if( f->state != OPENED )
249 	    break;
250 	LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id));
251 	fsm_sdata(f, ECHOREP, id, inp, len);
252 	break;
253 
254     case ECHOREP:
255     case DISCREQ:
256 	break;
257 
258     default:
259 	return 0;
260     }
261     return 1;
262 }
263 
264 
265 /*
266  * lcp_rprotrej - Receive an Protocol-Reject.
267  *
268  * Figure out which protocol is rejected and inform it.
269  */
270 static void
lcp_rprotrej(f,inp,len)271 lcp_rprotrej(f, inp, len)
272     fsm *f;
273     u_char *inp;
274     int len;
275 {
276     u_short prot;
277 
278     LCPDEBUG((LOG_INFO, "lcp_rprotrej."));
279 
280     if (len < sizeof (u_short)) {
281 	LCPDEBUG((LOG_INFO,
282 		  "lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
283 	return;
284     }
285 
286     GETSHORT(prot, inp);
287 
288     LCPDEBUG((LOG_INFO,
289 	      "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!",
290 	      prot));
291 
292     /*
293      * Protocol-Reject packets received in any state other than the LCP
294      * OPENED state SHOULD be silently discarded.
295      */
296     if( f->state != OPENED ){
297 	LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d",
298 		  f->state));
299 	return;
300     }
301 
302     DEMUXPROTREJ(f->unit, prot);	/* Inform protocol */
303 }
304 
305 
306 /*
307  * lcp_protrej - A Protocol-Reject was received.
308  */
309 /*ARGSUSED*/
310 void
lcp_protrej(unit)311 lcp_protrej(unit)
312     int unit;
313 {
314     /*
315      * Can't reject LCP!
316      */
317     LCPDEBUG((LOG_WARNING,
318 	      "lcp_protrej: Received Protocol-Reject for LCP!"));
319     fsm_protreject(&lcp_fsm[unit]);
320 }
321 
322 
323 /*
324  * lcp_sprotrej - Send a Protocol-Reject for some protocol.
325  */
326 void
lcp_sprotrej(unit,p,len)327 lcp_sprotrej(unit, p, len)
328     int unit;
329     u_char *p;
330     int len;
331 {
332     /*
333      * Send back the protocol and the information field of the
334      * rejected packet.  We only get here if LCP is in the OPENED state.
335      */
336     p += 2;
337     len -= 2;
338 
339     fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
340 	      p, len);
341 }
342 
343 
344 /*
345  * lcp_resetci - Reset our CI.
346  */
347 static void
lcp_resetci(f)348   lcp_resetci(f)
349 fsm *f;
350 {
351     lcp_wantoptions[f->unit].magicnumber = magic();
352     lcp_wantoptions[f->unit].numloops = 0;
353     lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
354     peer_mru[f->unit] = MTU;
355 }
356 
357 
358 /*
359  * lcp_cilen - Return length of our CI.
360  */
361 static int
lcp_cilen(f)362 lcp_cilen(f)
363     fsm *f;
364 {
365     lcp_options *go = &lcp_gotoptions[f->unit];
366 
367 #define LENCIVOID(neg)	(neg ? CILEN_VOID : 0)
368 #define LENCICHAP(neg)	(neg ? CILEN_CHAP : 0)
369 #define LENCISHORT(neg)	(neg ? CILEN_SHORT : 0)
370 #define LENCILONG(neg)	(neg ? CILEN_LONG : 0)
371 #define LENCILQR(neg)	(neg ? CILEN_LQR: 0)
372     /*
373      * NB: we only ask for one of CHAP and UPAP, even if we will
374      * accept either.
375      */
376     return (LENCISHORT(go->neg_mru) +
377 	    LENCILONG(go->neg_asyncmap) +
378 	    LENCICHAP(go->neg_chap) +
379 	    LENCISHORT(!go->neg_chap && go->neg_upap) +
380 	    LENCILQR(go->neg_lqr) +
381 	    LENCILONG(go->neg_magicnumber) +
382 	    LENCIVOID(go->neg_pcompression) +
383 	    LENCIVOID(go->neg_accompression));
384 }
385 
386 
387 /*
388  * lcp_addci - Add our desired CIs to a packet.
389  */
390 static void
lcp_addci(f,ucp,lenp)391 lcp_addci(f, ucp, lenp)
392     fsm *f;
393     u_char *ucp;
394     int *lenp;
395 {
396     lcp_options *go = &lcp_gotoptions[f->unit];
397     u_char *start_ucp = ucp;
398 
399 #define ADDCIVOID(opt, neg) \
400     if (neg) { \
401 	PUTCHAR(opt, ucp); \
402 	PUTCHAR(CILEN_VOID, ucp); \
403     }
404 #define ADDCISHORT(opt, neg, val) \
405     if (neg) { \
406 	PUTCHAR(opt, ucp); \
407 	PUTCHAR(CILEN_SHORT, ucp); \
408 	PUTSHORT(val, ucp); \
409     }
410 #define ADDCICHAP(opt, neg, val, digest) \
411     if (neg) { \
412 	PUTCHAR(opt, ucp); \
413 	PUTCHAR(CILEN_CHAP, ucp); \
414 	PUTSHORT(val, ucp); \
415 	PUTCHAR(digest, ucp); \
416     }
417 #define ADDCILONG(opt, neg, val) \
418     if (neg) { \
419 	PUTCHAR(opt, ucp); \
420 	PUTCHAR(CILEN_LONG, ucp); \
421 	PUTLONG(val, ucp); \
422     }
423 #define ADDCILQR(opt, neg, val) \
424     if (neg) { \
425 	PUTCHAR(opt, ucp); \
426 	PUTCHAR(CILEN_LQR, ucp); \
427 	PUTSHORT(LQR, ucp); \
428 	PUTLONG(val, ucp); \
429     }
430 
431     ADDCISHORT(CI_MRU, go->neg_mru, go->mru);
432     ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap);
433     ADDCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype);
434     ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, UPAP);
435     ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
436     ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
437     ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
438     ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
439 
440     if (ucp - start_ucp != *lenp) {
441 	/* this should never happen, because peer_mtu should be 1500 */
442 	syslog(LOG_ERR, "Bug in lcp_addci: wrong length");
443     }
444 }
445 
446 
447 /*
448  * lcp_ackci - Ack our CIs.
449  * This should not modify any state if the Ack is bad.
450  *
451  * Returns:
452  *	0 - Ack was bad.
453  *	1 - Ack was good.
454  */
455 static int
lcp_ackci(f,p,len)456 lcp_ackci(f, p, len)
457     fsm *f;
458     u_char *p;
459     int len;
460 {
461     lcp_options *go = &lcp_gotoptions[f->unit];
462     u_char cilen, citype, cichar;
463     u_short cishort;
464     u_long cilong;
465 
466     /*
467      * CIs must be in exactly the same order that we sent.
468      * Check packet length and CI length at each step.
469      * If we find any deviations, then this packet is bad.
470      */
471 #define ACKCIVOID(opt, neg) \
472     if (neg) { \
473 	if ((len -= CILEN_VOID) < 0) \
474 	    goto bad; \
475 	GETCHAR(citype, p); \
476 	GETCHAR(cilen, p); \
477 	if (cilen != CILEN_VOID || \
478 	    citype != opt) \
479 	    goto bad; \
480     }
481 #define ACKCISHORT(opt, neg, val) \
482     if (neg) { \
483 	if ((len -= CILEN_SHORT) < 0) \
484 	    goto bad; \
485 	GETCHAR(citype, p); \
486 	GETCHAR(cilen, p); \
487 	if (cilen != CILEN_SHORT || \
488 	    citype != opt) \
489 	    goto bad; \
490 	GETSHORT(cishort, p); \
491 	if (cishort != val) \
492 	    goto bad; \
493     }
494 #define ACKCICHAP(opt, neg, val, digest) \
495     if (neg) { \
496 	if ((len -= CILEN_CHAP) < 0) \
497 	    goto bad; \
498 	GETCHAR(citype, p); \
499 	GETCHAR(cilen, p); \
500 	if (cilen != CILEN_CHAP || \
501 	    citype != opt) \
502 	    goto bad; \
503 	GETSHORT(cishort, p); \
504 	if (cishort != val) \
505 	    goto bad; \
506 	GETCHAR(cichar, p); \
507 	if (cichar != digest) \
508 	  goto bad; \
509     }
510 #define ACKCILONG(opt, neg, val) \
511     if (neg) { \
512 	if ((len -= CILEN_LONG) < 0) \
513 	    goto bad; \
514 	GETCHAR(citype, p); \
515 	GETCHAR(cilen, p); \
516 	if (cilen != CILEN_LONG || \
517 	    citype != opt) \
518 	    goto bad; \
519 	GETLONG(cilong, p); \
520 	if (cilong != val) \
521 	    goto bad; \
522     }
523 #define ACKCILQR(opt, neg, val) \
524     if (neg) { \
525 	if ((len -= CILEN_LQR) < 0) \
526 	    goto bad; \
527 	GETCHAR(citype, p); \
528 	GETCHAR(cilen, p); \
529 	if (cilen != CILEN_LQR || \
530 	    citype != opt) \
531 	    goto bad; \
532 	GETSHORT(cishort, p); \
533 	if (cishort != LQR) \
534 	    goto bad; \
535 	GETLONG(cilong, p); \
536 	if (cilong != val) \
537 	  goto bad; \
538     }
539 
540     ACKCISHORT(CI_MRU, go->neg_mru, go->mru);
541     ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap);
542     ACKCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype);
543     ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, UPAP);
544     ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
545     ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
546     ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
547     ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
548 
549     /*
550      * If there are any remaining CIs, then this packet is bad.
551      */
552     if (len != 0)
553 	goto bad;
554     return (1);
555 bad:
556     LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!"));
557     return (0);
558 }
559 
560 
561 /*
562  * lcp_nakci - Peer has sent a NAK for some of our CIs.
563  * This should not modify any state if the Nak is bad
564  * or if LCP is in the OPENED state.
565  *
566  * Returns:
567  *	0 - Nak was bad.
568  *	1 - Nak was good.
569  */
570 static int
lcp_nakci(f,p,len)571 lcp_nakci(f, p, len)
572     fsm *f;
573     u_char *p;
574     int len;
575 {
576     lcp_options *go = &lcp_gotoptions[f->unit];
577     lcp_options *wo = &lcp_wantoptions[f->unit];
578     u_char cilen, citype, cichar, *next;
579     u_short cishort;
580     u_long cilong;
581     lcp_options no;		/* options we've seen Naks for */
582     lcp_options try;		/* options to request next time */
583     int looped_back = 0;
584 
585     BZERO(&no, sizeof(no));
586     try = *go;
587 
588     /*
589      * Any Nak'd CIs must be in exactly the same order that we sent.
590      * Check packet length and CI length at each step.
591      * If we find any deviations, then this packet is bad.
592      */
593 #define NAKCIVOID(opt, neg, code) \
594     if (go->neg && \
595 	len >= CILEN_VOID && \
596 	p[1] == CILEN_VOID && \
597 	p[0] == opt) { \
598 	len -= CILEN_VOID; \
599 	INCPTR(CILEN_VOID, p); \
600 	no.neg = 1; \
601 	code \
602     }
603 #define NAKCICHAP(opt, neg, code) \
604     if (go->neg && \
605 	len >= CILEN_CHAP && \
606 	p[1] == CILEN_CHAP && \
607 	p[0] == opt) { \
608 	len -= CILEN_CHAP; \
609 	INCPTR(2, p); \
610 	GETSHORT(cishort, p); \
611 	GETCHAR(cichar, p); \
612 	no.neg = 1; \
613 	code \
614     }
615 #define NAKCISHORT(opt, neg, code) \
616     if (go->neg && \
617 	len >= CILEN_SHORT && \
618 	p[1] == CILEN_SHORT && \
619 	p[0] == opt) { \
620 	len -= CILEN_SHORT; \
621 	INCPTR(2, p); \
622 	GETSHORT(cishort, p); \
623 	no.neg = 1; \
624 	code \
625     }
626 #define NAKCILONG(opt, neg, code) \
627     if (go->neg && \
628 	len >= CILEN_LONG && \
629 	p[1] == CILEN_LONG && \
630 	p[0] == opt) { \
631 	len -= CILEN_LONG; \
632 	INCPTR(2, p); \
633 	GETLONG(cilong, p); \
634 	no.neg = 1; \
635 	code \
636     }
637 #define NAKCILQR(opt, neg, code) \
638     if (go->neg && \
639 	len >= CILEN_LQR && \
640 	p[1] == CILEN_LQR && \
641 	p[0] == opt) { \
642 	len -= CILEN_LQR; \
643 	INCPTR(2, p); \
644 	GETSHORT(cishort, p); \
645 	GETLONG(cilong, p); \
646 	no.neg = 1; \
647 	code \
648     }
649 
650     /*
651      * We don't care if they want to send us smaller packets than
652      * we want.  Therefore, accept any MRU less than what we asked for,
653      * but then ignore the new value when setting the MRU in the kernel.
654      * If they send us a bigger MRU than what we asked, accept it, up to
655      * the limit of the default MRU we'd get if we didn't negotiate.
656      */
657     NAKCISHORT(CI_MRU, neg_mru,
658 	       if (cishort <= wo->mru || cishort < DEFMRU)
659 		   try.mru = cishort;
660 	       );
661     /*
662      * Add any characters they want to our (receive-side) asyncmap.
663      */
664     NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
665 	      try.asyncmap = go->asyncmap | cilong;
666 	      );
667     /*
668      * If they can't cope with our CHAP hash algorithm, we'll have
669      * to stop asking for CHAP.  We haven't got any other algorithm.
670      */
671     NAKCICHAP(CI_AUTHTYPE, neg_chap,
672 	      try.neg_chap = 0;
673 	      );
674     /*
675      * Peer shouldn't send Nak for UPAP, protocol compression or
676      * address/control compression requests; they should send
677      * a Reject instead.  If they send a Nak, treat it as a Reject.
678      */
679     if (!go->neg_chap ){
680 	NAKCISHORT(CI_AUTHTYPE, neg_upap,
681 		   try.neg_upap = 0;
682 		   );
683     }
684     /*
685      * If they can't cope with our link quality protocol, we'll have
686      * to stop asking for LQR.  We haven't got any other protocol.
687      * If they Nak the reporting period, take their value XXX ?
688      */
689     NAKCILONG(CI_QUALITY, neg_lqr,
690 	      if (cishort != LQR)
691 		  try.neg_lqr = 0;
692 	      else
693 	          try.lqr_period = cilong;
694 	      );
695     /*
696      * Check for a looped-back line.
697      */
698     NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
699 	      try.magicnumber = magic();
700 	      ++try.numloops;
701 	      looped_back = 1;
702 	      );
703 
704     NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
705 	      try.neg_pcompression = 0;
706 	      );
707     NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
708 	      try.neg_accompression = 0;
709 	      );
710 
711     /*
712      * There may be remaining CIs, if the peer is requesting negotiation
713      * on an option that we didn't include in our request packet.
714      * If we see an option that we requested, or one we've already seen
715      * in this packet, then this packet is bad.
716      * If we wanted to respond by starting to negotiate on the requested
717      * option(s), we could, but we don't, because except for the
718      * authentication type and quality protocol, if we are not negotiating
719      * an option, it is because we were told not to.
720      * For the authentication type, the Nak from the peer means
721      * `let me authenticate myself with you' which is a bit pointless.
722      * For the quality protocol, the Nak means `ask me to send you quality
723      * reports', but if we didn't ask for them, we don't want them.
724      */
725     while (len > CILEN_VOID) {
726 	GETCHAR(citype, p);
727 	GETCHAR(cilen, p);
728 	if( (len -= cilen) < 0 )
729 	    goto bad;
730 	next = p + cilen - 2;
731 
732 	switch (citype) {
733 	case CI_MRU:
734 	    if (go->neg_mru || no.neg_mru || cilen != CILEN_SHORT)
735 		goto bad;
736 	    break;
737 	case CI_ASYNCMAP:
738 	    if (go->neg_asyncmap || no.neg_asyncmap || cilen != CILEN_LONG)
739 		goto bad;
740 	    break;
741 	case CI_AUTHTYPE:
742 	    if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
743 		goto bad;
744 	    break;
745 	case CI_MAGICNUMBER:
746 	    if (go->neg_magicnumber || no.neg_magicnumber ||
747 		cilen != CILEN_LONG)
748 		goto bad;
749 	    break;
750 	case CI_PCOMPRESSION:
751 	    if (go->neg_pcompression || no.neg_pcompression
752 		|| cilen != CILEN_VOID)
753 		goto bad;
754 	    break;
755 	case CI_ACCOMPRESSION:
756 	    if (go->neg_accompression || no.neg_accompression
757 		|| cilen != CILEN_VOID)
758 		goto bad;
759 	    break;
760 	case CI_QUALITY:
761 	    if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
762 		goto bad;
763 	    break;
764 	default:
765 	    goto bad;
766 	}
767 	p = next;
768     }
769 
770     /* If there is still anything left, this packet is bad. */
771     if (len != 0)
772 	goto bad;
773 
774     /*
775      * OK, the Nak is good.  Now we can update state.
776      */
777     if (f->state != OPENED) {
778 	*go = try;
779 	if (looped_back && try.numloops % lcp_warnloops == 0)
780 	    LCPDEBUG((LOG_INFO, "The line appears to be looped back."));
781     }
782 
783     return 1;
784 
785 bad:
786     LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!"));
787     return 0;
788 }
789 
790 
791 /*
792  * lcp_rejci - Peer has Rejected some of our CIs.
793  * This should not modify any state if the Reject is bad
794  * or if LCP is in the OPENED state.
795  *
796  * Returns:
797  *	0 - Reject was bad.
798  *	1 - Reject was good.
799  */
800 static int
lcp_rejci(f,p,len)801 lcp_rejci(f, p, len)
802     fsm *f;
803     u_char *p;
804     int len;
805 {
806     lcp_options *go = &lcp_gotoptions[f->unit];
807     u_char cichar;
808     u_short cishort;
809     u_long cilong;
810     u_char *start = p;
811     int plen = len;
812     lcp_options try;		/* options to request next time */
813 
814     try = *go;
815 
816     /*
817      * Any Rejected CIs must be in exactly the same order that we sent.
818      * Check packet length and CI length at each step.
819      * If we find any deviations, then this packet is bad.
820      */
821 #define REJCIVOID(opt, neg) \
822     if (go->neg && \
823 	len >= CILEN_VOID && \
824 	p[1] == CILEN_VOID && \
825 	p[0] == opt) { \
826 	len -= CILEN_VOID; \
827 	INCPTR(CILEN_VOID, p); \
828 	try.neg = 0; \
829 	LCPDEBUG((LOG_INFO, "lcp_rejci rejected void opt %d", opt)); \
830     }
831 #define REJCISHORT(opt, neg, val) \
832     if (go->neg && \
833 	len >= CILEN_SHORT && \
834 	p[1] == CILEN_SHORT && \
835 	p[0] == opt) { \
836 	len -= CILEN_SHORT; \
837 	INCPTR(2, p); \
838 	GETSHORT(cishort, p); \
839 	/* Check rejected value. */ \
840 	if (cishort != val) \
841 	    goto bad; \
842 	try.neg = 0; \
843 	LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)); \
844     }
845 #define REJCICHAP(opt, neg, val, digest) \
846     if (go->neg && \
847 	len >= CILEN_CHAP && \
848 	p[1] == CILEN_CHAP && \
849 	p[0] == opt) { \
850 	len -= CILEN_CHAP; \
851 	INCPTR(2, p); \
852 	GETSHORT(cishort, p); \
853 	GETCHAR(cichar, p); \
854 	/* Check rejected value. */ \
855 	if (cishort != val || cichar != digest) \
856 	    goto bad; \
857 	try.neg = 0; \
858 	LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)); \
859     }
860 #define REJCILONG(opt, neg, val) \
861     if (go->neg && \
862 	len >= CILEN_LONG && \
863 	p[1] == CILEN_LONG && \
864 	p[0] == opt) { \
865 	len -= CILEN_LONG; \
866 	INCPTR(2, p); \
867 	GETLONG(cilong, p); \
868 	/* Check rejected value. */ \
869 	if (cilong != val) \
870 	    goto bad; \
871 	try.neg = 0; \
872 	LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)); \
873     }
874 #define REJCILQR(opt, neg, val) \
875     if (go->neg && \
876 	len >= CILEN_LQR && \
877 	p[1] == CILEN_LQR && \
878 	p[0] == opt) { \
879 	len -= CILEN_LQR; \
880 	INCPTR(2, p); \
881 	GETSHORT(cishort, p); \
882 	GETLONG(cilong, p); \
883 	/* Check rejected value. */ \
884 	if (cishort != LQR || cichar != val) \
885 	    goto bad; \
886 	try.neg = 0; \
887 	LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \
888     }
889 
890     REJCISHORT(CI_MRU, neg_mru, go->mru);
891     REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
892     REJCICHAP(CI_AUTHTYPE, neg_chap, CHAP, go->chap_mdtype);
893     if (!go->neg_chap) {
894 	REJCISHORT(CI_AUTHTYPE, neg_upap, UPAP);
895     }
896     REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
897     REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
898     REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
899     REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
900 
901     /*
902      * If there are any remaining CIs, then this packet is bad.
903      */
904     if (len != 0)
905 	goto bad;
906     /*
907      * Now we can update state.
908      */
909     if (f->state != OPENED)
910 	*go = try;
911     return 1;
912 
913 bad:
914     LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!"));
915     LCPDEBUG((LOG_WARNING, "lcp_rejci: plen %d len %d off %d",
916 	      plen, len, p - start));
917     return 0;
918 }
919 
920 
921 /*
922  * lcp_reqci - Check the peer's requested CIs and send appropriate response.
923  *
924  * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
925  * appropriately.  If reject_if_disagree is non-zero, doesn't return
926  * CONFNAK; returns CONFREJ if it can't return CONFACK.
927  */
928 static int
lcp_reqci(f,inp,lenp,reject_if_disagree)929 lcp_reqci(f, inp, lenp, reject_if_disagree)
930     fsm *f;
931     u_char *inp;		/* Requested CIs */
932     int *lenp;			/* Length of requested CIs */
933     int reject_if_disagree;
934 {
935     lcp_options *go = &lcp_gotoptions[f->unit];
936     lcp_options *ho = &lcp_hisoptions[f->unit];
937     lcp_options *ao = &lcp_allowoptions[f->unit];
938     u_char *cip, *next;		/* Pointer to current and next CIs */
939     u_char cilen, citype, cichar;/* Parsed len, type, char value */
940     u_short cishort;		/* Parsed short value */
941     u_long cilong;		/* Parse long value */
942     int rc = CONFACK;		/* Final packet return code */
943     int orc;			/* Individual option return code */
944     u_char *p;			/* Pointer to next char to parse */
945     u_char *ucp = inp;		/* Pointer to current output char */
946     int l = *lenp;		/* Length left */
947 
948     /*
949      * Reset all his options.
950      */
951     BZERO(ho, sizeof(*ho));
952 
953     /*
954      * Process all his options.
955      */
956     next = inp;
957     while (l) {
958 	orc = CONFACK;			/* Assume success */
959 	cip = p = next;			/* Remember begining of CI */
960 	if (l < 2 ||			/* Not enough data for CI header or */
961 	    p[1] < 2 ||			/*  CI length too small or */
962 	    p[1] > l) {			/*  CI length too big? */
963 	    LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!"));
964 	    orc = CONFREJ;		/* Reject bad CI */
965 	    cilen = l;			/* Reject till end of packet */
966 	    l = 0;			/* Don't loop again */
967 	    goto endswitch;
968 	}
969 	GETCHAR(citype, p);		/* Parse CI type */
970 	GETCHAR(cilen, p);		/* Parse CI length */
971 	l -= cilen;			/* Adjust remaining length */
972 	next += cilen;			/* Step to next CI */
973 
974 	switch (citype) {		/* Check CI type */
975 	case CI_MRU:
976 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU"));
977 	    if (!ao->neg_mru ||		/* Allow option? */
978 		cilen != CILEN_SHORT) {	/* Check CI length */
979 		orc = CONFREJ;		/* Reject CI */
980 		break;
981 	    }
982 	    GETSHORT(cishort, p);	/* Parse MRU */
983 	    LCPDEBUG((LOG_INFO, "(%d)", cishort));
984 
985 	    /*
986 	     * He must be able to receive at least our minimum.
987 	     * No need to check a maximum.  If he sends a large number,
988 	     * we'll just ignore it.
989 	     */
990 	    if (cishort < MINMRU) {
991 		orc = CONFNAK;		/* Nak CI */
992 		if( !reject_if_disagree ){
993 		    DECPTR(sizeof (short), p);	/* Backup */
994 		    PUTSHORT(MINMRU, p);	/* Give him a hint */
995 		}
996 		break;
997 	    }
998 	    ho->neg_mru = 1;		/* Remember he sent MRU */
999 	    ho->mru = cishort;		/* And remember value */
1000 	    break;
1001 
1002 	case CI_ASYNCMAP:
1003 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP"));
1004 	    if (!ao->neg_asyncmap ||
1005 		cilen != CILEN_LONG) {
1006 		orc = CONFREJ;
1007 		break;
1008 	    }
1009 	    GETLONG(cilong, p);
1010 	    LCPDEBUG((LOG_INFO, "(%lx)", cilong));
1011 
1012 	    /*
1013 	     * Asyncmap must have set at least the bits
1014 	     * which are set in lcp_allowoptions[unit].asyncmap.
1015 	     */
1016 	    if ((ao->asyncmap & ~cilong) != 0) {
1017 		orc = CONFNAK;
1018 		if( !reject_if_disagree ){
1019 		    DECPTR(sizeof (long), p);
1020 		    PUTLONG(ao->asyncmap | cilong, p);
1021 		}
1022 		break;
1023 	    }
1024 	    ho->neg_asyncmap = 1;
1025 	    ho->asyncmap = cilong;
1026 	    break;
1027 
1028 	case CI_AUTHTYPE:
1029 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE"));
1030 	    if (cilen < CILEN_SHORT ||
1031 		!(ao->neg_upap || ao->neg_chap)) {
1032 		orc = CONFREJ;
1033 		break;
1034 	    }
1035 	    GETSHORT(cishort, p);
1036 	    LCPDEBUG((LOG_INFO, "(%x)", cishort));
1037 
1038 	    /*
1039 	     * Authtype must be UPAP or CHAP.
1040 	     *
1041 	     * Note: if both ao->neg_upap and ao->neg_chap are set,
1042 	     * and the peer sends a Configure-Request with two
1043 	     * authenticate-protocol requests, one for CHAP and one
1044 	     * for UPAP, then we will reject the second request.
1045 	     * Whether we end up doing CHAP or UPAP depends then on
1046 	     * the ordering of the CIs in the peer's Configure-Request.
1047 	     */
1048 
1049 	    if (cishort == UPAP) {
1050 		if (!ao->neg_upap ||	/* we don't want to do PAP */
1051 		    ho->neg_chap ||	/* or we've already accepted CHAP */
1052 		    cilen != CILEN_SHORT) {
1053 		    LCPDEBUG((LOG_WARNING,
1054 			      "lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
1055 		    orc = CONFREJ;
1056 		    break;
1057 		}
1058 		ho->neg_upap = 1;
1059 		break;
1060 	    }
1061 	    if (cishort == CHAP) {
1062 		if (!ao->neg_chap ||	/* we don't want to do CHAP */
1063 		    ho->neg_upap ||	/* or we've already accepted UPAP */
1064 		    cilen != CILEN_CHAP) {
1065 		    LCPDEBUG((LOG_INFO,
1066 			      "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
1067 		    orc = CONFREJ;
1068 		    break;
1069 		}
1070 		GETCHAR(cichar, p);	/* get digest type*/
1071 		if (cichar != ao->chap_mdtype) {
1072 		    orc = CONFNAK;
1073 		    if( !reject_if_disagree ){
1074 			DECPTR(sizeof (u_char), p);
1075 			PUTCHAR(ao->chap_mdtype, p);
1076 		    }
1077 		    break;
1078 		}
1079 		ho->chap_mdtype = cichar; /* save md type */
1080 		ho->neg_chap = 1;
1081 		break;
1082 	    }
1083 
1084 	    /*
1085 	     * We don't recognize the protocol they're asking for.
1086 	     * Reject it.
1087 	     */
1088 	    orc = CONFREJ;
1089 	    break;
1090 
1091 	case CI_QUALITY:
1092 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd QUALITY"));
1093 	    if (!ao->neg_lqr ||
1094 		cilen != CILEN_LQR) {
1095 		orc = CONFREJ;
1096 		break;
1097 	    }
1098 
1099 	    GETSHORT(cishort, p);
1100 	    GETLONG(cilong, p);
1101 	    LCPDEBUG((LOG_INFO, "(%x %lx)", cishort, cilong));
1102 	    if (cishort != LQR) {
1103 		orc = CONFREJ;
1104 		break;
1105 	    }
1106 
1107 	    /*
1108 	     * Check the reporting period.
1109 	     * XXX When should we Nak this, and what with?
1110 	     */
1111 	    break;
1112 
1113 	case CI_MAGICNUMBER:
1114 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER"));
1115 	    if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
1116 		cilen != CILEN_LONG) {
1117 		orc = CONFREJ;
1118 		break;
1119 	    }
1120 	    GETLONG(cilong, p);
1121 	    LCPDEBUG((LOG_INFO, "(%lx)", cilong));
1122 
1123 	    /*
1124 	     * He must have a different magic number.
1125 	     */
1126 	    if (go->neg_magicnumber &&
1127 		cilong == go->magicnumber) {
1128 		orc = CONFNAK;
1129 		DECPTR(sizeof (long), p);
1130 		cilong = magic();	/* Don't put magic() inside macro! */
1131 		PUTLONG(cilong, p);
1132 		break;
1133 	    }
1134 	    ho->neg_magicnumber = 1;
1135 	    ho->magicnumber = cilong;
1136 	    break;
1137 
1138 
1139 	case CI_PCOMPRESSION:
1140 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION"));
1141 	    if (!ao->neg_pcompression ||
1142 		cilen != CILEN_VOID) {
1143 		orc = CONFREJ;
1144 		break;
1145 	    }
1146 	    ho->neg_pcompression = 1;
1147 	    break;
1148 
1149 	case CI_ACCOMPRESSION:
1150 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION"));
1151 	    if (!ao->neg_accompression ||
1152 		cilen != CILEN_VOID) {
1153 		orc = CONFREJ;
1154 		break;
1155 	    }
1156 	    ho->neg_accompression = 1;
1157 	    break;
1158 
1159 	default:
1160 	    LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d",
1161 		      citype));
1162 	    orc = CONFREJ;
1163 	    break;
1164 	}
1165 
1166 endswitch:
1167 	LCPDEBUG((LOG_INFO, " (%s)", CODENAME(orc)));
1168 	if (orc == CONFACK &&		/* Good CI */
1169 	    rc != CONFACK)		/*  but prior CI wasnt? */
1170 	    continue;			/* Don't send this one */
1171 
1172 	if (orc == CONFNAK) {		/* Nak this CI? */
1173 	    if (reject_if_disagree)	/* Getting fed up with sending NAKs? */
1174 		orc = CONFREJ;		/* Get tough if so */
1175 	    else {
1176 		if (rc == CONFREJ)	/* Rejecting prior CI? */
1177 		    continue;		/* Don't send this one */
1178 		if (rc == CONFACK) {	/* Ack'd all prior CIs? */
1179 		    rc = CONFNAK;	/* Not anymore... */
1180 		    ucp = inp;		/* Backup */
1181 		}
1182 	    }
1183 	}
1184 	if (orc == CONFREJ &&		/* Reject this CI */
1185 	    rc != CONFREJ) {		/*  but no prior ones? */
1186 	    rc = CONFREJ;
1187 	    ucp = inp;			/* Backup */
1188 	}
1189 	if (ucp != cip)			/* Need to move CI? */
1190 	    BCOPY(cip, ucp, cilen);	/* Move it */
1191 	INCPTR(cilen, ucp);		/* Update output pointer */
1192     }
1193 
1194     /*
1195      * If we wanted to send additional NAKs (for unsent CIs), the
1196      * code would go here.  This must be done with care since it might
1197      * require a longer packet than we received.  At present there
1198      * are no cases where we want to ask the peer to negotiate an option.
1199      */
1200 
1201     *lenp = ucp - inp;			/* Compute output length */
1202     LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.", CODENAME(rc)));
1203     return (rc);			/* Return final code */
1204 }
1205 
1206 
1207 /*
1208  * lcp_up - LCP has come UP.
1209  *
1210  * Start UPAP, IPCP, etc.
1211  */
1212 static void
lcp_up(f)1213 lcp_up(f)
1214     fsm *f;
1215 {
1216     lcp_options *wo = &lcp_wantoptions[f->unit];
1217     lcp_options *ho = &lcp_hisoptions[f->unit];
1218     lcp_options *go = &lcp_gotoptions[f->unit];
1219     lcp_options *ao = &lcp_allowoptions[f->unit];
1220 
1221     /*
1222      * Set our MTU to the smaller of the MTU we wanted and
1223      * the MRU our peer wanted.  If we negotiated an MRU,
1224      * set our MRU to the larger of value we wanted and
1225      * the value we got in the negotiation.
1226      */
1227     ppp_send_config(f->unit, MIN(ao->mru, (ho->neg_mru? ho->mru: MTU)),
1228 		    (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
1229 		    ho->neg_pcompression, ho->neg_accompression);
1230     ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): MTU),
1231 		    (go->neg_asyncmap? go->asyncmap: 0xffffffff),
1232 		    go->neg_pcompression, go->neg_accompression);
1233 
1234     if (ho->neg_mru)
1235 	peer_mru[f->unit] = ho->mru;
1236 
1237     ChapLowerUp(f->unit);	/* Enable CHAP */
1238     upap_lowerup(f->unit);	/* Enable UPAP */
1239     ipcp_lowerup(f->unit);	/* Enable IPCP */
1240 
1241     link_established(f->unit);
1242 }
1243 
1244 
1245 /*
1246  * lcp_down - LCP has gone DOWN.
1247  *
1248  * Alert other protocols.
1249  */
1250 static void
lcp_down(f)1251 lcp_down(f)
1252     fsm *f;
1253 {
1254     ipcp_lowerdown(f->unit);
1255     ChapLowerDown(f->unit);
1256     upap_lowerdown(f->unit);
1257 
1258     sifdown(f->unit);
1259     ppp_send_config(f->unit, MTU, 0xffffffff, 0, 0);
1260     ppp_recv_config(f->unit, MTU, 0, 0, 0);
1261     peer_mru[f->unit] = MTU;
1262 
1263     link_down(f->unit);
1264 }
1265 
1266 
1267 /*
1268  * lcp_starting - LCP needs the lower layer up.
1269  */
1270 static void
lcp_starting(f)1271 lcp_starting(f)
1272     fsm *f;
1273 {
1274     link_required(f->unit);
1275 }
1276 
1277 
1278 /*
1279  * lcp_finished - LCP has finished with the lower layer.
1280  */
1281 static void
lcp_finished(f)1282 lcp_finished(f)
1283     fsm *f;
1284 {
1285     link_terminated(f->unit);
1286 }
1287 
1288 
1289 /*
1290  * lcp_printpkt - print the contents of an LCP packet.
1291  */
1292 char *lcp_codenames[] = {
1293     "ConfReq", "ConfAck", "ConfNak", "ConfRej",
1294     "TermReq", "TermAck", "CodeRej", "ProtRej",
1295     "EchoReq", "EchoRep", "DiscReq"
1296 };
1297 
1298 int
lcp_printpkt(p,plen,printer,arg)1299 lcp_printpkt(p, plen, printer, arg)
1300     u_char *p;
1301     int plen;
1302     void (*printer) __ARGS((void *, char *, ...));
1303     void *arg;
1304 {
1305     int code, id, len, olen;
1306     u_char *pstart, *optend;
1307     u_short cishort;
1308     u_long cilong;
1309 
1310     if (plen < HEADERLEN)
1311 	return 0;
1312     pstart = p;
1313     GETCHAR(code, p);
1314     GETCHAR(id, p);
1315     GETSHORT(len, p);
1316     if (len < HEADERLEN || len > plen)
1317 	return 0;
1318 
1319     if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *))
1320 	printer(arg, " %s", lcp_codenames[code-1]);
1321     else
1322 	printer(arg, " code=0x%x", code);
1323     printer(arg, " id=0x%x", id);
1324     len -= HEADERLEN;
1325     switch (code) {
1326     case CONFREQ:
1327     case CONFACK:
1328     case CONFNAK:
1329     case CONFREJ:
1330 	/* print option list */
1331 	while (len >= 2) {
1332 	    GETCHAR(code, p);
1333 	    GETCHAR(olen, p);
1334 	    p -= 2;
1335 	    if (olen < 2 || olen > len) {
1336 		break;
1337 	    }
1338 	    printer(arg, " <");
1339 	    len -= olen;
1340 	    optend = p + olen;
1341 	    switch (code) {
1342 	    case CI_MRU:
1343 		if (olen == CILEN_SHORT) {
1344 		    p += 2;
1345 		    GETSHORT(cishort, p);
1346 		    printer(arg, "mru %d", cishort);
1347 		}
1348 		break;
1349 	    case CI_ASYNCMAP:
1350 		if (olen == CILEN_LONG) {
1351 		    p += 2;
1352 		    GETLONG(cilong, p);
1353 		    printer(arg, "asyncmap 0x%x", cilong);
1354 		}
1355 		break;
1356 	    case CI_AUTHTYPE:
1357 		if (olen >= CILEN_SHORT) {
1358 		    p += 2;
1359 		    printer(arg, "auth ");
1360 		    GETSHORT(cishort, p);
1361 		    switch (cishort) {
1362 		    case UPAP:
1363 			printer(arg, "upap");
1364 			break;
1365 		    case CHAP:
1366 			printer(arg, "chap");
1367 			break;
1368 		    default:
1369 			printer(arg, "0x%x", cishort);
1370 		    }
1371 		}
1372 		break;
1373 	    case CI_QUALITY:
1374 		if (olen >= CILEN_SHORT) {
1375 		    p += 2;
1376 		    printer(arg, "quality ");
1377 		    GETSHORT(cishort, p);
1378 		    switch (cishort) {
1379 		    case LQR:
1380 			printer(arg, "lqr");
1381 			break;
1382 		    default:
1383 			printer(arg, "0x%x", cishort);
1384 		    }
1385 		}
1386 		break;
1387 	    case CI_MAGICNUMBER:
1388 		if (olen == CILEN_LONG) {
1389 		    p += 2;
1390 		    GETLONG(cilong, p);
1391 		    printer(arg, "magic 0x%x", cilong);
1392 		}
1393 		break;
1394 	    case CI_PCOMPRESSION:
1395 		if (olen == CILEN_VOID) {
1396 		    p += 2;
1397 		    printer(arg, "pcomp");
1398 		}
1399 		break;
1400 	    case CI_ACCOMPRESSION:
1401 		if (olen == CILEN_VOID) {
1402 		    p += 2;
1403 		    printer(arg, "accomp");
1404 		}
1405 		break;
1406 	    }
1407 	    while (p < optend) {
1408 		GETCHAR(code, p);
1409 		printer(arg, " %.2x", code);
1410 	    }
1411 	    printer(arg, ">");
1412 	}
1413 	break;
1414     }
1415 
1416     /* print the rest of the bytes in the packet */
1417     for (; len > 0; --len) {
1418 	GETCHAR(code, p);
1419 	printer(arg, " %.2x", code);
1420     }
1421 
1422     return p - pstart;
1423 }
1424